├── .gitattributes ├── .github └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .lgtm.yml ├── .pre-commit-config.yaml ├── .vscode └── settings.json ├── Conversion ├── DeepCopy.FCMacro ├── DeepCopy.svg ├── DxfToSketchLayers.FCMacro ├── MultiCopy.FCMacro ├── MultiCopy.svg └── MultiCopy │ ├── MultiCopyAuxFunc.py │ ├── MultiCopyCore.py │ ├── MultiCopyGui.py │ ├── __init__.py │ └── resources │ ├── MultiCopy.svg │ ├── MultiCopy_Commands_Dialog.ui │ └── MultiCopy_Main_Dialog.ui ├── FCRotateViewAbsolute.FCMacro ├── FEM ├── ExportFem.FCMacro ├── FemAnimateModeShapes.FCMacro └── fem_animate_mode_shapes.ui ├── Foto.FCMacro ├── GenevaWheelGUI.py ├── ImportExport ├── Export2Slicer.FCMacro ├── Export2Slicer.png ├── FabricationCutlist.FCMacro ├── FabricationCutlist.png ├── ObjectsToPython.FCMacro ├── ObjectsToPython.svg └── ObjectsToPython │ ├── ObjectsToPython.ui │ └── README.md ├── Information ├── CenterOfMass.FCMacro ├── DatumPlaneLocalAxis.FCMacro ├── DatumPlaneLocalAxis.svg ├── DatumPlaneLocalAxis.txt ├── GetGlobalPlacement-test.txt ├── GetGlobalPlacement.FCMacro ├── GetGlobalPlacement.svg ├── InfoPlus.FCMacro ├── InfoPlus.svg ├── InfoPlus │ └── ui │ │ └── InfoPlus.ui ├── MeasureCircle.FCMacro ├── MeasureCircle.png ├── SimpleProperties.FCMacro └── TreeToAscii.FCMacro ├── ObjectCreation ├── AeroFoil.FCMacro ├── AeroFoil.svg ├── AeroFoil_UI_Files │ ├── AeroFoil_CSVInput_Dialog.ui │ ├── AeroFoil_CurvesInput_Dialog.ui │ ├── AeroFoil_DATInput_Dialog.ui │ ├── AeroFoil_FileLoad_Dialog.ui │ ├── AeroFoil_Final_Dialog.ui │ ├── AeroFoil_Initial_Dialog.ui │ ├── AeroFoil_Math_Functions_Box.ui │ ├── AeroFoil_NACA4Digit_Dialog.ui │ ├── AeroFoil_NACA5Digit_Dialog.ui │ ├── AeroFoil_PointsInput_Dialog.ui │ └── AeroFoil_mfb_img.gif ├── AirfoilImportAndScale.FCMacro ├── BSpline3D.FCMacro ├── BoxCreator.FCMacro ├── BoxCreator.svg ├── Draft_Circle_3_Points.FCMacro ├── EllipseCenter2Points.FCMacro ├── G3d.FCMacro ├── G3d.svg ├── HalfHull.FCMacro ├── HilbertCurve.FCMacro ├── HilbertCurve.svg ├── HyperbolaCreater.FCMacro ├── Macro_FCCircularText.FCMacro ├── ParabolaCreater.FCMacro ├── ScrewMaker.FCMacro ├── TimingGear.FCMacro ├── TimingGear.png ├── boxcreator │ ├── Readme.md │ ├── __init__.py │ ├── boxcreator.py │ ├── boxcreator.ui │ ├── boxcreator_screenshot.jpg │ └── boxcreator_screenshot2.jpg └── g3d │ ├── translations │ ├── g3d_en.txt │ ├── g3d_es.txt │ └── g3d_it.txt │ └── ui │ └── g3d15.ui ├── ParametricObjectCreation ├── GeodesicDome.FCMacro ├── HoneycombSolid.FCMacro ├── HoneycombSolid.xpm ├── Rectellipse.FCMacro ├── geodesic_dome │ ├── __init__.py │ └── geodesic_dome.py └── honeycomb_solid │ ├── __init__.py │ └── honeycomb_solid.py ├── PureGui ├── Camera.FCMacro ├── Camera.png ├── Camera │ ├── accept.png │ ├── align_object_to_view.png │ ├── align_to_axis.png │ ├── align_to_face.png │ ├── axis_rotation_d.png │ ├── axis_rotation_x.png │ ├── axis_rotation_y.png │ ├── axis_rotation_z.png │ ├── create_plane_of_view.png │ ├── detect_orientation.png │ ├── quit.png │ ├── reset.png │ └── save_view.png ├── GuiResetToolbars.FCMacro ├── GuiResetToolbars.svg ├── GuiSpacemouseRotationOff.FCMacro ├── GuiSpacemouseRotationOn.FCMacro ├── GuiSpacemouseRotationToggle.FCMacro ├── SplitPropEditor.FCMacro ├── ViewRotation.FCMacro ├── ViewRotationOut.png ├── ViewRotationRight.png └── ViewRotationUp.png ├── README.md ├── Sketcher ├── SketchUnmap.FCMacro ├── SketchUnmap.svg ├── SketcherBlockAll.FCMacro ├── SketcherClipView.FCMacro ├── SketcherClipView.svg ├── SketcherFixAllPoints.FCMacro ├── SketcherFixAllPoints.svg ├── SketcherOffset.FCMacro └── SketcherOffset.svg ├── SolidSweep.FCMacro ├── Spreadsheet └── .gitkeep ├── TechDraw ├── LasercutterSVGExport.FCMacro ├── LasercutterSVGExport.svg ├── LasercutterSVGExport │ ├── LasercutterSVGExport_screenshot.png │ ├── LasercutterTechdrawExport.py │ ├── README.md │ └── lasercuttersvg.ui ├── TechDrawAnnoTextFromv018.FCMacro ├── TechDrawMigrateFromv018.FCMacro ├── TechDrawSymbolsLibrary.FCMacro ├── TechDrawTools.FCMacro ├── TechDrawTools.svg └── TechDrawViewSet.FCMacro ├── Utility ├── GroupSorting.FCMacro ├── GroupSorting.svg ├── HighlightCommon.FCMacro ├── HighlightCommon.png ├── HighlightDifference.FCMacro ├── HighlightDifference.svg ├── MessageBox.FCMacro ├── PiecesTemplates.FCMacro ├── PiecesTemplates.svg ├── PiecesTemplates │ ├── Groups.txt │ └── ui │ │ └── PiecesTemplates.ui ├── PlacementAbsolufy.FCMacro ├── SelectVisible.FCMacro ├── SketchAp.FCMacro ├── SketchAp.svg ├── StraightenObject.FCMacro ├── Transparencies.FCMacro ├── Transparencies.svg ├── WikiObjectPropertiesListGenerator.FCMacro ├── WikiObjectPropertiesListGenerator.svg ├── pcbway.FCMacro ├── treeHelper.FCMacro └── treeHelper │ ├── treeHelper.svg │ └── ui_treewindow.py ├── apothemBasedPrism.py ├── icons ├── Camera │ ├── FCCamera_00.png │ ├── FCCamera_01.png │ ├── FCCamera_02.png │ ├── FCCamera_03.png │ ├── FCCamera_04.png │ ├── FCCamera_05.png │ ├── FCCamera_06.png │ ├── FCCamera_07.png │ ├── FCCamera_08.png │ ├── FCCamera_Axis_rotation_X.png │ ├── FCCamera_Axis_rotation_Y.png │ └── FCCamera_Axis_rotation_Z.png ├── HyperbolaIcon.png ├── MeasureCircle.png ├── MeasureCircle.svg ├── RotateView │ ├── out.png │ ├── right.png │ ├── right_abs.png │ └── up.png ├── SelectVisible.png └── SelectVisible.svg ├── makecamera2dview.py └── myMacroDir ├── __init__.py └── myMacro.py /.gitattributes: -------------------------------------------------------------------------------- 1 | *.FCMacro linguist-language=Python 2 | *.fcmacro linguist-language=Python 3 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Thank you for creating a pull request to contribute to FreeCAD-macros! 2 | To integrate your macro please make sure the following steps are complete: 3 | 4 | - [ ] Please check this box if you're not submitting a new macro. 5 | - [ ] Are you submitting a new macro ? 6 | - [ ] Have you followed the ['How to submit a macro'](../README.md#how-to-submit-a-macro) section of the README.md ? 7 | - [ ] Your macro has a [Description](../README.md#macro-description) in its header. 8 | - [ ] Your macro has a [CamelCase name](../README.md#camelcase-macro-name). 9 | - [ ] Your macro is named [appropriately](../README.md#macro-name-specifics). 10 | - [ ] Your macro contains a [Metadata section](../README.md#macro-metadata) that immediately follows the header description. 11 | - [ ] Your macro is Python3/Qt5 compliant and tested on the latest FreeCAD stable and development releases. 12 | - [ ] You're including documentation on how your macro works (bonus: screenshots and/or video on the Wiki) 13 | - [ ] Commit message is [well-written](https://chris.beams.io/posts/git-commit/) 14 | - [ ] Commit message is titled in the following way `[MacroName] Short description`. 15 | - [ ] Optional, write or update the changelog in the macro, from latest to oldest. 16 | 17 | And please remember to update the Wiki with the features added or changed once this PR is merged. 18 | **Note**: If you don't have wiki access, then please mention your contribution on the [0.19 Changelog Forum Thread](https://forum.freecadweb.org/viewtopic.php?f=10&t=34586). 19 | 20 | --- 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.py? 2 | -------------------------------------------------------------------------------- /.lgtm.yml: -------------------------------------------------------------------------------- 1 | extraction: 2 | python: 3 | python_setup: 4 | version: 3 5 | setup_py: false 6 | index: 7 | filters: 8 | - include: "**/*.FCMacro" 9 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # To use: 2 | # 3 | # pre-commit run -a 4 | # 5 | # Or: 6 | # 7 | # pre-commit install # (runs every time you commit in git) 8 | # 9 | # To update this file: 10 | # 11 | # pre-commit autoupdate 12 | # 13 | # See https://github.com/pre-commit/pre-commit for more information. 14 | # See https://pre-commit.com/hooks.html for more hooks. 15 | repos: 16 | - repo: https://github.com/pre-commit/pre-commit-hooks 17 | rev: v4.6.0 18 | hooks: 19 | - id: check-added-large-files 20 | - id: check-ast 21 | - id: check-builtin-literals 22 | - id: check-case-conflict 23 | - id: check-docstring-first 24 | # - id: check-illegal-windows-names 25 | - id: check-json 26 | - id: check-merge-conflict 27 | - id: check-symlinks 28 | - id: check-toml 29 | - id: check-xml 30 | - id: check-yaml 31 | - id: debug-statements 32 | - id: destroyed-symlinks 33 | - id: fix-byte-order-marker 34 | - id: forbid-submodules 35 | - id: mixed-line-ending 36 | - id: trailing-whitespace 37 | exclude: '(\.ui|\.svg)$' 38 | - repo: https://github.com/asottile/pyupgrade 39 | rev: v3.19.1 40 | hooks: 41 | - id: pyupgrade 42 | args: [--py38-plus] 43 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "python.linting.flake8Enabled": true, 3 | "python.linting.enabled": true 4 | } -------------------------------------------------------------------------------- /Conversion/DeepCopy.FCMacro: -------------------------------------------------------------------------------- 1 | # Make a copy of the shape of all children of a part and make a compound 2 | # from them. 3 | 4 | from __future__ import annotations 5 | 6 | __Name__ = 'Deep Copy' 7 | __Comment__ = 'Takes a part and makes a compound out of it' 8 | __License__ = 'Apache-2.0' 9 | __Web__ = 'https://www.freecadweb.org/wiki/Macro_DeepCopy' 10 | __Wiki__ = 'https://www.freecadweb.org/wiki/Macro_DeepCopy' 11 | __Icon__ = 'DeepCopy.svg' 12 | __Help__ = 'Select a part and launch' 13 | __Author__ = 'galou_breizh' 14 | __Version__ = '1.1.0' 15 | __Date__ = '2023-03-22' 16 | __Status__ = 'Stable' 17 | __Requires__ = 'FreeCAD >= v0.17' 18 | __Files__ = 'DeepCopy.svg' 19 | 20 | from typing import Iterable, Optional 21 | 22 | from Arch import pruneIncluded 23 | 24 | import FreeCAD as app 25 | import FreeCADGui as gui 26 | 27 | 28 | # Typing hints. 29 | DO: app.DocumentObject 30 | AppPart: DO # TypeId == 'App::Part'. 31 | DOList: Iterable[DO] 32 | 33 | 34 | def deep_copy(doc: Optional[app.Document] = None): 35 | """Copy the shape of the selected objects. 36 | 37 | Parameters 38 | ---------- 39 | - doc: document to copy the shape to. Defaults to the document of the 40 | selected objects. 41 | 42 | """ 43 | sel = gui.Selection.getSelectionEx() 44 | if not sel: 45 | app.Console.PrintWarning('No objects selected') 46 | return 47 | # Check that all selected objects are in the same document. 48 | if doc is None: 49 | doc = sel[0].Object.Document 50 | for sel_object in sel: 51 | if sel_object.Object.Document is not doc: 52 | app.Console.PrintError('Selected objects belong to' 53 | ' different documents\n') 54 | return 55 | 56 | for sel_object in sel: 57 | deep_copy_part(sel_object.Object, doc) 58 | 59 | 60 | def deep_copy_part(part: AppPart, doc: Optional[app.Document] = None): 61 | """Copy the shape of a "App::Part" object. 62 | 63 | Parameters 64 | ---------- 65 | - doc: document to copy the shape to. Defaults to the document of the 66 | given object. 67 | 68 | """ 69 | if (not hasattr(part, 'TypeId')) or part.TypeId != 'App::Part': 70 | # Part is not a part, return. 71 | try: 72 | app.Console.PrintWarning(f'"{part.Label}" ({part.Name})' 73 | ' is not a part, ignoring\n') 74 | except AttributeError: 75 | app.Console.PrintWarning('Object is not a part, ignoring\n') 76 | return 77 | 78 | if doc is None: 79 | doc = part.Document 80 | 81 | copied_subobjects = [] 82 | for o in get_all_subobjects(part): 83 | copied_subobjects += copy_subobject(doc, o) 84 | 85 | compound = doc.addObject('Part::Compound', 'Copy of ' + part.Label) 86 | compound.Links = copied_subobjects 87 | compound.Placement = part.Placement 88 | doc.recompute() 89 | 90 | 91 | def get_all_subobjects(obj: DO) -> DOList: 92 | """Recursively get all subobjects 93 | 94 | Subobjects of objects having a Shape attribute are not included otherwise 95 | each single feature of the object would be copied. The result is that 96 | bodies, compounds, and the result of boolean operations will be converted 97 | into a simple copy of their shape. 98 | 99 | """ 100 | # Depth-first search algorithm. 101 | discovered = [] 102 | # We do not need an extra copy for stack because OutList is already a copy. 103 | stack = obj.OutList 104 | while stack: 105 | v = stack.pop(0) 106 | if v not in discovered: 107 | discovered.append(v) 108 | if not hasattr(v, 'Shape'): 109 | stack += v.OutList 110 | return pruneIncluded(discovered) 111 | 112 | 113 | def copy_subobject(doc: app.DocumentObject, obj: DO) -> DOList: 114 | """Copy the shape of an object 115 | 116 | Some GUI attributes are also copied 117 | 118 | """ 119 | copied_object = [] 120 | if not hasattr(obj, 'Shape') or obj.Shape.isNull(): 121 | return copied_object 122 | vobj = obj.ViewObject 123 | try: 124 | copy = doc.addObject('Part::Feature', obj.Name + '_Shape') 125 | copy.Shape = obj.Shape 126 | copy.Label = 'Copy of ' + obj.Label 127 | except AttributeError as e: 128 | app.Console.PrintLog(e) 129 | pass 130 | else: 131 | copied_object = [copy] 132 | vo_copy = copy.ViewObject 133 | # Implementation note: cannot use __setattr__ and __getattribute__. 134 | try: 135 | vo_copy.ShapeColor = vobj.ShapeColor 136 | except AttributeError: 137 | pass 138 | try: 139 | vo_copy.LineColor = vobj.LineColor 140 | except AttributeError: 141 | pass 142 | try: 143 | vo_copy.PointColor = vobj.PointColor 144 | except AttributeError: 145 | pass 146 | try: 147 | vo_copy.DiffuseColor = vobj.DiffuseColor 148 | except AttributeError: 149 | pass 150 | try: 151 | vo_copy.Transparency = vobj.Transparency 152 | except AttributeError: 153 | pass 154 | return copied_object 155 | 156 | 157 | if __name__ == '__main__': 158 | deep_copy() 159 | -------------------------------------------------------------------------------- /Conversion/MultiCopy.FCMacro: -------------------------------------------------------------------------------- 1 | 2 | 3 | ########################################################################################## 4 | ##### L I C E N S E ##### 5 | ########################################################################################## 6 | # 7 | # GNU LESSER GENERAL PUBLIC LICENSE 8 | # Version 2.1, February 1999 9 | # 10 | # Copyright (C) 1991, 1999 Free Software Foundation, Inc. 11 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 12 | # Everyone is permitted to copy and distribute verbatim copies 13 | # of this license document, but changing it is not allowed. 14 | # 15 | # [This is the first released version of the Lesser GPL. It also counts 16 | # as the successor of the GNU Library Public License, version 2, hence 17 | # the version number 2.1.] 18 | # 19 | # 'MultiCopy' is a FreeCAD macro package. MultiCopy allows the duplication 20 | # (copy and paste) of multiple FreeCAD objects that can be labelled 21 | # sequentially and in a custom manner. 22 | # 23 | # Copyright (C) 2021 Melwyn Francis Carlo 24 | # 25 | # This library is free software; you can redistribute it and/or 26 | # modify it under the terms of the GNU Lesser General Public 27 | # License as published by the Free Software Foundation; either 28 | # version 2.1 of the License, or (at your option) any later version. 29 | # 30 | # This library is distributed in the hope that it will be useful, 31 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 32 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 33 | # Lesser General Public License for more details. 34 | # 35 | # You should have received a copy of the GNU Lesser General Public License 36 | # along with this library; if not, write to the Free Software Foundation, Inc., 37 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 38 | # 39 | # Contact Information :- 40 | # Email : carlo.melwyn@outlook.com 41 | # FreeCAD UserTalk : http://www.freecadweb.org/wiki/index.php?title=User:Melwyncarlo 42 | # 43 | ########################################################################################## 44 | ##### L I C E N S E ##### 45 | ########################################################################################## 46 | # 47 | # 48 | # 49 | # The MultiCopy macro was developed and tested on a platform containing the 50 | # following system and FreeCAD software specifications : 51 | # 52 | # - OS : Ubuntu 18.04.5 LTS (LXDE/Lubuntu) 53 | # - Word size of OS : 64-bit 54 | # - Word size of FreeCAD: 64-bit 55 | # - Version : 0.19 56 | # - Build type : Release 57 | # - Branch : unknown 58 | # - Hash : 32200b604d421c4dad527fe587a7d047cf953b4f 59 | # - Python version : 3.6.9 60 | # - Qt version : 5.9.5 61 | # - Coin version : 4.0.0a 62 | # - OCC version : 7.3.0 63 | # - Locale : English/UnitedKingdom (en_GB) 64 | 65 | 66 | 67 | """ 68 | To use this macro, the steps to be followed are simple and straightforward : 69 | select one or more FreeCAD objects from the Tree view, and then select 'MultiCopy'. 70 | In the dialog box that pops up, choose the copy method, select and input the relevant 71 | paste parameters and commands, and then click on 'Paste'. 72 | 73 | Note (1) The single underscore prefix (e.g. _name) denotes a private 74 | function or a private variable. 75 | Note (2) Some of the short forms used in this script are as follows: 76 | 'mc' stands for MultiCopy 77 | 'pcc' stands for Paste Code Commands 78 | 'd' stands for Dialog (e.g. _d_, etc.) 79 | """ 80 | 81 | 82 | 83 | __Title__='MultiCopy' 84 | __Author__='Melwyncarlo' 85 | __Version__='2.0.0' 86 | __Date__='2021-03-23' 87 | __Comment__='MultiCopy allows the duplication (copy and paste) of multiple FreeCAD objects that can be labelled sequentially and in a custom manner.' 88 | __Web__='https://github.com/melwyncarlo/MultiCopy' 89 | __Wiki__='http://www.freecadweb.org/wiki/index.php?title=Macro_MultiCopy' 90 | __Icon__='MultiCopy.svg' 91 | __Help__='Select one or more FreeCAD objects, then click on the MultiCopy button/macro, and follow the instructions in the dialog box.' 92 | __Status__='stable' 93 | __Requires__='Freecad >= v0.17' 94 | __Communication__='https://github.com/melwyncarlo/MultiCopy/issues' 95 | __Files__='MultiCopyGui.py, MultiCopyCore.py, MultiCopyAuxFunc.py, MultiCopy/resources/MultiCopy_Main_Dialog.ui, MultiCopy/resources/MultiCopy_Commands_Dialog.ui, MultiCopy/resources/MultiCopy.svg' 96 | 97 | 98 | 99 | 100 | # Library Imports 101 | #------------------------------------------------------------------------------------------------ 102 | 103 | import MultiCopy 104 | 105 | 106 | 107 | ################################################################### 108 | ###-------------------------------------------------------------### 109 | ### MULTICOPY MACRO CALLS ### 110 | ###-------------------------------------------------------------### 111 | ### ### 112 | ### This is the main macro call. The code below commences ### 113 | ### the MultiCopy GUI interface. This script cannot be ### 114 | ### called externally. ### 115 | ### 116 | ### 117 | if __name__ == '__main__': ### 118 | MultiCopy.Gui.Launch() ### 119 | ### 120 | ###-------------------------------------------------------------### 121 | ### MULTICOPY MACRO CALLS ### 122 | ###-------------------------------------------------------------### 123 | ################################################################### 124 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /Conversion/MultiCopy/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | 4 | ########################################################################################## 5 | ##### L I C E N S E ##### 6 | ########################################################################################## 7 | # 8 | # GNU LESSER GENERAL PUBLIC LICENSE 9 | # Version 2.1, February 1999 10 | # 11 | # Copyright (C) 1991, 1999 Free Software Foundation, Inc. 12 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 13 | # Everyone is permitted to copy and distribute verbatim copies 14 | # of this license document, but changing it is not allowed. 15 | # 16 | # [This is the first released version of the Lesser GPL. It also counts 17 | # as the successor of the GNU Library Public License, version 2, hence 18 | # the version number 2.1.] 19 | # 20 | # 'MultiCopy' is a FreeCAD macro package. MultiCopy allows the duplication 21 | # (copy and paste) of multiple FreeCAD objects that can be labelled 22 | # sequentially and in a custom manner. 23 | # 24 | # Copyright (C) 2021 Melwyn Francis Carlo 25 | # 26 | # This library is free software; you can redistribute it and/or 27 | # modify it under the terms of the GNU Lesser General Public 28 | # License as published by the Free Software Foundation; either 29 | # version 2.1 of the License, or (at your option) any later version. 30 | # 31 | # This library is distributed in the hope that it will be useful, 32 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 33 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 34 | # Lesser General Public License for more details. 35 | # 36 | # You should have received a copy of the GNU Lesser General Public License 37 | # along with this library; if not, write to the Free Software Foundation, Inc., 38 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 39 | # 40 | # Contact Information :- 41 | # Email : carlo.melwyn@outlook.com 42 | # FreeCAD UserTalk : http://www.freecadweb.org/wiki/index.php?title=User:Melwyncarlo 43 | # 44 | ########################################################################################## 45 | ##### L I C E N S E ##### 46 | ########################################################################################## 47 | 48 | 49 | """This is the MultiCopy package documentation. 50 | 51 | NAME 52 | -------------------- 53 | MultiCopy 54 | 55 | VERSION 56 | -------------------- 57 | v2.0.0 58 | 59 | DESCRIPTION 60 | -------------------- 61 | 'MultiCopy' is a user-created macro to be used within the FreeCAD application. 62 | MultiCopy allows the duplication (copy and paste) of multiple FreeCAD objects 63 | that can be labelled sequentially and in a custom manner. 64 | 65 | Key features include: 66 | > Two input methods: by mouse, or by keyboard (Paste Code Commands) 67 | > Standard Copy and Simple Copy methods supported 68 | > Duplication across two different documents 69 | > Delete selected objects after duplication 70 | > Duplicate with or without dependencies 71 | > Add custom label separators 72 | > Add padded numbering to labels 73 | > Numbering types: Ordinary numerals, upper/lower-case roman numerals and 74 | upper/lower-case alphabetic characters 75 | > Unique 'Paste Code Commands' that allow multiple duplication procedurally 76 | as well as in nested loops 77 | > Both CUI and GUI methods available 78 | 79 | For more details, visit: 80 | https://github.com/melwyncarlo/MultiCopy 81 | https://wiki.freecadweb.org/Macro_MultiCopy 82 | 83 | PACKAGE CONTENTS 84 | -------------------- 85 | MultiCopyCore.py 86 | MultiCopyGui.py 87 | """ 88 | 89 | 90 | __Title__ = 'MultiCopy' 91 | __Author__ = 'Melwyncarlo' 92 | __Version__ = '2.0.0' 93 | __Date__ = '2021-03-23' 94 | __Comment__ = 'MultiCopy allows the duplication (copy and paste) of multiple FreeCAD objects that can be labelled sequentially and in a custom manner.' 95 | __Web__ = 'https://github.com/melwyncarlo/MultiCopy' 96 | __Wiki__ = 'http://www.freecadweb.org/wiki/index.php?title=Macro_MultiCopy' 97 | __Help__ = 'Select one or more FreeCAD objects, then click on the MultiCopy button/macro, and follow the instructions in the dialog box.' 98 | __Status__ = 'stable' 99 | __Requires__ = 'Freecad >= v0.17' 100 | __Communication__ = 'https://github.com/melwyncarlo/MultiCopy/issues' 101 | __Files__ = 'MultiCopyGui.py, MultiCopyCore.py, MultiCopyAuxFunc.py, resources/MultiCopy_Main_Dialog.ui, resources/MultiCopy_Commands_Dialog.ui, resources/MultiCopy.svg' 102 | 103 | 104 | # Library Imports 105 | # ------------------------------------------------------------------------------------------------ 106 | 107 | from . import MultiCopyGui as Gui 108 | from . import MultiCopyCore as Core 109 | 110 | 111 | # Alias Functions 112 | # ------------------------------------------------------------------------------------------------ 113 | 114 | Run = Core.Run 115 | Launch = Gui.Launch 116 | -------------------------------------------------------------------------------- /FCRotateViewAbsolute.FCMacro: -------------------------------------------------------------------------------- 1 | __Name__ = 'FCRotateViewAbsolute' 2 | __Comment__ = '' 3 | __License__ = '' 4 | __Web__ = '' 5 | __Wiki__ = '' 6 | __Icon__ = '' 7 | __Help__ = '' 8 | __Author__ = '' 9 | __Version__ = '' 10 | __Status__ = '' 11 | __Requires__ = '' 12 | __Files__ = '' 13 | 14 | from FreeCAD import Base 15 | import FreeCADGui as Gui 16 | 17 | 18 | from math import cos,sin,pi 19 | 20 | import sys 21 | from PySide.QtCore import * 22 | from PySide.QtGui import * 23 | 24 | from pivy import coin 25 | 26 | class Form(QDialog): 27 | 28 | def __init__(self, parent=None): 29 | super(Form, self).__init__(parent) 30 | self.alpha=00 31 | self.beta=180 32 | self.setWindowFlags(Qt.WindowStaysOnTopHint) 33 | 34 | dial = QDial() 35 | dial.setNotchesVisible(True) 36 | self.dial=dial 37 | dial.setMaximum(360) 38 | dial.setValue(self.alpha) 39 | 40 | 41 | 42 | dial2 = QDial() 43 | dial2.setNotchesVisible(True) 44 | self.dial2=dial2 45 | dial2.setMaximum(360) 46 | dial2.setValue(self.beta) 47 | 48 | spinbox = QSpinBox() 49 | spinbox.setMaximum(360) 50 | 51 | layout = QHBoxLayout() 52 | 53 | 54 | self.pushButton00 = QPushButton(QIcon('icons:freecad.svg'),"Dimetric") 55 | self.pushButton01 = QPushButton(QIcon('icons:freecad.svg'),"Trimetric") 56 | self.pushButton02 = QPushButton(QIcon('icons:freecad.svg'),"Isometric") 57 | self.pushButton03 = QPushButton(QIcon('icons:freecad.svg'),"Front") 58 | self.pushButton00.clicked.connect(self.dimetric) 59 | self.pushButton01.clicked.connect(self.trimetric) 60 | self.pushButton02.clicked.connect(self.isometric) 61 | self.pushButton03.clicked.connect(self.front) 62 | layout.addWidget(self.pushButton03) 63 | layout.addWidget(self.pushButton00) 64 | layout.addWidget(self.pushButton01) 65 | layout.addWidget(self.pushButton02) 66 | 67 | 68 | layout.addWidget(dial) 69 | layout.addWidget(dial2) 70 | # layout.addWidget(spinbox) 71 | self.setLayout(layout) 72 | 73 | #self.connect(dial, SIGNAL("valueChanged(int)"), spinbox.setValue) 74 | #self.connect(spinbox, SIGNAL("valueChanged(int)"), dial.setValue) 75 | dial.valueChanged.connect(spinbox.setValue); 76 | 77 | dial.valueChanged.connect(self.dreher); 78 | dial2.valueChanged.connect(self.heber); 79 | 80 | 81 | self.cami() 82 | self.setWindowTitle("Camera position") 83 | Gui.SendMsgToActiveView("ViewFit") 84 | 85 | 86 | 87 | def dreher(self): 88 | self.alpha=self.dial.value() 89 | self.cami() 90 | 91 | def heber(self): 92 | self.beta=self.dial2.value() 93 | self.cami() 94 | 95 | def rotY(self,delta=10): 96 | if self.beta==1: 97 | self.beta=0 98 | 99 | self.beta += delta 100 | if self.beta >360: 101 | self.beta -= 360 102 | if self.beta <0: 103 | self.beta += 360 104 | if self.beta==0: 105 | self.beta=1 106 | FreeCAD.Console.PrintMessage(str(self.beta)+" # "); 107 | self.dial2.setValue(self.beta) 108 | 109 | 110 | def rotZ(self,delta=10): 111 | self.alpha += delta 112 | if self.alpha >360: 113 | self.alpha -= 360 114 | if self.alpha <0: 115 | self.alpha += 360 116 | self.dial.setValue(self.alpha) 117 | 118 | def cami(self): 119 | 120 | from pivy import coin 121 | alpha=self.alpha 122 | beta=self.beta 123 | 124 | camera = FreeCADGui.ActiveDocument.ActiveView.getCameraNode() 125 | 126 | Gui=FreeCADGui 127 | if False: 128 | typeCamera="Orthographic" 129 | if typeCamera=="Orthographic": 130 | Gui.activeDocument().activeView().setCameraType("Orthographic") 131 | else: 132 | Gui.activeDocument().activeView().setCameraType("Perspective") 133 | 134 | campos=Base.Vector( 1000 * cos (pi*alpha/360*2)*sin(pi*beta/360*1), 1000*sin(pi*alpha/360*2)*sin(pi*beta/360*1), 1000*cos(pi*beta/360*1)) 135 | camera.position.setValue( campos) 136 | 137 | pos3=FreeCAD.Vector(0,0,0) 138 | pos3.sub(campos) 139 | 140 | #if False: 141 | camera.pointAt(coin.SbVec3f(pos3),coin.SbVec3f(0,0,1)) 142 | 143 | App.ActiveDocument.recompute() 144 | FreeCADGui.updateGui() 145 | 146 | def front(self): 147 | from pivy import coin 148 | pos3=FreeCAD.Vector(0,0,0) 149 | campos=FreeCAD.Vector(0,-10,0) 150 | camera = FreeCADGui.ActiveDocument.ActiveView.getCameraNode() 151 | camera.position.setValue( campos) 152 | camera.pointAt(coin.SbVec3f(pos3),coin.SbVec3f(0,0,1)) 153 | 154 | def isometric(self): 155 | 156 | pos3=FreeCAD.Vector(0,0,0) 157 | campos=FreeCAD.Vector(100,-100,100) 158 | camera = FreeCADGui.ActiveDocument.ActiveView.getCameraNode() 159 | camera.position.setValue( campos) 160 | pos3.sub(campos) 161 | camera.pointAt(coin.SbVec3f(pos3),coin.SbVec3f(0,0,1)) 162 | 163 | def dimetric(self): 164 | pos3=FreeCAD.Vector(0,0,0) 165 | campos=FreeCAD.Vector(68,-68,27) 166 | camera = FreeCADGui.ActiveDocument.ActiveView.getCameraNode() 167 | camera.position.setValue( campos) 168 | pos3.sub(campos) 169 | camera.pointAt(coin.SbVec3f(pos3),coin.SbVec3f(0,0,1)) 170 | App.ActiveDocument.recompute() 171 | FreeCADGui.updateGui() 172 | 173 | 174 | def trimetric(self): 175 | pos3=FreeCAD.Vector(0,0,0) 176 | campos=FreeCAD.Vector(210,-790,580) 177 | camera = FreeCADGui.ActiveDocument.ActiveView.getCameraNode() 178 | camera.position.setValue( campos) 179 | pos3.sub(campos) 180 | camera.pointAt(coin.SbVec3f(pos3),coin.SbVec3f(0,0,1)) 181 | 182 | 183 | if hasattr(FreeCAD,"ViewMgr")and FreeCAD.ViewMgr: 184 | FreeCAD.ViewMgr.show() 185 | FreeCAD.Console.PrintMessage("neu gestrte") 186 | else: 187 | FreeCAD.ViewMgr=Form() 188 | FreeCAD.ViewMgr.show() 189 | FreeCAD.Console.PrintMessage(" war schon da") 190 | 191 | 192 | t=FreeCADGui.getMainWindow() 193 | t.activateWindow() 194 | -------------------------------------------------------------------------------- /FEM/ExportFem.FCMacro: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Export FreeCAD FEM Data 3 | # Author: Gomez Lucio 4 | # License: LGPL v 2.1 5 | 6 | 7 | __Name__ = "Export Fem" 8 | __Comment__ = "This macro exports FEM Data" 9 | __License__ = "LGPL v 2.1" 10 | __Web__ = "http://forum.freecadweb.org/viewtopic.php?f=18&t=11455" 11 | __Wiki__ = "" 12 | __Icon__ = "" 13 | __Help__ = "Select an object and start the macro" 14 | __Author__ ="Gomez Lucio" 15 | __Version__= "" 16 | __Status__ = "" 17 | __Requires__ = "" 18 | __Files__ = "" 19 | 20 | 21 | 22 | 23 | import os 24 | import FreeCADGui 25 | import FreeCAD 26 | import shutil 27 | from PySide import QtGui 28 | 29 | ### START OF MACRO ### 30 | 31 | # Mehcanical Analysis Select 32 | sel = FreeCADGui.Selection.getSelection() # Selection 33 | sel1=sel[0] 34 | 35 | # Save folder select 36 | dialog = QtGui.QFileDialog.getExistingDirectory() 37 | destiny_folder = str(dialog) 38 | 39 | # Proceed 40 | if sel1.TypeId == 'Fem::FemAnalysisPython': 41 | try: 42 | dir1 = sel1.Document.TransientDir # Temporary Directory 43 | nam_fold = sel1.Uid[32:] # Analysis temporary folder name 44 | # Analysis Final Directory 45 | direc = str(dir1 + '/FemAnl_' + nam_fold + '/') 46 | calculix_files = os.listdir(direc) 47 | for files in calculix_files: 48 | shutil.copy(direc + files,destiny_folder) 49 | FreeCAD.Console.PrintMessage('Mechanical Analysis files save in' + destiny_folder) 50 | except: 51 | FreeCAD.Console.PrintError('Sorry but no temporary file exists') 52 | else: 53 | FreeCAD.Console.PrintError('Error in Selection: Select a correct Mechanical Analysis') 54 | 55 | ### END OF MACRO ### 56 | -------------------------------------------------------------------------------- /FEM/FemAnimateModeShapes.FCMacro: -------------------------------------------------------------------------------- 1 | """FreeCAD macro to animate mode shapes. 2 | 3 | These are the steps used to generate the data to be animated, these are the 4 | usual steps: 5 | - click on "CalculiXccxTools" 6 | - select "Frequency" 7 | - select "Write .inp file" 8 | - select "Run CalculiX" 9 | 10 | These are the steps to prepare for and perform the animation: 11 | - open "Macro" in the ToolBar. 12 | - select "Macros..." and choose "FemAnimateModeShapes.py" and "execute". A 13 | widget "Animate" will be generated in a separate window. 14 | - Double-click on a set of results "CalculiX_frequency_mode_7", for example. 15 | This panel must be visible for the animation to work, Note (1). 16 | - select "Abs displacement", for example. 17 | - click the radio button "Displacement/Show" 18 | - click on "Start Animation" in the widget. 19 | 20 | The entries in the widget are: 21 | - "scale" - scale for the displacements, 1 to 999, this may be a problem if the 22 | displacements are very large/small before scaling. 23 | - "factor" - enter the value for the scale, this is linked to "scale" 24 | - "number of steps..." - the number of steps in 1 vibratory cycle, if this 25 | value is increased the apparent speed of the animation will slow down. 26 | - "steps/second" - the number of steps displayed per second, if this value is 27 | increased the animation will speed up. 28 | - "number of cycles" - the number of cycles in the animation. 29 | - "Start Animation" - starts the animation, this changes to "Stop Animation" 30 | during the animation. 31 | 32 | Note(1) - if you click on "Results_mesh001", and start animation you may 33 | possibly be animating the wrong mode shape. 34 | """ 35 | 36 | import math 37 | import os 38 | import time 39 | 40 | import FreeCAD as app 41 | 42 | import FreeCADGui as gui 43 | 44 | from PySide import QtGui # FreeCAD's PySide! 45 | 46 | __Name__ = 'Animate Mode Shapes for FEM' 47 | __Comment__ = 'Animate mode shapes after running CalculiX' 48 | __Author__ = 'mac_the_bike,galou' 49 | __Version__ = '1.0.1' 50 | __Date__ = '2022-02-04' 51 | __License__ = 'LGPL-2.0-or-later' 52 | __Web__ = 'https://forum.freecadweb.org/viewtopic.php?t=39081' 53 | __Wiki__ = '' 54 | __Icon__ = '' 55 | __Help__ = '' 56 | __Status__ = '' 57 | __Requires__ = '' 58 | __Communication__ = 'https://github.com/FreeCAD/FreeCAD-macros/issues/' 59 | __Files__ = 'fem_animate_mode_shapes.ui' 60 | 61 | # Items in the widget window: 62 | # - startEndButton 63 | # - amplitude 64 | # - factor 65 | # - steps 66 | # - steps/second is coded as frames/second 67 | # - loops 68 | 69 | 70 | class FemAnimateModeShapes(): 71 | def __init__(self): 72 | self.do_animation = False 73 | self.ui_file = os.path.join(app.getUserMacroDir(True), 74 | 'fem_animate_mode_shapes.ui') 75 | self.inc = 1 # TODO: find a better name and use. 76 | 77 | self.form = gui.PySideUic.loadUi(self.ui_file) 78 | self._connect_widgets() 79 | self.form.show() 80 | self.box = self._message_box() 81 | 82 | def _connect_widgets(self): 83 | self.form.startEndButton.clicked.connect(self.start_stop) 84 | self.form.amplitude.valueChanged.connect(self._on_amplitude_changed) 85 | self.form.factor.valueChanged.connect(self._on_factor_changed) 86 | 87 | def start(self): 88 | self.do_animation = True 89 | self._do_animate() 90 | 91 | def start_stop(self): 92 | if self.do_animation: 93 | self.do_animation = False 94 | else: 95 | self.do_animation = True 96 | self._do_animate() 97 | 98 | def _on_amplitude_changed(self): 99 | if self.inc == 0: 100 | self.form.factor.setValue(self.form.amplitude.value()) 101 | self.inc = 1 - self.inc 102 | 103 | def _on_factor_changed(self): 104 | if self.inc == 0: 105 | self.form.amplitude.setValue(int(self.form.factor.value())) 106 | self.inc = 1 - self.inc 107 | 108 | def _message_box(self): 109 | box = QtGui.QMessageBox() 110 | box.setWindowTitle('No Results') 111 | box.setText('Select a case and select a displacement type, e.g. Abs') 112 | box.setInformativeText('Tick the displacement radio button') 113 | box.setStandardButtons(QtGui.QMessageBox.Ok) 114 | box.setDefaultButton(QtGui.QMessageBox.Save) 115 | return box 116 | 117 | def _do_animate(self): 118 | if not self.do_animation: 119 | return 120 | try: 121 | mesh_obj = app.FEM_dialog['result_obj'] 122 | mesh_obj.Mesh.ViewObject.applyDisplacement(0) 123 | except: 124 | self.box.exec_() 125 | self.do_animation = False 126 | return 127 | 128 | self.form.startEndButton.setText('Stop Animation') 129 | 130 | frame_count = self.form.frames.value() 131 | steps = self.form.steps.value() 132 | loops = self.form.loops.value() 133 | inc = 2 * math.pi / steps 134 | 135 | done = False 136 | for lo in range(loops): 137 | for st in range(steps): 138 | mesh_obj.Mesh.ViewObject.applyDisplacement( 139 | math.sin(inc * st) * self.form.factor.value()) 140 | gui.updateGui() 141 | if not self.do_animation: 142 | done = True 143 | break 144 | time.sleep(1. / frame_count) 145 | if done: 146 | break 147 | self.form.startEndButton.setText('Start Animation') 148 | self.do_animation = False 149 | 150 | 151 | if __name__ == '__main__': 152 | FemAnimateModeShapes() 153 | -------------------------------------------------------------------------------- /FEM/fem_animate_mode_shapes.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | myAnimate 4 | 5 | 6 | Qt::ApplicationModal 7 | 8 | 9 | 10 | 0 11 | 0 12 | 700 13 | 400 14 | 15 | 16 | 17 | Animate 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | scale 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 300 34 | 0 35 | 36 | 37 | 38 | 0 39 | 40 | 41 | 999 42 | 43 | 44 | 40 45 | 46 | 47 | Qt::Horizontal 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | factor 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 142 67 | 0 68 | 69 | 70 | 71 | 72 | 142 73 | 16777215 74 | 75 | 76 | 77 | 4 78 | 79 | 80 | 999.000000000000000 81 | 82 | 83 | 40.000000000000000 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | number of steps per cycle 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 142 103 | 0 104 | 105 | 106 | 107 | 24 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | steps/second 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 142 127 | 0 128 | 129 | 130 | 131 | 132 | 142 133 | 16777215 134 | 135 | 136 | 137 | 24 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | number of cycles 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 142 157 | 0 158 | 159 | 160 | 161 | 999 162 | 163 | 164 | 2 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | Start Animation 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | -------------------------------------------------------------------------------- /Foto.FCMacro: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # (c) microelly 2015 3 | 4 | __Name__ = 'Foto' 5 | __Comment__ = 'Creates a real sized 300-dpi foto of the selected part from top view' 6 | __Author__ = 'microelly' 7 | __Version__ = '0.1.0' 8 | __Date__ = '' 9 | __License__ = '' 10 | __Web__ = '' 11 | __Wiki__ = '' 12 | __Icon__ = '' 13 | __Help__ = 'Select an object' 14 | __Status__ = 'alpha' 15 | __Requires__ = '' 16 | __Contact__ = 'https://forum.freecadweb.org/memberlist.php?mode=viewprofile&u=2364' 17 | __Communication__ = '' 18 | __Files__ = '' 19 | 20 | import os 21 | 22 | import FreeCADGui 23 | 24 | 25 | def foto(fn='/tmp/fc_camera_out.jpg', width=150, height=100, x=0, y=0, res=300): 26 | if res <= 0: 27 | # TODO: add a warning about wrong res value. 28 | res = 300 29 | dpmm = 11.8110 * res / 300 30 | ppy = int(dpmm * height) 31 | ppx = int(dpmm * width) 32 | y -= 1 33 | cam = '''#Inventor V2.1 ascii 34 | OrthographicCamera {{ 35 | viewportMapping ADJUST_CAMERA 36 | orientation 1 0 0 0.001 37 | nearDistance 0 38 | farDistance 1000 39 | aspectRatio 100 40 | focalDistance 1 41 | position {x} {y} 999 42 | height {height} 43 | }} 44 | 45 | '''.format(x=x, y=y, height=height) 46 | 47 | FreeCADGui.activeDocument().activeView().setCamera(cam) 48 | 49 | FreeCADGui.ActiveDocument.update() 50 | FreeCADGui.activeDocument().activeView().saveImage(fn, ppx, ppy, '#ffffff') 51 | 52 | fn = "/tmp/fc_photo.jpg" 53 | res = 300 54 | 55 | sel = FreeCADGui.Selection.getSelection() 56 | sel0 = sel[0] 57 | sel0.ViewObject.LineWidth = 1 58 | bbox = sel0.Shape.BoundBox 59 | px = 0.5 * (bbox.XMin + bbox.XMax) 60 | py = 0.5 * (bbox.YMin + bbox.YMax) 61 | width = bbox.XMax - bbox.XMin 62 | height = bbox.YMax - bbox.YMin 63 | 64 | foto(fn, width, height, px, py, res) 65 | os.system('xdg-open {}'.format(fn)) 66 | -------------------------------------------------------------------------------- /ImportExport/Export2Slicer.FCMacro: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Opens current visible objects in Slic3r. One can use different slicing software 3 | 4 | __Title__ = '3D Print / Slice' 5 | __Author__ = 'Damian Łoziński' 6 | __Version__ = '0.4.1' 7 | __Date__ = '2024-08-28' 8 | __Comment__ = 'Export selected objects to amf/stl files and open them in slicing program' 9 | __Web__ = 'https://github.com/dlozinski/FreeCAD-macros/blob/doc/ImportExport/ExportToSlicer.md' 10 | __Wiki__ = '' 11 | __Icon__ = 'Export2Slicer.png' 12 | __Help__ = 'You may need to change slicer path. To do so open Tools > Edit parameters > BaseApp/Preferences/Macros/Export2Slicer' 13 | __Status__ = 'Stable' 14 | __Requires__ = 'freecad 0.17+' 15 | __Communication__ = 'lozinski dot d at gmail dot com' 16 | __Files__ = 'Export2Slicer.png' 17 | 18 | import os 19 | import re 20 | from subprocess import Popen 21 | import shlex 22 | 23 | import FreeCAD as app 24 | import FreeCADGui as gui 25 | import Mesh 26 | import MeshPart 27 | 28 | 29 | MACRO_PARAMS = app.ParamGet('User parameter:BaseApp/Preferences/Macros/Export2Slicer') 30 | DEFAULT_SLICER_CMD = '"/Applications/Original Prusa Drivers/PrusaSlicer.app/Contents/MacOS/PrusaSlicer" --single-instance "{file}"' 31 | DEFAULT_OUTPUT_FORMAT = 'amf' 32 | DEFAULT_ANGULAR_DEFLECTION = 0.07 33 | 34 | 35 | def get_string_param(name, default): 36 | value = MACRO_PARAMS.GetString(name) 37 | if not value: 38 | MACRO_PARAMS.SetString(name, default) 39 | value = default 40 | return value 41 | 42 | 43 | def get_float_param(name, default): 44 | value = MACRO_PARAMS.GetFloat(name) 45 | if not value: 46 | MACRO_PARAMS.SetFloat(name, default) 47 | value = default 48 | return value 49 | 50 | 51 | slicer_cmd = get_string_param('SlicerCommand', DEFAULT_SLICER_CMD) 52 | output_format = get_string_param('OutputFormat', DEFAULT_OUTPUT_FORMAT) 53 | angular_deflection = get_float_param('AngularDeflection', DEFAULT_ANGULAR_DEFLECTION) 54 | 55 | 56 | def escape(text): 57 | return re.sub(r'\W', '_', text) 58 | 59 | 60 | def get_mesh_filename(doc_filename, mesh_names): 61 | """Returns valid filename for temporary mesh file.""" 62 | if doc_filename: 63 | dirname = os.path.dirname(doc_filename) 64 | filename = ( 65 | os.path.basename(doc_filename).partition('.')[0] 66 | + '-' 67 | + escape('_'.join(mesh_names)) 68 | + '.' 69 | + output_format) 70 | file_path = os.path.join(dirname, filename) 71 | else: 72 | file_path = 'meshes-export.' + output_format 73 | return file_path 74 | 75 | 76 | def main(): 77 | doc = app.activeDocument() 78 | if not doc: 79 | raise RuntimeError('Export2Slicer: No active document') 80 | 81 | selection = gui.Selection.getSelectionEx() 82 | objects_to_export = [x.Object for x in selection] or [doc.ActiveObject] 83 | try: 84 | # Create temporary doc to store meshes so that we don't affect current doc history 85 | tmp_doc = app.newDocument('meshes_to_export', temp=True) 86 | meshes = [] 87 | mesh_names = [] 88 | for o in objects_to_export: 89 | if o.TypeId == 'Mesh::Feature': 90 | meshes.append(o) 91 | else: 92 | mesh = tmp_doc.addObject('Mesh::Feature', f'{doc.Label}_{o.Label}') 93 | mesh.Mesh = MeshPart.meshFromShape(o.Shape, LinearDeflection=0.1, AngularDeflection=angular_deflection, Relative=False) 94 | meshes.append(mesh) 95 | mesh_names.append(o.Label) 96 | if meshes: 97 | mesh_path = get_mesh_filename(doc.FileName, mesh_names) 98 | Mesh.export(meshes, mesh_path) 99 | else: 100 | raise RuntimeError('Export2Slicer: No objects to export') 101 | finally: 102 | app.closeDocument('meshes_to_export') 103 | for x in selection: 104 | gui.Selection.addSelection(doc.Name, x.ObjectName) 105 | 106 | # Launch Slicer with meshes 107 | Popen(shlex.split(slicer_cmd.format(file=mesh_path))) 108 | app.Console.PrintMessage(f'Export2Slicer: Objects exported into: {mesh_path}\n') 109 | 110 | 111 | try: 112 | main() 113 | except Exception as e: 114 | app.Console.PrintError('Export2Slicer: ERROR: {}\n'.format(e)) 115 | -------------------------------------------------------------------------------- /ImportExport/Export2Slicer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FreeCAD/FreeCAD-macros/d643f70db1fa5a6c582473694da0a5bc8ff3bc37/ImportExport/Export2Slicer.png -------------------------------------------------------------------------------- /ImportExport/FabricationCutlist.FCMacro: -------------------------------------------------------------------------------- 1 | # This macro aims to make it easy to create an accurate cutlist when you are going to fabricate something made out of rectangles and round bar. 2 | # For example, if you're welding together a gate from commercial mild steel, you are probably using square tubing, round bar and / or angle iron. 3 | # For all of these you can use Part WB's *Box* and *Cylinder* types. 4 | # Having done so, this macro will create a comma-separated set of values that make up your cutlist. The collumns are as follows: 5 | # Timestamp: Just throw this away 6 | # Label: The label you gave the part. Perhaps use this to indicate the role of the part in your design. 7 | # Label2: The secondary label you gave the part. Perhaps use this to indicate the profile ("roundbar", "squaretube" etc) 8 | # Dim1: For round bar, this is the diameter. For box shapes, this is one dimension of the profile size. 9 | # Dim2: For round bar, this is blank. For box shapes this is the other dimension of the profile size. 10 | # Length: How long this piece is. 11 | 12 | __Name__ = 'Fabrication Cutlist Maker' 13 | __Comment__ = 'For designs made of Part WB Box and Cylinder primitives, creates a CSV cutlist.' 14 | __Author__ = 'Johan Pretorius' 15 | __Date__ = '2024-08-28' 16 | __Version__ = '2024.8.10' 17 | __License__ = 'AAL' 18 | __Web__ = 'https://github.com/FreeCAD/FreeCAD-macros' 19 | __Wiki__ = '' 20 | __Icon__ = 'FabricationCutlist.png' 21 | __Xpm__ = '' 22 | __Help__ = 'Make your design out of "Box" and "Cylinder" primitives in Part WB. Then run the macro to get a cutlist in the Report View.' 23 | __Status__ = 'Stable' 24 | __Requires__ = 'https://github.com/FreeCAD/FreeCAD-macros/issues/' 25 | __Communication__ = '' 26 | __Files__ = '' 27 | 28 | import FreeCAD as app 29 | 30 | doc = app.activeDocument() 31 | if doc: 32 | objects = doc.Objects 33 | warnings = [] 34 | 35 | app.Console.PrintMessage('------- CSV Cutlist Begins -------\n') 36 | 37 | for obj in objects: 38 | label = obj.Label 39 | label2 = obj.Label2 40 | 41 | if (obj.TypeId == 'Part::Cylinder') and label2: 42 | diameter = obj.Radius.Value * 2 43 | length = obj.Height.Value 44 | app.Console.PrintMessage( 45 | f', {label}, {label2}, {diameter}, , {length}\n') 46 | elif obj.TypeId == 'Part::Cylinder': 47 | warnings.append( 48 | f'"{label}" not used, it is a Part::Cylinder object but has no Label2\n') 49 | elif (obj.TypeId == 'Part::Box') and label2: 50 | size = [obj.Length.Value, obj.Width.Value, obj.Height.Value] 51 | size.sort() 52 | app.Console.PrintMessage( 53 | f', {label}, {label2}, {size[0]}, {size[1]}, {size[2]}\n') 54 | elif obj.TypeId == 'Part::Box': 55 | warnings.append( 56 | f'"{label}" not used, it is a Part::Box object but has no Label2\n') 57 | else: 58 | warnings.append( 59 | f'"{label}" not used, only considering Box and Cylinder objects\n') 60 | 61 | app.Console.PrintMessage('======= CSV Cutlist Completed =======\n') 62 | 63 | for w in warnings: 64 | app.Console.PrintWarning(w) 65 | -------------------------------------------------------------------------------- /ImportExport/FabricationCutlist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FreeCAD/FreeCAD-macros/d643f70db1fa5a6c582473694da0a5bc8ff3bc37/ImportExport/FabricationCutlist.png -------------------------------------------------------------------------------- /ImportExport/ObjectsToPython.svg: -------------------------------------------------------------------------------- 1 | 2 | 12 | 14 | 15 | 17 | image/svg+xml 18 | 20 | 21 | 22 | 23 | 24 | 26 | 34 | 37 | 41 | 42 | 49 | 52 | 56 | 57 | 66 | 69 | 73 | 74 | 75 | 79 | 83 | 87 | 91 | 95 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /ImportExport/ObjectsToPython/ObjectsToPython.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 800 10 | 800 11 | 12 | 13 | 14 | Objects To Python 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | Close 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /ImportExport/ObjectsToPython/README.md: -------------------------------------------------------------------------------- 1 | # Objects To Python 2 | Exports existing objects from a FreeCAD project to a python script 3 | 4 | This macro should create clean, human readable python code from already existing FreeCAD objects. 5 | When the generated code is executed, it should create all objects from scratch. 6 | 7 | ATTENTION: It does not work for all objects in FreeCAD. Especially objects created by built in python scripts like everything in the Draft workbench will not work correctly. 8 | 9 | ## How it works 10 | The macro is quite simple. It loops through the properties of all selected objects, compares then to a default object, and generates script lines for object creation and changed properties. For some objects (Sketcher or Spreadsheet objects), special handling in the macro is needed. It may not be complete. 11 | 12 | ## Usage 13 | This macro generates python code from all selected objects in a FreeCAD project. 14 | If no object is selected, all objects in the project will be selected. 15 | 16 | A dialog will open and the output will be written into it. Press CTRL-a and CTRL-c to copy the code. 17 | 18 | -------------------------------------------------------------------------------- /Information/DatumPlaneLocalAxis.txt: -------------------------------------------------------------------------------- 1 | from FreeCAD import Vector, Placement, Rotation 2 | import FreeCAD as app 3 | 4 | 5 | def make_test_datum_plane_local_axis(): 6 | doc = app.activeDocument() 7 | 8 | # Part / Body / DatumPlane + Part / LinkToBody / DatumPlane. 9 | Part = doc.addObject('App::Part', 'Part') 10 | Part.Placement = Placement(Vector(0.0, 0.0, 10.0), 11 | Rotation(0.0, 0.0, 0.0, 1.0)) 12 | 13 | Body = doc.addObject('PartDesign::Body', 'Body') 14 | Part.addObject(Body) 15 | Body.Placement = Placement(Vector(0.0, 0.0, 20.0), 16 | Rotation(0.0, 0.0, 0.0, 1.0)) 17 | 18 | DatumPlane = doc.addObject('PartDesign::Plane', 'DatumPlane') 19 | Body.addObject(DatumPlane) 20 | DatumPlane.AttachmentOffset = Placement(Vector(10.0, 0.0, 0.0), 21 | Rotation(0.0, 0.0, 0.0, 1.0)) 22 | DatumPlane.MapMode = 'FlatFace' 23 | DatumPlane.Placement = Placement(Vector(10.0, 0.0, 0.0), 24 | Rotation(0.0, 0.0, 0.0, 1.0)) 25 | DatumPlane.Support = [(Body.Origin.OriginFeatures[3], (''))] # XY-plane. 26 | 27 | Box = doc.addObject('PartDesign::AdditiveBox', 'Box') 28 | Body.addObject(Box) 29 | 30 | LinkToBody = doc.addObject('App::Link', 'LinkToBody') 31 | Part.addObject(LinkToBody) 32 | LinkToBody.LinkedObject = Body 33 | LinkToBody.LinkPlacement = Placement(Vector(0.0, 0.0, 40.0), 34 | Rotation(0.0, 0.0, 0.0, 1.0)) 35 | 36 | 37 | # LinkToPart / Body / DatumPlane + LinkToPart / LinkToBody / DatumPlane 38 | LinkToPart = doc.addObject('App::Link', 'LinkToPart') 39 | LinkToPart.LinkPlacement = Placement(Vector(0.0, 50.0, 0.0), 40 | Rotation(0.0, 0.0, 0.0, 1.0)) 41 | LinkToPart.LinkedObject = Part 42 | 43 | # Body / DatumPlane. 44 | Body002 = doc.addObject('PartDesign::Body', 'Body002') 45 | 46 | DatumPlane001 = doc.addObject('PartDesign::Plane', 'DatumPlane001') 47 | Body002.addObject(DatumPlane001) 48 | DatumPlane001.MapMode = 'FlatFace' 49 | DatumPlane001.Support = [(Body002.Origin.OriginFeatures[3], (''))] # XY-plane. 50 | 51 | doc.recompute() 52 | 53 | 54 | make_test_datum_plane_local_axis() 55 | -------------------------------------------------------------------------------- /Information/GetGlobalPlacement-test.txt: -------------------------------------------------------------------------------- 1 | from FreeCAD import Vector, Placement, Rotation, activeDocument 2 | 3 | # A matrix with no rotation. 4 | g_placement_fail_1_1_1 = Placement( 5 | Vector(), 6 | Rotation(0.45642222410108496, 0.49809724456331667, 0.5849212871934979, -0.4488262203714842)) 7 | 8 | 9 | def position(x, y, z): 10 | return Placement(Vector(x, y, z), Rotation ()) 11 | 12 | 13 | def make_GetGlobalPlacement_test(check = False): 14 | # just a visual reference point 15 | doc = activeDocument() 16 | Ref = doc.addObject('PartDesign::CoordinateSystem', 'Ref') 17 | 18 | Cube = doc.addObject('Part::Box', 'Cube') 19 | Cube.Width = Cube.Height = Cube.Length = 2 20 | Cube.Placement = position(0, 0, -1) 21 | 22 | CubeLink = doc.addObject('App::Link', 'CubeLink') 23 | CubeLink.Placement = position(8, -2, 0) 24 | CubeLink.LinkedObject = Cube 25 | CubeLink.Scale = 0.50 26 | CubeLink.LinkTransform = True 27 | 28 | Part = doc.addObject('App::Part', 'Part') 29 | Part.Placement = position(0, 4, 0) 30 | Part.Group = [Cube] 31 | 32 | PartLink = doc.addObject('App::Link', 'PartLink') 33 | PartLink.Placement = position(-2, -2, 0) 34 | PartLink.LinkedObject = Part 35 | PartLink.ElementCount = 2 36 | PartLink.ShowElement = True 37 | PartLink.ElementList[0].Scale = 2 38 | PartLink.ElementList[0].Placement.Rotation.Yaw = 60 39 | PartLink.ElementList[1].Placement = position(5, 0, 0) 40 | 41 | # A test that fails on version 1.1.1 because of scale retrieval. 42 | # Should return the same rotation. 43 | cube_1_1_1 = doc.addObject('Part::Box', 'Fails_on_1_1_1') 44 | cube_1_1_1.Width = 1.0 45 | cube_1_1_1.Height = 1.0 46 | cube_1_1_1.Length = 1.0 47 | cube_1_1_1.Placement = g_placement_fail_1_1_1 48 | 49 | doc.recompute() 50 | 51 | 52 | # Not possible to import a .FCMacro 53 | # the suffix must be changed to .py (for example via a symlink). 54 | try: 55 | from GetGlobalPlacement import get_global_placement_and_scale as p_and_s 56 | except ImportError: 57 | pass 58 | 59 | 60 | def check_GetGlobalPlacement_test(): 61 | tol = 1e-6 62 | doc = App.activeDocument() 63 | Cube = doc.getObject('Cube') 64 | CubeLink = doc.getObject('CubeLink') 65 | Part = doc.getObject('Part') 66 | PartLink = doc.getObject('PartLink') 67 | cube_1_1_1 = doc.getObject('Fails_on_1_1_1') 68 | assert p_and_s(Cube,'')[0].isSame(position(0, 0, -1), tol) 69 | assert p_and_s(CubeLink,'')[0].isSame(position(8, -2, -0.5), tol) 70 | assert p_and_s(Part,'')[0].isSame(position(0, 4, 0), tol) 71 | assert p_and_s(Part,'Cube.')[0].isSame(position(0, 4, -1), tol) 72 | assert p_and_s(PartLink,'')[0].isSame(position(-2, -2, 0), tol) 73 | p = Placement(Vector(-2, -2, 0), Rotation(60, 0, 0)) 74 | assert p_and_s(PartLink,'PartLink_i0.')[0].isSame(p, tol) 75 | p = Placement(Vector(-2, -2, -2), Rotation(60, 0, 0)) 76 | assert p_and_s(PartLink,'0.Cube.Edge1')[0].isSame(p, tol) 77 | assert p_and_s(PartLink,'1.')[0].isSame(position(3, -2, 0), tol) 78 | assert p_and_s(PartLink,'1.Cube.Edge1')[0].isSame(position(3, -2, -1), tol) 79 | assert p_and_s(cube_1_1_1, '')[0].isSame(g_placement_fail_1_1_1, tol) 80 | print('OK') 81 | 82 | 83 | make_GetGlobalPlacement_test() 84 | 85 | # Correct results: 86 | # 87 | # Ref: 0.000, 0.000, 0.000; 0.0000, 0.0000, 0.0000, 1.0000; (rpy: 0.00, 0.00, 0.00) deg 88 | # CubeLink: 8.000, -2.000, -0.500; 0.0000, 0.0000, 0.0000, 1.0000; (rpy: 0.00, 0.00, 0.00) deg 89 | # Part: 0.000, 4.000, 0.000; 0.0000, 0.0000, 0.0000, 1.0000; (rpy: 0.00, 0.00, 0.00) deg 90 | # Part.Cube: 0.000, 4.000, -1.000; 0.0000, 0.0000, 0.0000, 1.0000; (rpy: 0.00, 0.00, 0.00) deg 91 | # PartLink: -2.000, -2.000, 0.000; 0.0000, 0.0000, 0.0000, 1.0000; (rpy: 0.00, 0.00, 0.00) deg 92 | # PartLink.PartLink_i0: -2.000, -2.000, 0.000; 0.0000, 0.0000, 0.5000, 0.8660; (rpy: 0.00, 0.00, 60.00) deg 93 | # PartLink.PartLink_i0.Origin.Z_Axis: -2.000, -2.000, 0.000; -0.1830, 0.6830, 0.1830, 0.6830; (rpy: -30.00, 90.00, 0.00) deg 94 | # PartLink.PartLink_i0.Cube: -2.000, -2.000, -2.000; 0.0000, 0.0000, 0.5000, 0.8660; (rpy: 0.00, 0.00, 60.00) deg 95 | # PartLink.PartLink_i1: 3.000, -2.000, 0.000; 0.0000, 0.0000, 0.0000, 1.0000; (rpy: 0.00, 0.00, 0.00) deg 96 | # PartLink.PartLink_i1.Cube: 3.000, -2.000, -1.000; 0.0000, 0.0000, 0.0000, 1.0000; (rpy: 0.00, 0.00, 0.00) deg 97 | # Fails_on_1_1_1: 0.000, 0.000, 0.000; 0.4564, 0.4981, 0.5849, -0.4488; App.Placement(App.Vector(0.000, 0.000, 0.000), App.Rotation(0.45642, 0.49810, 0.58492, -0.44883)); (rpy: 63.26, -78.83, -158.70) deg 98 | -------------------------------------------------------------------------------- /Information/MeasureCircle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FreeCAD/FreeCAD-macros/d643f70db1fa5a6c582473694da0a5bc8ff3bc37/Information/MeasureCircle.png -------------------------------------------------------------------------------- /Information/SimpleProperties.FCMacro: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | ##################################### 3 | # Copyright (c) openBrain 2019 4 | # Licensed under LGPL v2 5 | # 6 | # This FreeCAD macro will give basic properties of the selected object (volume, boundbox, ...) 7 | # 8 | # 9 | # Version history : 10 | # *0.7 : some typo improvement + commenting 11 | # *0.6 : check if selected object has a valid shape 12 | # *0.5 : beta release 13 | # 14 | ##################################### 15 | 16 | __Name__ = 'Simple Properties' 17 | __Comment__ = 'Gives basic properties of object (volume, boundbox, ...)' 18 | __Author__ = 'openBrain' 19 | __Version__ = '0.7.1' 20 | __Date__ = '2019-07-10' 21 | __License__ = 'LGPL v2' 22 | __Web__ = 'https://www.freecadweb.org/wiki/Macro_SimpleProperties' 23 | __Wiki__ = 'https://www.freecadweb.org/wiki/Macro_SimpleProperties' 24 | __Icon__ = '' 25 | __Help__ = 'Select an object and run the macro' 26 | __Status__ = 'Beta' 27 | __Requires__ = 'FreeCAD >= 0.17' 28 | 29 | __dbg__ = False # True for debugging. 30 | g_disp_width = 3 # Set the display format of numbers. 31 | 32 | from PySide import QtGui 33 | 34 | import FreeCAD as app 35 | import FreeCADGui as gui 36 | 37 | def cslM(msg): #Print message in console 38 | app.Console.PrintMessage('\n') 39 | app.Console.PrintMessage(msg) 40 | 41 | 42 | def cslW(msg): #Print warning in console 43 | app.Console.PrintMessage('\n') 44 | app.Console.PrintWarning(msg) 45 | 46 | 47 | def cslE(msg): #Print error in console 48 | app.Console.PrintMessage('\n') 49 | app.Console.PrintError(msg) 50 | 51 | 52 | def cslD(msg): #Print debug message in console 53 | if __dbg__: 54 | app.Console.PrintMessage('\n') 55 | app.Console.PrintMessage('Debug: ' + str(msg)) 56 | 57 | if __dbg__: ##Clear report view in debug mode 58 | gui.getMainWindow().findChild(QtGui.QTextEdit, 'Report view').clear() 59 | 60 | cslM('Starting Simple Properties macro') 61 | 62 | if len(gui.Selection.getSelection()) != 1: 63 | # If not exactly one object selected, warn user & quit. 64 | cslE('One and only one object shall be selected ... Exiting') 65 | elif not ('Shape' in gui.Selection.getSelection()[0].PropertiesList): 66 | # If selected object has no shape, warn user & exit. 67 | cslE('Selected object has no valid shape ... Exiting') 68 | else: 69 | obj = gui.Selection.getSelection()[0] 70 | # Get selected object. 71 | retStr = '' 72 | if len(gui.Selection.getSelectionEx()[0].SubObjects) != 1: 73 | # If several object subobjects have been selected, ignore & warn user. 74 | cslW('None or several subobject(s) selected, will be ignored') 75 | else: 76 | # If one subobject selected. 77 | objEx = gui.Selection.getSelectionEx()[0].SubObjects[0] 78 | if isinstance(objEx, Part.Edge): 79 | # If it's an edge, print its length. 80 | retStr += 'Edge length: {:.{w}g} mm\n'.format(objEx.Length, w=g_disp_width) 81 | elif isinstance(objEx, Part.Face): 82 | # If it's a face, print its area. 83 | retStr += 'Face area: {:.{w}g} m²\n'.format(objEx.Area / 1000000, w=g_disp_width) 84 | else: 85 | # If other (unsupported) type, warn user. 86 | cslD('Subobject type: ' + str(objEx.ShapeType)) 87 | cslW('Unsupported type of subobject') 88 | retStr += 'Object volume: {:.{w}g} l\n'.format(obj.Shape.Volume / 1000000, w=g_disp_width) 89 | bb = obj.Shape.BoundBox # Get object's bounding box. 90 | retStr += 'Object boundbox : {:.{w}g} x {:.{w}g} x {:.{w}g} mm³\n'.format( 91 | bb.XLength, bb.YLength, bb.ZLength, w=g_disp_width) 92 | 93 | # Display information in a message box. 94 | QtGui.QMessageBox(QtGui.QMessageBox.Information, 'Object Simple Props', retStr).exec_() 95 | -------------------------------------------------------------------------------- /ObjectCreation/AeroFoil_UI_Files/AeroFoil_CurvesInput_Dialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog 4 | 5 | 6 | true 7 | 8 | 9 | 10 | 0 11 | 0 12 | 425 13 | 380 14 | 15 | 16 | 17 | 18 | 425 19 | 380 20 | 21 | 22 | 23 | 24 | 425 25 | 380 26 | 27 | 28 | 29 | AeroFoil : Custom Curves 30 | 31 | 32 | 33 | 34 | 25 35 | 25 36 | 160 37 | 30 38 | 39 | 40 | 41 | Airfoil Profile Type : 42 | 43 | 44 | 45 | 46 | 47 | 295 48 | 335 49 | 100 50 | 30 51 | 52 | 53 | 54 | Next 55 | 56 | 57 | 58 | 59 | 60 | 25 61 | 335 62 | 100 63 | 30 64 | 65 | 66 | 67 | Back 68 | 69 | 70 | 71 | 72 | 73 | 200 74 | 30 75 | 125 76 | 25 77 | 78 | 79 | 80 | Symmetric 81 | 82 | 83 | true 84 | 85 | 86 | true 87 | 88 | 89 | 90 | 91 | 92 | 200 93 | 60 94 | 210 95 | 30 96 | 97 | 98 | 99 | Asymmetric / Cambered 100 | 101 | 102 | false 103 | 104 | 105 | 106 | 107 | 108 | 25 109 | 150 110 | 191 111 | 30 112 | 113 | 114 | 115 | Enter top curve function : 116 | 117 | 118 | 119 | 120 | 121 | 25 122 | 230 123 | 221 124 | 30 125 | 126 | 127 | 128 | Enter bottom curve function : 129 | 130 | 131 | 132 | 133 | 134 | 25 135 | 180 136 | 370 137 | 30 138 | 139 | 140 | 141 | 142 | 143 | false 144 | 145 | 146 | 147 | 25 148 | 260 149 | 370 150 | 30 151 | 152 | 153 | 154 | false 155 | 156 | 157 | 158 | 159 | 160 | 20 161 | 110 162 | 335 163 | 30 164 | 165 | 166 | 167 | PointingHandCursor 168 | 169 | 170 | color: navy; 171 | text-decoration: underline; 172 | border: 0px; 173 | 174 | 175 | Click here to look through the 'List of Functions' 176 | 177 | 178 | 179 | 180 | af_d2c_textbox_1 181 | af_d2c_list_button 182 | af_d2c_radio_1 183 | af_d2c_radio_2 184 | af_d2c_textbox_2 185 | af_d2c_next_button 186 | af_d2c_back_button 187 | 188 | 189 | 190 | 191 | -------------------------------------------------------------------------------- /ObjectCreation/AeroFoil_UI_Files/AeroFoil_DATInput_Dialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog 4 | 5 | 6 | true 7 | 8 | 9 | 10 | 0 11 | 0 12 | 350 13 | 305 14 | 15 | 16 | 17 | 18 | 350 19 | 305 20 | 21 | 22 | 23 | 24 | 350 25 | 305 26 | 27 | 28 | 29 | AeroFoil : Custom Points 30 | 31 | 32 | 33 | 34 | 220 35 | 260 36 | 100 37 | 30 38 | 39 | 40 | 41 | Next 42 | 43 | 44 | 45 | 46 | 47 | 25 48 | 260 49 | 100 50 | 30 51 | 52 | 53 | 54 | Back 55 | 56 | 57 | 58 | 59 | 60 | 25 61 | 60 62 | 240 63 | 30 64 | 65 | 66 | 67 | Commence from Line Number : 68 | 69 | 70 | 71 | 72 | 73 | 25 74 | 90 75 | 211 76 | 30 77 | 78 | 79 | 80 | Terminate to Line Number : 81 | 82 | 83 | 84 | 85 | 86 | 270 87 | 60 88 | 50 89 | 30 90 | 91 | 92 | 93 | 0 94 | 95 | 96 | 999999999 97 | 98 | 99 | 0 100 | 101 | 102 | 103 | 104 | 105 | 270 106 | 90 107 | 50 108 | 30 109 | 110 | 111 | 112 | 0 113 | 114 | 115 | 999999999 116 | 117 | 118 | 0 119 | 120 | 121 | 122 | 123 | 124 | 25 125 | 25 126 | 141 127 | 30 128 | 129 | 130 | 131 | 132 | 50 133 | false 134 | true 135 | 136 | 137 | 138 | Text File Controls : 139 | 140 | 141 | 142 | 143 | 144 | 25 145 | 130 146 | 175 147 | 30 148 | 149 | 150 | 151 | 152 | true 153 | 154 | 155 | 156 | text-decoration: underline; 157 | 158 | 159 | Decimal Point Type : 160 | 161 | 162 | 163 | 164 | 165 | 20 166 | 140 167 | 300 168 | 75 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 15 178 | 35 179 | 150 180 | 30 181 | 182 | 183 | 184 | Period ( . ) 185 | 186 | 187 | true 188 | 189 | 190 | true 191 | 192 | 193 | 194 | 195 | 196 | 180 197 | 35 198 | 211 199 | 30 200 | 201 | 202 | 203 | Comma ( , ) 204 | 205 | 206 | 207 | 208 | 209 | af_d2d1a_spinBox_1 210 | af_d2d1a_spinBox_2 211 | af_d2d1a_radio_1 212 | af_d2d1a_radio_2 213 | af_d2d1a_next_button 214 | af_d2d1a_back_button 215 | 216 | 217 | 218 | 219 | -------------------------------------------------------------------------------- /ObjectCreation/AeroFoil_UI_Files/AeroFoil_FileLoad_Dialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog 4 | 5 | 6 | true 7 | 8 | 9 | 10 | 0 11 | 0 12 | 500 13 | 235 14 | 15 | 16 | 17 | 18 | 500 19 | 235 20 | 21 | 22 | 23 | 24 | 500 25 | 235 26 | 27 | 28 | 29 | AeroFoil : Load 'Data Points' 30 | 31 | 32 | 33 | 34 | 375 35 | 190 36 | 100 37 | 30 38 | 39 | 40 | 41 | Next 42 | 43 | 44 | 45 | 46 | 47 | 25 48 | 190 49 | 100 50 | 30 51 | 52 | 53 | 54 | Back 55 | 56 | 57 | 58 | 59 | 60 | 25 61 | 25 62 | 341 63 | 30 64 | 65 | 66 | 67 | Enter a File Name or Load a File : 68 | 69 | 70 | 71 | 72 | 73 | 25 74 | 90 75 | 450 76 | 30 77 | 78 | 79 | 80 | Load File 81 | 82 | 83 | 84 | 85 | 86 | 25 87 | 55 88 | 450 89 | 30 90 | 91 | 92 | 93 | true 94 | 95 | 96 | 97 | 98 | 99 | 34 100 | 125 101 | 431 102 | 30 103 | 104 | 105 | 106 | 107 | Ubuntu Mono 108 | 12 109 | 75 110 | true 111 | true 112 | false 113 | true 114 | 115 | 116 | 117 | color: black; 118 | 119 | 120 | File Not Loaded 121 | 122 | 123 | Qt::AlignCenter 124 | 125 | 126 | 127 | 128 | af_d2cd2_load_button 129 | af_d2cd2_next_button 130 | af_d2cd2_back_button 131 | af_d2cd2_textbox 132 | 133 | 134 | 135 | 136 | -------------------------------------------------------------------------------- /ObjectCreation/AeroFoil_UI_Files/AeroFoil_Initial_Dialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog 4 | 5 | 6 | true 7 | 8 | 9 | 10 | 0 11 | 0 12 | 390 13 | 150 14 | 15 | 16 | 17 | 18 | 390 19 | 150 20 | 21 | 22 | 23 | 24 | 390 25 | 150 26 | 27 | 28 | 29 | AeroFoil 30 | 31 | 32 | 33 | 34 | 25 35 | 25 36 | 150 37 | 30 38 | 39 | 40 | 41 | Choose airfoil type : 42 | 43 | 44 | 45 | 46 | 47 | 255 48 | 100 49 | 100 50 | 30 51 | 52 | 53 | 54 | Next 55 | 56 | 57 | 58 | 59 | 60 | 25 61 | 100 62 | 100 63 | 30 64 | 65 | 66 | 67 | Close 68 | 69 | 70 | 71 | 72 | 73 | 180 74 | 25 75 | 175 76 | 30 77 | 78 | 79 | 80 | 81 | NACA - 4 Digit 82 | 83 | 84 | 85 | 86 | NACA - 5 Digit 87 | 88 | 89 | 90 | 91 | Custom - Curves 92 | 93 | 94 | 95 | 96 | Custom - Points 97 | 98 | 99 | 100 | 101 | 102 | af_d1_combo 103 | af_d1_next_button 104 | af_d1_close_button 105 | 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /ObjectCreation/AeroFoil_UI_Files/AeroFoil_Math_Functions_Box.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 275 10 | 400 11 | 12 | 13 | 14 | 15 | 275 16 | 400 17 | 18 | 19 | 20 | 21 | 275 22 | 400 23 | 24 | 25 | 26 | AeroFoil : Functions 27 | 28 | 29 | 30 | 31 | 25 32 | 25 33 | 231 34 | 30 35 | 36 | 37 | 38 | 39 | true 40 | 41 | 42 | 43 | List of mathematical functions : 44 | 45 | 46 | 47 | 48 | 49 | 93 50 | 355 51 | 100 52 | 30 53 | 54 | 55 | 56 | Okay 57 | 58 | 59 | 60 | 61 | 62 | 30 63 | 60 64 | 100 65 | 225 66 | 67 | 68 | 69 | QFrame::StyledPanel 70 | 71 | 72 | QFrame::Raised 73 | 74 | 75 | 76 | 77 | 0 78 | 25 79 | 100 80 | 350 81 | 82 | 83 | 84 | + 85 | - 86 | * 87 | / 88 | ^ 89 | e 90 | pi 91 | ln(#) 92 | log(#) 93 | sqrt(#) 94 | 95 | 96 | Qt::AlignHCenter|Qt::AlignTop 97 | 98 | 99 | 100 | 101 | 102 | 103 | 140 104 | 60 105 | 100 106 | 140 107 | 108 | 109 | 110 | QFrame::StyledPanel 111 | 112 | 113 | QFrame::Raised 114 | 115 | 116 | 117 | 118 | 0 119 | 15 120 | 100 121 | 110 122 | 123 | 124 | 125 | sin(θ) 126 | cos(θ) 127 | tan(θ) 128 | asin(θ) 129 | acos(θ) 130 | atan(θ) 131 | 132 | 133 | 134 | Qt::AlignHCenter|Qt::AlignTop 135 | 136 | 137 | 138 | 139 | 140 | 141 | 140 142 | 210 143 | 100 144 | 75 145 | 146 | 147 | 148 | QFrame::StyledPanel 149 | 150 | 151 | QFrame::Raised 152 | 153 | 154 | 155 | 156 | 0 157 | 7 158 | 100 159 | 60 160 | 161 | 162 | 163 | Function 164 | variable 165 | is 'x'. 166 | 167 | 168 | Qt::AlignCenter 169 | 170 | 171 | 172 | 173 | 174 | 175 | 35 176 | 300 177 | 50 178 | 30 179 | 180 | 181 | 182 | 183 | 75 184 | true 185 | true 186 | 187 | 188 | 189 | Note : 190 | 191 | 192 | 193 | 194 | 195 | 90 196 | 300 197 | 150 198 | 30 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | -------------------------------------------------------------------------------- /ObjectCreation/AeroFoil_UI_Files/AeroFoil_NACA4Digit_Dialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog 4 | 5 | 6 | true 7 | 8 | 9 | 10 | 0 11 | 0 12 | 390 13 | 150 14 | 15 | 16 | 17 | 18 | 390 19 | 150 20 | 21 | 22 | 23 | 24 | 390 25 | 150 26 | 27 | 28 | 29 | AeroFoil : NACA - 4 Digit 30 | 31 | 32 | 33 | 34 | 25 35 | 25 36 | 160 37 | 30 38 | 39 | 40 | 41 | Enter airfoil number : 42 | 43 | 44 | 45 | 46 | 47 | 255 48 | 100 49 | 100 50 | 30 51 | 52 | 53 | 54 | Next 55 | 56 | 57 | 58 | 59 | 60 | 25 61 | 100 62 | 100 63 | 30 64 | 65 | 66 | 67 | Back 68 | 69 | 70 | 71 | 72 | 73 | 195 74 | 25 75 | 160 76 | 30 77 | 78 | 79 | 80 | 4 81 | 82 | 83 | Qt::AlignCenter 84 | 85 | 86 | 87 | 88 | af_d2a_textbox 89 | af_d2a_next_button 90 | af_d2a_back_button 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /ObjectCreation/AeroFoil_UI_Files/AeroFoil_NACA5Digit_Dialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog 4 | 5 | 6 | true 7 | 8 | 9 | 10 | 0 11 | 0 12 | 390 13 | 150 14 | 15 | 16 | 17 | 18 | 390 19 | 150 20 | 21 | 22 | 23 | 24 | 390 25 | 150 26 | 27 | 28 | 29 | AeroFoil : NACA - 5 Digit 30 | 31 | 32 | 33 | 34 | 25 35 | 25 36 | 160 37 | 30 38 | 39 | 40 | 41 | Enter airfoil number : 42 | 43 | 44 | 45 | 46 | 47 | 255 48 | 100 49 | 100 50 | 30 51 | 52 | 53 | 54 | Next 55 | 56 | 57 | 58 | 59 | 60 | 25 61 | 100 62 | 100 63 | 30 64 | 65 | 66 | 67 | Back 68 | 69 | 70 | 71 | 72 | 73 | 195 74 | 25 75 | 160 76 | 30 77 | 78 | 79 | 80 | 5 81 | 82 | 83 | Qt::AlignCenter 84 | 85 | 86 | 87 | 88 | af_d2b_textbox 89 | af_d2b_next_button 90 | af_d2b_back_button 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /ObjectCreation/AeroFoil_UI_Files/AeroFoil_PointsInput_Dialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog 4 | 5 | 6 | true 7 | 8 | 9 | 10 | 0 11 | 0 12 | 425 13 | 222 14 | 15 | 16 | 17 | 18 | 425 19 | 222 20 | 21 | 22 | 23 | 24 | 425 25 | 222 26 | 27 | 28 | 29 | AeroFoil : Custom Points 30 | 31 | 32 | 33 | 34 | 295 35 | 180 36 | 100 37 | 30 38 | 39 | 40 | 41 | Next 42 | 43 | 44 | 45 | 46 | 47 | 25 48 | 180 49 | 100 50 | 30 51 | 52 | 53 | 54 | Back 55 | 56 | 57 | 58 | 59 | 60 | 30 61 | 120 62 | 201 63 | 25 64 | 65 | 66 | 67 | Mirror the Airfoil Profile 68 | 69 | 70 | false 71 | 72 | 73 | true 74 | 75 | 76 | 77 | 78 | 79 | 25 80 | 25 81 | 371 82 | 71 83 | 84 | 85 | 86 | QFrame::StyledPanel 87 | 88 | 89 | QFrame::Raised 90 | 91 | 92 | 93 | 94 | 25 95 | 10 96 | 191 97 | 30 98 | 99 | 100 | 101 | Import from : 102 | 103 | 104 | 105 | 106 | 107 | 225 108 | 10 109 | 125 110 | 25 111 | 112 | 113 | 114 | Text File (*.dat) 115 | 116 | 117 | true 118 | 119 | 120 | true 121 | 122 | 123 | 124 | 125 | 126 | 225 127 | 35 128 | 210 129 | 30 130 | 131 | 132 | 133 | CSV File (*.csv) 134 | 135 | 136 | false 137 | 138 | 139 | 140 | 141 | 142 | af_d2d_radio_1 143 | af_d2d_radio_2 144 | af_d2d_checkbox 145 | af_d2d_next_button 146 | af_d2d_back_button 147 | 148 | 149 | 150 | 151 | -------------------------------------------------------------------------------- /ObjectCreation/AeroFoil_UI_Files/AeroFoil_mfb_img.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FreeCAD/FreeCAD-macros/d643f70db1fa5a6c582473694da0a5bc8ff3bc37/ObjectCreation/AeroFoil_UI_Files/AeroFoil_mfb_img.gif -------------------------------------------------------------------------------- /ObjectCreation/AirfoilImportAndScale.FCMacro: -------------------------------------------------------------------------------- 1 | # This Macro, when run, will first provide the user with a file browser to 2 | # locate and select a .dat airfoil text file. Once selected, a chord length is 3 | # entered and upon pressing the OK button, a properly scaled airfoil is 4 | # produced. There are two versions provided here. Version 1.5 should work on 5 | # FreeCAD versions, 0.13 stable as well as all 0.14 versions. Version 2 should 6 | # only be used with FreeCAD versions equal to or greater than 0.14 3077 and 7 | # will work best with versions built with OCE/OCC versions 6.7 or greater (See 8 | # the Wiki page for all available version). 9 | # 10 | # (c) quick61 11 | 12 | __Name__ = 'Airfoil Import and Scale' 13 | __Comment__ = 'Imports and scales an Airfoil in the form of a Draft Wire (DWire) or Basic Spline (BSpline)' 14 | __Author__ = "quick61" 15 | __Version__ = '2.1.2' 16 | __Date__ = '2024-01-14' 17 | __License__ = '' 18 | __Web__ = 'http://forum.freecadweb.org/viewtopic.php?f=22&t=5554' 19 | __Wiki__ = 'http://www.freecadweb.org/wiki/Macro_Airfoil_Import_%26_Scale' 20 | __Icon__ = '' 21 | __Help__ = '' 22 | __Status__ = 'stable' 23 | __Requires__ = 'freecad >= 0.14.3706' 24 | __Communication__ = '' 25 | __Files__ = '' 26 | 27 | 28 | import FreeCAD as app 29 | import FreeCADGui as gui 30 | from PySide import QtCore, QtGui # FreeCAD's PySide! 31 | import Draft # FreeCAD. 32 | import importAirfoilDAT # From the Draft module. 33 | 34 | # Select .dat airfoil data file to be imported 35 | 36 | # PySide returns a tuple (filename, filter) instead of just a string like in PyQt 37 | filename, filefilter = QtGui.QFileDialog.getOpenFileName( 38 | gui.getMainWindow(), 'Open An Airfoil File', '*.dat') 39 | 40 | 41 | class AirfoilImporterAndScaler(): 42 | 43 | def __init__(self): 44 | self.dialog = None 45 | 46 | # Make dialog box and get the scale size 47 | self.dialog = QtGui.QDialog(gui.getMainWindow()) 48 | self.dialog.resize(350, 100) 49 | self.dialog.setWindowTitle('Airfoil Import & Scale') 50 | layout = QtGui.QVBoxLayout(self.dialog) 51 | label = QtGui.QLabel('Chord Length') 52 | layout.addWidget(label) 53 | self.line_edit_scale = QtGui.QLineEdit() 54 | layout.addWidget(self.line_edit_scale) 55 | 56 | # Add radio buttons to select between DWire and BSpline 57 | self.radio_dwire = QtGui.QRadioButton('Make DWire') 58 | self.radio_bspline = QtGui.QRadioButton('Make BSpline') 59 | 60 | # set default to DWire & make radio buttons - Change self.radio1.setChecked(True) to 61 | # self.radio2.setChecked(True) to set BSpline as default 62 | 63 | self.radio_dwire.setChecked(True) 64 | layout.addWidget(self.radio_dwire) 65 | layout.addWidget(self.radio_bspline) 66 | 67 | # Add OK / Cancel buttons 68 | button_box = QtGui.QDialogButtonBox(self.dialog) 69 | button_box.setOrientation(QtCore.Qt.Horizontal) 70 | button_box.setStandardButtons( 71 | QtGui.QDialogButtonBox.Cancel | QtGui.QDialogButtonBox.Ok) 72 | layout.addWidget(button_box) 73 | button_box.accepted.connect(self.proceed) 74 | button_box.rejected.connect(self.close) 75 | QtCore.QMetaObject.connectSlotsByName(self.dialog) 76 | self.dialog.show() 77 | self.dialog.exec_() 78 | 79 | def proceed(self): 80 | global filename 81 | if self.radio_dwire.isChecked(): 82 | try: 83 | # This produces a scaled Airfoil with a DWire 84 | scalefactor = float(self.line_edit_scale.text()) 85 | f1 = str(filename) 86 | importAirfoilDAT.insert(f1, 'Unnamed') 87 | Draft.scale( 88 | app.ActiveDocument.ActiveObject, 89 | delta=app.Vector(scalefactor, scalefactor, scalefactor), 90 | center=app.Vector(0, 0, 0), 91 | legacy=True) 92 | except Exception as e: 93 | app.Console.PrintError('Error, not a valid .dat file\n') 94 | 95 | self.close() 96 | 97 | if self.radio_bspline.isChecked(): 98 | try: 99 | # This produces a scaled Airfoil with a BSpline 100 | scalefactor = float(self.line_edit_scale.text()) 101 | f1 = str(filename) 102 | importAirfoilDAT.insert(f1, 'Unnamed') 103 | points = app.ActiveDocument.ActiveObject.Points 104 | Draft.makeBSpline(points, closed=True) 105 | Draft.scale(app.ActiveDocument.ActiveObject, 106 | delta=app.Vector(scalefactor, scalefactor, scalefactor), 107 | center=app.Vector(0, 0, 0), 108 | legacy=True) 109 | app.getDocument('Unnamed').removeObject('DWire') 110 | except: 111 | app.Console.PrintError('Error, not a valid .dat file\n') 112 | 113 | self.close() 114 | 115 | def close(self): 116 | self.dialog.hide() 117 | 118 | 119 | AirfoilImporterAndScaler() 120 | -------------------------------------------------------------------------------- /ObjectCreation/BoxCreator.FCMacro: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | __Name__ = 'BoxCreator' 4 | __Comment__ = 'Creates a box with interlocked notches' 5 | __Author__ = 'christi' 6 | __Version__ = '1.3.7' 7 | __Date__ = '2023-01-03' 8 | __License__ = 'LGPL-3.0-or-later' 9 | __Web__ = 'https://forum.freecadweb.org/viewtopic.php?t=31795' 10 | __Wiki__ = '' 11 | __Icon__ = 'BoxCreator.svg' 12 | __Help__ = 'Try it out and play with it. It should be self explanatory' 13 | __Status__ = 'Beta' 14 | __Requires__ = 'FreeCAD >= v0.17' 15 | __Communication__ = 'https://forum.freecadweb.org/viewtopic.php?p=264483' 16 | __Files__ = 'BoxCreator.svg,boxcreator/__init__.py,boxcreator/boxcreator.py,boxcreator/boxcreator.ui' 17 | 18 | 19 | import os 20 | 21 | import FreeCAD as app 22 | import FreeCADGui as gui 23 | from FreeCAD import Vector 24 | 25 | from boxcreator import boxcreator 26 | 27 | 28 | class BoxcreatorDialog(): 29 | """Show a dialog for BoxCreator""" 30 | def __init__(self): 31 | self.ui_file = os.path.join(app.getUserMacroDir(True), 32 | 'boxcreator/boxcreator.ui') 33 | self.form = gui.PySideUic.loadUi(self.ui_file) 34 | self._connect_widgets() 35 | self.form.show() 36 | 37 | def _connect_widgets(self): 38 | self.form.pushButtonCreate.pressed.connect(self.createBox) 39 | self.form.pushButton_CompartX.pressed.connect(self.compartmentX) 40 | self.form.pushButton_CompartY.pressed.connect(self.compartmentY) 41 | self.form.pushButton_CompartZ.pressed.connect(self.compartmentZ) 42 | 43 | def createBox(self): 44 | boxWidth = self.form.doubleSpinBoxWidth.value() 45 | boxHeight = self.form.doubleSpinBoxHeight.value() 46 | boxLength = self.form.doubleSpinBoxLength.value() 47 | notchWidth = self.form.doubleSpinBoxNotchWidth.value() 48 | materialWidth = self.form.doubleSpinBoxMaterialWidth.value() 49 | drawSides = [self.form.checkBoxTop.isChecked(), 50 | self.form.checkBoxBottom.isChecked(), 51 | self.form.checkBoxLeft.isChecked(), 52 | self.form.checkBoxRight.isChecked(), 53 | self.form.checkBoxFront.isChecked(), 54 | self.form.checkBoxBack.isChecked()] 55 | overhangTop = [self.form.overhangTopLeft.value(), 56 | self.form.overhangTopRight.value(), 57 | self.form.overhangTopFront.value(), 58 | self.form.overhangTopBack.value()] 59 | overhangBottom = [self.form.overhangBotLeft.value(), 60 | self.form.overhangBotRight.value(), 61 | self.form.overhangBotFront.value(), 62 | self.form.overhangBotBack.value()] 63 | if (boxWidth == 0) or (boxHeight == 0) or (boxLength == 0): 64 | app.Console.PrintError('Error! None of the values can be 0!') 65 | # we bail out without doing anything 66 | return 67 | 68 | box = boxcreator.create_box(materialWidth, boxWidth, boxHeight, boxLength, notchWidth, drawSides, overhangTop, overhangBottom, app.activeDocument()) 69 | gui.Selection.clearSelection() 70 | gui.Selection.addSelection(box) 71 | gui.SendMsgToActiveView('ViewFit') 72 | 73 | def compartmentX(self): 74 | self.createCompartment(Vector(1,0,0)) 75 | 76 | def compartmentY(self): 77 | self.createCompartment(Vector(0,1,0)) 78 | 79 | def compartmentZ(self): 80 | self.createCompartment(Vector(0,0,1)) 81 | 82 | def createCompartment(self, direction): 83 | box = gui.Selection.getSelection() 84 | notchWidth = self.form.doubleSpinBoxNotchWidth.value() 85 | materialWidth = self.form.doubleSpinBoxMaterialWidth.value() 86 | drawSides = [self.form.checkBoxTop.isChecked(), 87 | self.form.checkBoxBottom.isChecked(), 88 | self.form.checkBoxLeft.isChecked(), 89 | self.form.checkBoxRight.isChecked(), 90 | self.form.checkBoxFront.isChecked(), 91 | self.form.checkBoxBack.isChecked()] 92 | offset = self.form.compartmentOffset.value() 93 | boxsize = Vector(self.form.doubleSpinBoxWidth.value(), self.form.doubleSpinBoxLength.value(), self.form.doubleSpinBoxHeight.value()) 94 | compartment = boxcreator.create_compartment(box, direction, offset, materialWidth, notchWidth, drawSides, boxsize) 95 | if compartment: 96 | gui.Selection.clearSelection() 97 | gui.Selection.addSelection(compartment) 98 | 99 | 100 | if __name__ == '__main__': 101 | d = BoxcreatorDialog() 102 | -------------------------------------------------------------------------------- /ObjectCreation/EllipseCenter2Points.FCMacro: -------------------------------------------------------------------------------- 1 | __Name__ = 'Ellipse Center + 2 Points' 2 | __Comment__ = '' 3 | __Author__ = 'Eriossoltero' 4 | __Version__ = '' 5 | __Date__ = '' 6 | __License__ = 'LGPL-2.0-or-later' 7 | __Web__ = 'http://freecad-tutorial.blogspot.com/2011/12/engine-9-poly-v-belt.html' 8 | __Wiki__ = '' 9 | __Icon__ = '' 10 | __Help__ = '' 11 | __Status__ = '' 12 | __Requires__ = '' 13 | __Communication__ = '' 14 | __Files__ = '' 15 | 16 | import FreeCAD as app 17 | import FreeCADGui as gui 18 | import Part, PartGui 19 | from FreeCAD import Base 20 | 21 | # get the selected objects, with first selection for the trajectory and second for the section 22 | # Adapted from: 23 | # http://freecad-tutorial.blogspot.com/2011/12/engine-9-poly-v-belt.html 24 | s = gui.Selection.getSelection() 25 | try: 26 | sel1 = s[0].Shape 27 | sel2 = s[1].Shape 28 | sel3 = s[2].Shape 29 | except: 30 | app.Console.PrintError('Wrong selection') 31 | 32 | pt_center = sel1.Point 33 | pt_radmay = sel2.Point 34 | pt_radmen = sel3.Point 35 | 36 | # create Part object in the current document 37 | myObject = app.ActiveDocument.addObject('Part::Feature', 'Ellipse') 38 | 39 | # create a shape and assign it to the current document 40 | ellipse = Part.Ellipse(pt_radmay, pt_radmen, pt_center) 41 | myObject.Shape = ellipse.toShape() 42 | -------------------------------------------------------------------------------- /ObjectCreation/HilbertCurve.svg: -------------------------------------------------------------------------------- 1 | 2 | 13 | 15 | 16 | 18 | image/svg+xml 19 | 21 | 22 | 23 | 24 | 25 | 27 | 31 | 35 | b'Hilbert' 37 | 38 | 39 | -------------------------------------------------------------------------------- /ObjectCreation/ParabolaCreater.FCMacro: -------------------------------------------------------------------------------- 1 | __Name__ = 'Parabola Creater' 2 | __Comment__ = 'Parabola Creater' 3 | __Author__ = 'unknown' 4 | __Version__ = '1.0.1' 5 | __Date__ = '2019-02-02' 6 | __License__ = 'LGPL-2.0-or-later' 7 | __Web__ = '' 8 | __Wiki__ = '' 9 | __Icon__ = '' 10 | __Help__ = '' 11 | __Status__ = '' 12 | __Requires__ = '' 13 | __Communication__ = 'https://github.com/FreeCAD/FreeCAD-macros/issues/' 14 | __Files__ = '' 15 | 16 | import FreeCAD as app 17 | import Part, PySide 18 | from PySide import QtCore, QtGui 19 | from PySide.QtGui import QLineEdit, QRadioButton 20 | 21 | 22 | class ParabolaCreater(): 23 | def __init__(self): 24 | self.dialog = None 25 | 26 | self.dialog = QtGui.QDialog() 27 | #self.dialog.resize(450,110) 28 | 29 | self.dialog.setWindowTitle("Parabola Creator") 30 | la = QtGui.QVBoxLayout(self.dialog) 31 | 32 | self.radio1 = QRadioButton("Make 2D Shape") 33 | self.radio2 = QRadioButton("Make 3D Revolution") 34 | 35 | # set default to "Make 2D Shape" & make radio buttons - Change self.radio1.setChecked(True) to 36 | # self.radio2.setChecked(True) to set "Make 3D Revolution" as default 37 | 38 | self.radio1.setChecked(True) 39 | la.addWidget(self.radio1) 40 | la.addWidget(self.radio2) 41 | 42 | iN1 = QtGui.QLabel("Range Of Curve - Minimum") 43 | la.addWidget(iN1) 44 | self.r1 = QtGui.QLineEdit() 45 | la.addWidget(self.r1) 46 | self.r1.setText('-3') 47 | 48 | iN2 = QtGui.QLabel("Range Of Curve - Maximum") 49 | la.addWidget(iN2) 50 | self.r2 = QtGui.QLineEdit() 51 | la.addWidget(self.r2) 52 | self.r2.setText('3') 53 | 54 | iN3 = QtGui.QLabel("Focus") 55 | la.addWidget(iN3) 56 | self.f1 = QtGui.QLineEdit() 57 | la.addWidget(self.f1) 58 | self.f1.setText('2') 59 | 60 | iN4 = QtGui.QLabel("Degrees of Revolve (Only valid for 3D Revolution)") 61 | la.addWidget(iN4) 62 | self.Dg = QtGui.QLineEdit() 63 | la.addWidget(self.Dg) 64 | self.Dg.setText('360') 65 | 66 | okbox = QtGui.QDialogButtonBox(self.dialog) 67 | okbox.setOrientation(QtCore.Qt.Horizontal) 68 | okbox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) 69 | la.addWidget(okbox) 70 | QtCore.QObject.connect(okbox, QtCore.SIGNAL("accepted()"), self.makeHyp) 71 | QtCore.QObject.connect(okbox, QtCore.SIGNAL("rejected()"), self.close) 72 | QtCore.QMetaObject.connectSlotsByName(self.dialog) 73 | self.dialog.show() 74 | self.dialog.exec_() 75 | 76 | def makeHyp(self): 77 | if self.radio1.isChecked(): 78 | try: 79 | tS1 = float(self.r1.text()) 80 | tS2 = float(self.r2.text()) 81 | fc = float(self.f1.text()) 82 | 83 | para=Part.Parabola() 84 | para.Focal = fc 85 | 86 | shape=para.toShape(tS1,tS2) 87 | Part.show(shape) 88 | except: 89 | sayexc() 90 | app.Console.PrintError("Unable to complete task") 91 | 92 | self.close() 93 | 94 | if self.radio2.isChecked(): 95 | try: 96 | tS1 = float(self.r1.text()) 97 | tS2 = float(self.r2.text()) 98 | fc = float(self.f1.text()) 99 | Dg = float(self.Dg.text()) 100 | 101 | para=Part.Parabola() 102 | para.Focal = fc 103 | 104 | shape=para.toShape(tS1,tS2) 105 | rev=shape.revolve(app.Vector(1,0,0),app.Vector(1,0,0),Dg) # revolve around X axis by number of degrees 106 | Part.show(rev) 107 | except: 108 | sayexc() 109 | app.Console.PrintError("Unable to complete task") 110 | 111 | self.close() 112 | 113 | def close(self): 114 | self.dialog.hide() 115 | 116 | if __name__ == '__main__': 117 | ParabolaCreater() 118 | -------------------------------------------------------------------------------- /ObjectCreation/TimingGear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FreeCAD/FreeCAD-macros/d643f70db1fa5a6c582473694da0a5bc8ff3bc37/ObjectCreation/TimingGear.png -------------------------------------------------------------------------------- /ObjectCreation/boxcreator/Readme.md: -------------------------------------------------------------------------------- 1 | # FreeCAD Box Creator Macro 2 | 3 | Creates a box with interlocked notches. 4 | 5 | ![alt text](boxcreator_screenshot.jpg "Screenshot") 6 | 7 | ## Installation 8 | In menu Tools select Addon Manager 9 | Select the Macros tab 10 | find Boxcreator in the list and click Install 11 | 12 | In menu Macro select Macros... 13 | Execute boxcreatorGUI.py 14 | 15 | ### Manual Installation 16 | Copy the .py files to your FreeCAD Macro directory (on Linux: ~/.FreeCAD/Macro) 17 | 18 | ## Support for Compartments 19 | ![alt text](boxcreator_screenshot2.jpg "Screenshot") -------------------------------------------------------------------------------- /ObjectCreation/boxcreator/__init__.py: -------------------------------------------------------------------------------- 1 | from . import boxcreator 2 | -------------------------------------------------------------------------------- /ObjectCreation/boxcreator/boxcreator_screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FreeCAD/FreeCAD-macros/d643f70db1fa5a6c582473694da0a5bc8ff3bc37/ObjectCreation/boxcreator/boxcreator_screenshot.jpg -------------------------------------------------------------------------------- /ObjectCreation/boxcreator/boxcreator_screenshot2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FreeCAD/FreeCAD-macros/d643f70db1fa5a6c582473694da0a5bc8ff3bc37/ObjectCreation/boxcreator/boxcreator_screenshot2.jpg -------------------------------------------------------------------------------- /ObjectCreation/g3d/translations/g3d_en.txt: -------------------------------------------------------------------------------- 1 | ## Strings for translation into any language (English Strings) 2 | 3 | "3D Generator", 4 | "Select a sketch in 2 or 3 views:", 5 | "Front view - (Front/Rear):", 6 | "Check this box if you want to activate the front view and assign it a sketch", 7 | "Side view - (Right/Left):", 8 | "Check this box if you want to activate the side view and assign it a sketch", 9 | "Plan view - (Top/Bott):", 10 | "Check this box if you want to activate the plan view and assign it a sketch", 11 | "Tol. 3D (mm):", 12 | "Length in millimeters. This should not influence the final object. If you do not need this tolerance, set this value to 0", 13 | "Make a new group (folder) with views (sketches)", 14 | "Hide views when generating the solid", 15 | "Fusion (intersection)", 16 | "Version:", 17 | "Brief guide to use the script:", 18 | "Create a sketch for each view required to generate your 3D solid. At least two sketches will be required.", 19 | "Then click on the script icon and select a sketch for each view.", 20 | "Finally, press the OK button to generate the solid.", 21 | "Documentation:", 22 | "Offset", 23 | "Offset from the sketch plane from which the extrusion along the Y axis will start", 24 | "Length", 25 | "Total length of the frontal extrusion along the Y axis (it must be always larger than the frontal offset)", 26 | "Offset", 27 | "Offset from the sketch plane from which the extrusion along the X axis will start", 28 | "Length", 29 | "Total length of the lateral extrusion along the X axis (it must be always larger than the lateral offset)", 30 | "Offset", 31 | "Offset from the sketch plane from which the extrusion along the Z axis will start", 32 | "Length", 33 | "Total length of the vertical extrusion along the Z axis (it must be always larger than the vertical offset)", 34 | "3D-Part", 35 | "3D-Part_Views" 36 | -------------------------------------------------------------------------------- /ObjectCreation/g3d/translations/g3d_es.txt: -------------------------------------------------------------------------------- 1 | ## Cadenas de traducción a cualquier idioma (Spanish Strings) 2 | 3 | "Generador 3D", 4 | "Seleccione un sketch (boceto) en 2 o 3 vistas:", 5 | "Frontal -Alzado- (Front/Rear):", 6 | "Marque esta casilla si quiere activar la vista frontal y asígnele un sketch (boceto)", 7 | "Lateral -Perfil- (Right/Left):", 8 | "Marque esta casilla si quiere activar la vista lateral y asígnele un sketch (boceto)", 9 | "Planta -Sup/Inf- (Top/Bott):", 10 | "Marque esta casilla si quiere activar la vista en planta y asígnele un sketch (boceto)", 11 | "Tol. 3D (mm.):", 12 | "Es la distancia en mm. que extruirá de más. No debe influir en el objeto final. Si no desea tolerancia, ponga este valor a 0", 13 | "Crear un Grupo (Carpeta) para las vistas (sketches)", 14 | "Ocultar las vistas al generar el sólido", 15 | "Fusión (interesección)", 16 | "Versión:", 17 | "Información Breve de Uso:", 18 | "Cree un boceto (sketch) para cada vista con la que desee generar un sólido 3D. Necesitará al menos 2 bocetos.", 19 | "Luego pinche en el icono del script y seleccione un boceto para cada vista.", 20 | "Finalmente, pulse OK para generar el sólido.", 21 | "Documentación:", 22 | "Offset FR", 23 | "Es la distancia desde la cual empezará la extrusión en el eje Y (desplazamiento en Y)", 24 | "Longitud FR", 25 | "Es la distancia total de extrusión frontal (en el eje Y), que siempre será mayor que el Offset Frontal", 26 | "Offset LAT", 27 | "Es la distancia desde la cual empezará la extrusión en el eje X (desplazamiento en X)", 28 | "Longitud LAT", 29 | "Es la distancia total de extrusión lateral (en el eje X), que siempre será mayor que el Offset Lateral", 30 | "Offset PL", 31 | "Es la distancia desde la cual empezará la extrusión en el eje Z (desplazamiento en Z)", 32 | "Longitud PL", 33 | "Es la distancia total de extrusión en planta (en el eje Z), que siempre será mayor que el Offset en Planta", 34 | "Pieza3D", 35 | "VistasPieza3D" 36 | -------------------------------------------------------------------------------- /ObjectCreation/g3d/translations/g3d_it.txt: -------------------------------------------------------------------------------- 1 | ## Stringhe per la traduzione in qualsiasi lingua (stringhe in italiano) 2 | 3 | "Generatore 3D", 4 | "Seleziona uno schizzo in 2 o 3 viste:", 5 | "Vista frontale - (Fronte/Retro):", 6 | "Spunta questa casella se desideri utilizzare la vista frontale ed assegnarle uno schizzo", 7 | "Vista laterale - (Destra/Sinistra):", 8 | "Spunta questa casella se desideri utilizzare la vista laterale ed assegnarle uno schizzo", 9 | "Vista in pianta - (Alto/Basso):", 10 | "Spunta questa casella se desideri utilizzare la vista in pianta ed assegnarle uno schizzo", 11 | "Tol. 3D (mm.):", 12 | "Lunghezza in mm di sovraestrusione. Non dovrebbe influenzare l'oggetto finale. Se non si necessita questa tolleranza impostare il valore relativo a 0 mm", 13 | "Crea un nuovo gruppo (cartella) con le viste (sketch)", 14 | "Nasconde le viste quando viene generato il solido", 15 | "Fusione (intersezione)", 16 | "Versione:", 17 | "Breve guida all'uso dello script:", 18 | "Crea uno sketch per ogni vista necessaria a generare il solido. Sono necessari almeno due sketch.", 19 | "Quindi clicca sull'icona dello script e seleziona uno schizzo per ciascuna vista necessaria.", 20 | "Infine, premi il pulsante OK per generare il solido.", 21 | "Documentazione:", 22 | "Offset", 23 | "Distanza rispetto al piano dello sketch frontale da cui inizierà l'estrusione lungo l'asse Y (spostamento in Y)", 24 | "Lunghezza", 25 | "Lunghezza totale dell'estrusione frontale lungo l'asse Y (dovrà essere sempre più grande dell'offset frontale)", 26 | "Offset", 27 | "Distanza rispetto al piano dello sketch laterale da cui inizierà l'estrusione lungo l'asse X (spostamento in X)", 28 | "Lunghezza", 29 | "Lunghezza totale dell'estrusione laterale lungo l'asse X (dovrà essere sempre più grande dell'offset laterale)", 30 | "Offset", 31 | "Distanza rispetto al piano dello sketch in pianta da cui inizierà l'estrusione lungo l'asse Z (spostamento in Z)", 32 | "Lunghezza", 33 | "Lunghezza totale dell'estrusione verticale lungo l'asse Z (dovrà essere sempre più grande dell'offset in pianta)", 34 | "Pezzo3D", 35 | "Pezzo3D_Viste" 36 | -------------------------------------------------------------------------------- /ParametricObjectCreation/GeodesicDome.FCMacro: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # ************************************************************************ 4 | # * Copyright (c)2015 Ulrich Brammer * 5 | # * * 6 | # * This file is a supplement to the FreeCAD CAx development system. * 7 | # * * 8 | # * This program is free software; you can redistribute it and/or modify * 9 | # * it under the terms of the GNU Lesser General Public License (LGPL) * 10 | # * as published by the Free Software Foundation; either version 2 of * 11 | # * the License, or (at your option) any later version. * 12 | # * for detail see the LICENCE text file. * 13 | # * * 14 | # * This software is distributed in the hope that it will be useful, * 15 | # * but WITHOUT ANY WARRANTY; without even the implied warranty of * 16 | # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 17 | # * GNU Library General Public License for more details. * 18 | # * * 19 | # * You should have received a copy of the GNU Library General Public * 20 | # * License along with this macro; if not, write to the Free Software * 21 | # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * 22 | # * USA * 23 | # * * 24 | # ************************************************************************ 25 | 26 | __Name__ = 'Geodesic Dome' 27 | __Comment__ = 'Geodesic Dome' 28 | __Author__ = 'Ulrich Brammer' 29 | __Version__ = '1.0.0' 30 | __Date__ = '2019-03-23' 31 | __License__ = 'LGPL-2.0-or-later' 32 | __Web__ = '' 33 | __Wiki__ = '' 34 | __Icon__ = '' 35 | __Help__ = '' 36 | __Status__ = '' 37 | __Requires__ = '' 38 | __Communication__ = 'https://github.com/FreeCAD/FreeCAD-macros/issues/' 39 | __Files__ = 'geodesic_dome/__init__.py,geodesic_dome/geodesic_dome.py' 40 | 41 | if __name__ == '__main__': 42 | #running as a macro. Load as module, to support save-restore. 43 | try: 44 | from geodesic_dome.geodesic_dome import GeodesicDome 45 | except ImportError as err: 46 | FreeCAD.Console.PrintError("Macro Geodesic Dome: failed to import MacroGeodesicDome.py. The parametric object will lose functionality after saving-loading your project, sorry.") 47 | else: 48 | from geodesic_dome.geodesic_dome import showDialog 49 | showDialog() 50 | -------------------------------------------------------------------------------- /ParametricObjectCreation/HoneycombSolid.FCMacro: -------------------------------------------------------------------------------- 1 | """ 2 | HoneycombSolid --> Honeycomb solid creator. 3 | (c) 2021 Christian González Di Antonio 4 | 5 | """ 6 | 7 | __Name__ = 'HoneycombSolid' 8 | __Comment__ = 'Macro to create a Honeycomb solid' 9 | __Author__ = "Christian González Di Antonio " 10 | __Version__ = 'v1.2.0' 11 | __License__ = 'LGPL-2.0-or-later' 12 | __Web__ = 'https://github.com/christiangda/FreeCAD-macros-HoneycombSolid' 13 | __Wiki__ = 'https://github.com/christiangda/FreeCAD-macros-HoneycombSolid/blob/main/README.md' 14 | __Icon__ = 'HoneycombSolid.xpm' 15 | __Help__ = '' 16 | __Status__ = 'Stable' 17 | __Requires__ = '' 18 | __Communication__ = 'https://github.com/FreeCAD/FreeCAD-macros/issues/' 19 | __Files__ = 'honeycomb_solid/__init__.py,honeycomb_solid/honeycomb_solid.py' 20 | __Xpm__ = ''' 21 | /* XPM */ 22 | static const char * ViewProviderHoneycombSolid_xpm[] = { 23 | "16 16 54 1", 24 | " c None", 25 | ". c #181818", 26 | "+ c #5F5F5F", 27 | "@ c #636363", 28 | "# c #353535", 29 | "$ c #474747", 30 | "% c #434343", 31 | "& c #606060", 32 | "* c #424242", 33 | "= c #111111", 34 | "- c #444444", 35 | "; c #151515", 36 | "> c #3F3F3F", 37 | ", c #1E1E1E", 38 | "' c #1C1C1C", 39 | ") c #1B1B1B", 40 | "! c #2C2C2C", 41 | "~ c #535353", 42 | "{ c #0A0A0A", 43 | "] c #363636", 44 | "^ c #383838", 45 | "/ c #2F2F2F", 46 | "( c #252525", 47 | "_ c #555555", 48 | ": c #393939", 49 | "< c #515151", 50 | "[ c #262626", 51 | "} c #161616", 52 | "| c #464646", 53 | "1 c #4F4F4F", 54 | "2 c #545454", 55 | "3 c #3A3A3A", 56 | "4 c #131313", 57 | "5 c #121212", 58 | "6 c #5E5E5E", 59 | "7 c #0C0C0C", 60 | "8 c #0F0F0F", 61 | "9 c #0B0B0B", 62 | "0 c #0D0D0D", 63 | "a c #1D1D1D", 64 | "b c #292929", 65 | "c c #3D3D3D", 66 | "d c #222222", 67 | "e c #171717", 68 | "f c #1A1A1A", 69 | "g c #282828", 70 | "h c #272727", 71 | "i c #5A5A5A", 72 | "j c #3C3C3C", 73 | "k c #595959", 74 | "l c #616161", 75 | "m c #505050", 76 | "n c #2E2E2E", 77 | "o c #565656", 78 | " .+@@ #$ ", 79 | " %@&* =-$ ", 80 | " ;>,')!~& ", 81 | " {]^ /@@ ", 82 | "@ ($$ _@@ ", 83 | "@ :$ /@@ ", 84 | "<][}|1 2~3['", 85 | "445#6@ 78889", 86 | "% '&@@ #$ ", 87 | "$ $@@ 0-$ ", 88 | " a&@+ b$$ ", 89 | " c*[de>_ ", 90 | "@ fgh {i@@ ", 91 | "@ ^$ j@@ ", 92 | "@@ 4|$ {k@@ ", 93 | "lm ,n$ ]@o^["}; 94 | ''' 95 | 96 | import FreeCAD as app 97 | 98 | 99 | if __name__ == '__main__': 100 | # Running as a macro. Load as module, to support save-restore. 101 | try: 102 | from honeycomb_solid.honeycomb_solid import HoneycombSolid 103 | from honeycomb_solid.honeycomb_solid import ViewProviderHoneycombSolid 104 | except ImportError as err: 105 | app.Console.PrintError('Macro HoneycombSolid: failed to import module honeycomb_solid.honeycomb_solid\n') 106 | else: 107 | from honeycomb_solid.honeycomb_solid import makeHoneycombSolid 108 | makeHoneycombSolid(__Version__) 109 | if app.GuiUp: 110 | import FreeCADGui as gui 111 | gui.SendMsgToActiveView('ViewFit') 112 | -------------------------------------------------------------------------------- /ParametricObjectCreation/HoneycombSolid.xpm: -------------------------------------------------------------------------------- 1 | /* XPM */ 2 | static const char * ViewProviderHoneycombSolid_xpm[] = { 3 | "16 16 54 1", 4 | " c None", 5 | ". c #181818", 6 | "+ c #5F5F5F", 7 | "@ c #636363", 8 | "# c #353535", 9 | "$ c #474747", 10 | "% c #434343", 11 | "& c #606060", 12 | "* c #424242", 13 | "= c #111111", 14 | "- c #444444", 15 | "; c #151515", 16 | "> c #3F3F3F", 17 | ", c #1E1E1E", 18 | "' c #1C1C1C", 19 | ") c #1B1B1B", 20 | "! c #2C2C2C", 21 | "~ c #535353", 22 | "{ c #0A0A0A", 23 | "] c #363636", 24 | "^ c #383838", 25 | "/ c #2F2F2F", 26 | "( c #252525", 27 | "_ c #555555", 28 | ": c #393939", 29 | "< c #515151", 30 | "[ c #262626", 31 | "} c #161616", 32 | "| c #464646", 33 | "1 c #4F4F4F", 34 | "2 c #545454", 35 | "3 c #3A3A3A", 36 | "4 c #131313", 37 | "5 c #121212", 38 | "6 c #5E5E5E", 39 | "7 c #0C0C0C", 40 | "8 c #0F0F0F", 41 | "9 c #0B0B0B", 42 | "0 c #0D0D0D", 43 | "a c #1D1D1D", 44 | "b c #292929", 45 | "c c #3D3D3D", 46 | "d c #222222", 47 | "e c #171717", 48 | "f c #1A1A1A", 49 | "g c #282828", 50 | "h c #272727", 51 | "i c #5A5A5A", 52 | "j c #3C3C3C", 53 | "k c #595959", 54 | "l c #616161", 55 | "m c #505050", 56 | "n c #2E2E2E", 57 | "o c #565656", 58 | " .+@@ #$ ", 59 | " %@&* =-$ ", 60 | " ;>,')!~& ", 61 | " {]^ /@@ ", 62 | "@ ($$ _@@ ", 63 | "@ :$ /@@ ", 64 | "<][}|1 2~3['", 65 | "445#6@ 78889", 66 | "% '&@@ #$ ", 67 | "$ $@@ 0-$ ", 68 | " a&@+ b$$ ", 69 | " c*[de>_ ", 70 | "@ fgh {i@@ ", 71 | "@ ^$ j@@ ", 72 | "@@ 4|$ {k@@ ", 73 | "lm ,n$ ]@o^["}; 74 | -------------------------------------------------------------------------------- /ParametricObjectCreation/Rectellipse.FCMacro: -------------------------------------------------------------------------------- 1 | # 2 | # Creates a parametric Rectellipse 3 | # (c) fcaponi78 4 | # 5 | # 6 | 7 | from __future__ import division 8 | 9 | __Comment__ = 'Creates a parametric approximation of a rectellipse' 10 | __Web__ = 'http://freecadweb.org/wiki/Macro_Rectellipse' 11 | __Wiki__ = 'http://freecadweb.org/wiki/Macro_Rectellipse' 12 | __Icon__ = "" 13 | __Help__ = 'Run, zoom fit, and change parameters' 14 | __Author__ = 'fcaponi78, shoogen and other contributors' 15 | __Version__ = 0.1 16 | __Status__ = 'alpha' 17 | __Requires__ = '' 18 | 19 | import FreeCAD 20 | from FreeCAD import Part 21 | from FreeCAD import Vector 22 | 23 | 24 | class RectEllipseFeature: 25 | def __init__(self, obj): 26 | """Add the properties: a, b, s, createFace""" 27 | obj.addProperty('App::PropertyLength', 'a', 'Rectellipse', 28 | 'Horizontal radius').a = 16.0 29 | obj.addProperty('App::PropertyLength', 'b', 'Rectellipse', 30 | 'Vertical radius').b = 9.0 31 | obj.addProperty('App::PropertyFloat', 's', 'Rectellipse', 32 | 'Squareness (0=ellipse, 1=rectangle)').s = 0.2 33 | obj.addProperty('App::PropertyBool', 'createFace', 'Rectellipse', 34 | 'true => face; false => wire').createFace = True 35 | obj.Proxy = self 36 | 37 | def onChanged(self, feature, prop): 38 | if prop in ['a', 'b', 's', 'createFace']: 39 | self.execute(feature) 40 | 41 | def execute(self, feature): 42 | r1 = feature.a 43 | r2 = feature.b 44 | if feature.s < 0: 45 | feature.s = 0 46 | elif feature.s > 1: 47 | feature.s = 1 48 | z = 0.0 49 | # TODO: tweak p, so that the curve is the exact definition of a 50 | # rectellipse (if possible). 51 | p = 1.0 52 | # NURBS knots weigts. 53 | if feature.s > 0.9999999: 54 | # If larger than 1e7, we obtain a rhombus. 55 | w = 1e7 56 | else: 57 | w = 2 ** 0.5 / 2.0 / (1 - feature.s ** p) 58 | # TODO: use a real rectangle if feature.s close to 1. 59 | curve = Part.BSplineCurve() 60 | curve.setPeriodic() 61 | curve.increaseDegree(2) 62 | # 5 knots, 8 poles 63 | curve.insertKnots([i / 4 for i in (1, 2, 3)], [2] * 3) 64 | curve.setPole(1, Vector(0, -r2, z), 1) 65 | curve.setPole(2, Vector(-r1, -r2, z), w) 66 | curve.setPole(3, Vector(-r1, 0, z), 1) 67 | curve.setPole(4, Vector(-r1, r2, z), w) 68 | curve.setPole(5, Vector(0, r2, z), 1) 69 | curve.setPole(6, Vector(r1, r2, z), w) 70 | curve.setPole(7, Vector(r1, 0, z), 1) 71 | curve.setPole(8, Vector(r1, -r2, z), w) 72 | if feature.createFace: 73 | feature.Shape = Part.Face(Part.Wire(curve.toShape())) 74 | else: 75 | feature.Shape = curve.toShape() 76 | 77 | 78 | def makeRectellipseFeature(): 79 | doc = FreeCAD.activeDocument() 80 | if doc is None: 81 | doc = FreeCAD.newDocument() 82 | if doc is None: 83 | return 84 | obj = doc.addObject('Part::FeaturePython', 'RectEllipseFeature') 85 | obj.Label = 'RectEllipse' 86 | RectEllipseFeature(obj) 87 | obj.ViewObject.Proxy = 0 88 | 89 | if __name__ == "__main__": 90 | makeRectellipseFeature() 91 | -------------------------------------------------------------------------------- /ParametricObjectCreation/geodesic_dome/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FreeCAD/FreeCAD-macros/d643f70db1fa5a6c582473694da0a5bc8ff3bc37/ParametricObjectCreation/geodesic_dome/__init__.py -------------------------------------------------------------------------------- /ParametricObjectCreation/honeycomb_solid/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | HoneycombSolid --> Honeycomb solid creator. 3 | (c) 2021 Christian González Di Antonio 4 | """ -------------------------------------------------------------------------------- /PureGui/Camera.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FreeCAD/FreeCAD-macros/d643f70db1fa5a6c582473694da0a5bc8ff3bc37/PureGui/Camera.png -------------------------------------------------------------------------------- /PureGui/Camera/accept.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FreeCAD/FreeCAD-macros/d643f70db1fa5a6c582473694da0a5bc8ff3bc37/PureGui/Camera/accept.png -------------------------------------------------------------------------------- /PureGui/Camera/align_object_to_view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FreeCAD/FreeCAD-macros/d643f70db1fa5a6c582473694da0a5bc8ff3bc37/PureGui/Camera/align_object_to_view.png -------------------------------------------------------------------------------- /PureGui/Camera/align_to_axis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FreeCAD/FreeCAD-macros/d643f70db1fa5a6c582473694da0a5bc8ff3bc37/PureGui/Camera/align_to_axis.png -------------------------------------------------------------------------------- /PureGui/Camera/align_to_face.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FreeCAD/FreeCAD-macros/d643f70db1fa5a6c582473694da0a5bc8ff3bc37/PureGui/Camera/align_to_face.png -------------------------------------------------------------------------------- /PureGui/Camera/axis_rotation_d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FreeCAD/FreeCAD-macros/d643f70db1fa5a6c582473694da0a5bc8ff3bc37/PureGui/Camera/axis_rotation_d.png -------------------------------------------------------------------------------- /PureGui/Camera/axis_rotation_x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FreeCAD/FreeCAD-macros/d643f70db1fa5a6c582473694da0a5bc8ff3bc37/PureGui/Camera/axis_rotation_x.png -------------------------------------------------------------------------------- /PureGui/Camera/axis_rotation_y.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FreeCAD/FreeCAD-macros/d643f70db1fa5a6c582473694da0a5bc8ff3bc37/PureGui/Camera/axis_rotation_y.png -------------------------------------------------------------------------------- /PureGui/Camera/axis_rotation_z.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FreeCAD/FreeCAD-macros/d643f70db1fa5a6c582473694da0a5bc8ff3bc37/PureGui/Camera/axis_rotation_z.png -------------------------------------------------------------------------------- /PureGui/Camera/create_plane_of_view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FreeCAD/FreeCAD-macros/d643f70db1fa5a6c582473694da0a5bc8ff3bc37/PureGui/Camera/create_plane_of_view.png -------------------------------------------------------------------------------- /PureGui/Camera/detect_orientation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FreeCAD/FreeCAD-macros/d643f70db1fa5a6c582473694da0a5bc8ff3bc37/PureGui/Camera/detect_orientation.png -------------------------------------------------------------------------------- /PureGui/Camera/quit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FreeCAD/FreeCAD-macros/d643f70db1fa5a6c582473694da0a5bc8ff3bc37/PureGui/Camera/quit.png -------------------------------------------------------------------------------- /PureGui/Camera/reset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FreeCAD/FreeCAD-macros/d643f70db1fa5a6c582473694da0a5bc8ff3bc37/PureGui/Camera/reset.png -------------------------------------------------------------------------------- /PureGui/Camera/save_view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FreeCAD/FreeCAD-macros/d643f70db1fa5a6c582473694da0a5bc8ff3bc37/PureGui/Camera/save_view.png -------------------------------------------------------------------------------- /PureGui/GuiResetToolbars.FCMacro: -------------------------------------------------------------------------------- 1 | # Reset Toolbars position 2 | # Author: Milos Petrasinovic 3 | # PROTORS, Belgrade, Serbia 4 | # info@protors.co 5 | # 6 | # -------------------- 7 | # 8 | # Copyright (C) 2020 PROTORS 9 | # 10 | # This program is free software: you can redistribute it and/or modify 11 | # it under the terms of the GNU Lesser General Public License as 12 | # published by the Free Software Foundation, either version 3 of the 13 | # License, or (at your option) any later version. 14 | # 15 | # This program is distributed in the hope that it will be useful, 16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | # GNU Lesser General Public License for more details. 19 | # 20 | # You should have received a copy of the GNU Lesser General Public License 21 | # along with this program. If not, see . 22 | # 23 | # -------------------- 24 | 25 | __Name__ = 'GuiResetToolbars' 26 | __Comment__ = 'Reset Toolbars position' 27 | __Author__ = 'PROTORS' 28 | __Version__ = '1.0.0' 29 | __Date__ = '2020-04-21' 30 | __License__ = 'LGPL-3.0-or-later' 31 | __Web__ = "https://github.com/protors/ResetToolbars/" 32 | __Wiki__ = 'https://wiki.freecadweb.org/Macro_GuiResetToolbars' 33 | __Icon__ = 'GuiResetToolbars.svg' 34 | __Help__ = 'Run the macro within a workbench that has missing toolbar(s)' 35 | __Status__ = 'stable' 36 | __Requires__ = 'Freecad >= 0.18.4' 37 | __Communication__ = 'https://github.com/protors/ResetToolbars/issues/' 38 | __Files__ = 'GuiResetToolbars.svg' 39 | 40 | import FreeCADGui as gui 41 | from PySide import QtGui, QtCore # FreeCAD's special PySide! 42 | 43 | mw = gui.getMainWindow() 44 | tb = mw.findChildren(QtGui.QToolBar) 45 | for i in tb: 46 | mw.addToolBar(QtCore.Qt.TopToolBarArea, i) 47 | -------------------------------------------------------------------------------- /PureGui/GuiSpacemouseRotationOff.FCMacro: -------------------------------------------------------------------------------- 1 | # Run to deactivate rotations on Spacemouse device. 2 | 3 | __Name__ = 'Spacemouse Rotation Off' 4 | __Comment__ = 'Run to deactivate rotations on Spacemouse device.' 5 | __Author__ = 'tja2468,R3D3,galou_breizh' 6 | __Version__ = '1.0.0' 7 | __Date__ = '2021-11-01' 8 | __License__ = 'LGPL-2.0-or-later' 9 | __Web__ = 'http://forum.freecadweb.org/viewtopic.php?f=?&t=????' 10 | __Wiki__ = 'http://www.freecadweb.org/wiki/Macro_Title_Of_macro' 11 | __Icon__ = '' 12 | __Help__ = 'Run to deactivate rotations on Spacemouse device.' 13 | __Status__ = 'production' 14 | __Requires__ = 'FreeCAD 0.20' 15 | __Communication__ = 'https://forum.freecadweb.org/viewtopic.php?f=22&t=61482' 16 | __Files__ = '' 17 | 18 | import FreeCAD as app 19 | 20 | # Activate rotations. 21 | app.ParamGet('User parameter:BaseApp/Spaceball/Motion').SetBool('Rotations', False) 22 | 23 | # display message to convey status 24 | app.Console.PrintMessage('Spacemouse rotations disabled\n') 25 | -------------------------------------------------------------------------------- /PureGui/GuiSpacemouseRotationOn.FCMacro: -------------------------------------------------------------------------------- 1 | # Run to activate rotations on Spacemouse device. 2 | 3 | __Name__ = 'Spacemouse Rotation On' 4 | __Comment__ = 'Run to activate rotations on Spacemouse device.' 5 | __Author__ = 'tja2468,R3D3,galou_breizh' 6 | __Version__ = '1.0.0' 7 | __Date__ = '2021-11-01' 8 | __License__ = 'LGPL-2.0-or-later' 9 | __Web__ = 'http://forum.freecadweb.org/viewtopic.php?f=?&t=????' 10 | __Wiki__ = 'http://www.freecadweb.org/wiki/Macro_Title_Of_macro' 11 | __Icon__ = '' 12 | __Help__ = 'Run to activate rotations on Spacemouse device.' 13 | __Status__ = 'production' 14 | __Requires__ = 'FreeCAD 0.20' 15 | __Communication__ = 'https://forum.freecadweb.org/viewtopic.php?f=22&t=61482' 16 | __Files__ = '' 17 | 18 | import FreeCAD as app 19 | 20 | # Activate rotations. 21 | app.ParamGet('User parameter:BaseApp/Spaceball/Motion').SetBool('Rotations', True) 22 | 23 | # display message to convey status 24 | app.Console.PrintMessage('Spacemouse rotations enabled\n') 25 | -------------------------------------------------------------------------------- /PureGui/GuiSpacemouseRotationToggle.FCMacro: -------------------------------------------------------------------------------- 1 | # Run to toggle the activation of rotations on Spacemouse device. 2 | 3 | __Name__ = 'Spacemouse Rotation Toggle' 4 | __Comment__ = 'Run to toggle the activation of rotations on Spacemouse device.' 5 | __Author__ = 'tja2468,R3D3,galou_breizh' 6 | __Version__ = '1.0.0' 7 | __Date__ = '2021-11-01' 8 | __License__ = 'LGPL-2.0-or-later' 9 | __Web__ = 'http://forum.freecadweb.org/viewtopic.php?f=?&t=????' 10 | __Wiki__ = 'http://www.freecadweb.org/wiki/Macro_Title_Of_macro' 11 | __Icon__ = '' 12 | __Help__ = 'Run to toggle the activation of rotations on Spacemouse device.' 13 | __Status__ = 'production' 14 | __Requires__ = 'FreeCAD 0.20' 15 | __Communication__ = 'https://forum.freecadweb.org/viewtopic.php?f=22&t=61482' 16 | __Files__ = '' 17 | 18 | import FreeCAD as app 19 | 20 | rotation_active = app.ParamGet('User parameter:BaseApp/Spaceball/Motion').GetBool('Rotations') 21 | 22 | # Toggle the activation state. 23 | app.ParamGet('User parameter:BaseApp/Spaceball/Motion').SetBool('Rotations', not rotation_active) 24 | 25 | # Display a message to convey the new status. 26 | if rotation_active: 27 | app.Console.PrintMessage('Spacemouse rotations disabled\n') 28 | else: 29 | app.Console.PrintMessage('Spacemouse rotations enabled\n') 30 | -------------------------------------------------------------------------------- /PureGui/ViewRotationOut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FreeCAD/FreeCAD-macros/d643f70db1fa5a6c582473694da0a5bc8ff3bc37/PureGui/ViewRotationOut.png -------------------------------------------------------------------------------- /PureGui/ViewRotationRight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FreeCAD/FreeCAD-macros/d643f70db1fa5a6c582473694da0a5bc8ff3bc37/PureGui/ViewRotationRight.png -------------------------------------------------------------------------------- /PureGui/ViewRotationUp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FreeCAD/FreeCAD-macros/d643f70db1fa5a6c582473694da0a5bc8ff3bc37/PureGui/ViewRotationUp.png -------------------------------------------------------------------------------- /Sketcher/SketchUnmap.FCMacro: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | ##################################### 3 | # Copyright (c) openBrain 2019 4 | # Licensed under LGPL v2 5 | # 6 | # This FreeCAD macro will unmap a sketch from eg. a face and make its placement absolute in the body. It proposes 3 options (forcely the first if sketch not in a body) : 7 | # * "Raw" mode => the sketch placement is made absolute in the body referential, nothing more 8 | # * "DP@Face mode" => a datum plane is created where the mapping face is, then the sketch is attached to it respecting its attachment offset 9 | # * "DP@Sketch" mode => a datum place is created where the sketch is (including attachment offset), then the sketch is attached to its origin 10 | # 11 | # Main use of the macro is to make design more robust to topological naming issue 12 | # 13 | # Version history : 14 | # *0.6.2 : add icon (metadata) 15 | # *0.6.1 : minor changes after PR review 16 | # *0.6 : some typo improvement + commenting for official PR 17 | # *0.5 : beta release 18 | # 19 | ##################################### 20 | 21 | __Name__ = 'SketchUnmap' 22 | __Comment__ = 'Unmap a sketch & makes its placement absolute"' 23 | __Author__ = 'openBrain' 24 | __Version__ = '0.6.2' 25 | __Date__ = '2019-06-14' 26 | __License__ = 'LGPL v2' 27 | __Web__ = 'https://www.freecadweb.org/wiki/Macro_SketchUnmap' 28 | __Wiki__ = 'https://www.freecadweb.org/wiki/Macro_SketchUnmap' 29 | __Icon__ = 'SketchUnmap.svg' 30 | __Help__ = 'Select the sketch to unmap (eg. in the tree view) then run the macro' 31 | __Status__ = 'Beta' 32 | __Requires__ = 'FreeCAD >= 0.17' 33 | __Communication__ = 'https://forum.freecadweb.org/viewtopic.php?f=22&t=36078' 34 | # __Files__ = '' 35 | 36 | __dbg__ = False #True for debugging 37 | 38 | from PySide import QtGui 39 | 40 | def cslM(msg): #Print message in console 41 | FreeCAD.Console.PrintMessage('\n') 42 | FreeCAD.Console.PrintMessage(msg) 43 | 44 | def cslW(msg): #Print warning in console 45 | FreeCAD.Console.PrintMessage('\n') 46 | FreeCAD.Console.PrintWarning(msg) 47 | 48 | def cslE(msg): #Print error in console 49 | FreeCAD.Console.PrintMessage('\n') 50 | FreeCAD.Console.PrintError(msg) 51 | 52 | def cslD(msg): #Print debug message in console 53 | if __dbg__: 54 | FreeCAD.Console.PrintMessage('\n') 55 | FreeCAD.Console.PrintMessage("Debug : " + str(msg)) 56 | 57 | _0Vec_ = FreeCAD.Vector(0, 0, 0) #Shortener for null vector 58 | _ZVec_ = FreeCAD.Vector(0, 0, 1) #Shortener for Z axis 59 | 60 | if __dbg__: ##Clear report view in debug mode 61 | FreeCADGui.getMainWindow().findChild(QtGui.QTextEdit, "Report view").clear() 62 | 63 | cslM("Starting SketchUnmap macro") 64 | cslD("Checking selection") 65 | 66 | ##If selection is wrong, print error message and exit 67 | if (len(Gui.Selection.getSelection()) != 1 or str(Gui.Selection.getSelection()[0]) != ''): 68 | cslE("You must select a sketch that you want to unmap (and only one) ... Exiting") 69 | else: 70 | cslD("Selection OK") 71 | App.ActiveDocument.openTransaction("SketchUnmap") #Open transaction for undo management 72 | sk = Gui.Selection.getSelection()[0] #Get target sketch 73 | skNatPl = sk.Placement #Store placement 74 | skNatAO = sk.AttachmentOffset #Store attachment offset 75 | skInBody = False 76 | for obj in sk.InList: ##Check if sketch is inside a Body 77 | if obj.TypeId == 'PartDesign::Body': 78 | skInBody = True 79 | skBody = obj 80 | cslD("Sketch in a body : " + str(skInBody)) 81 | msgb = QtGui.QMessageBox() ##Prepare the message box to choose option 82 | msgb.setWindowTitle("Unmap Sketch : Mode selection") 83 | msgb.setText("""Choose which unmapping mode you want to apply : 84 | Raw => the sketch placement is made absolute in the body referential, nothing more 85 | DP@Face => a datum plane is created where the mapping face is, then the sketch is attached to it respecting its attachment offset 86 | DP@Sketch => a datum place is created where the sketch is (including attachment offset), then the sketch is attached to its origin 87 | Click 'Cancel' to cancel operation""") 88 | msgb.setIcon(QtGui.QMessageBox.Question) 89 | rbut = msgb.addButton("Raw", QtGui.QMessageBox.AcceptRole) 90 | fbut = msgb.addButton("DP@Face", QtGui.QMessageBox.AcceptRole) 91 | sbut = msgb.addButton("DP@Sketch", QtGui.QMessageBox.AcceptRole) 92 | cbut = msgb.addButton("Cancel", QtGui.QMessageBox.RejectRole) 93 | if skInBody: #If sketch in a Body 94 | msgb.exec_() ##Show message box and get user choice 95 | msgbRep = msgb.clickedButton() 96 | else: 97 | msgbRep = rbut #Else apply raw mode automatically 98 | if msgbRep == rbut: #If raw mode 99 | sk.Support = None ##Just unmap the sketch so its placement is absolute 100 | sk.MapMode = 'Deactivated' 101 | elif msgbRep == fbut: #If DP@Face mode 102 | newDP = skBody.newObject('PartDesign::Plane','DP' + sk.Name) #Create a datum plane 103 | if sk.Label != sk.Name: ##Eventually clarify its label 104 | newDP.Label = 'DP' + sk.Label 105 | newDP.Support = None ##Plane is placed absolute 106 | newDP.MapMode = 'Deactivated' 107 | newDP.Placement = skNatPl.multiply(skNatAO.inverse()) #Plane placement is set to former face one 108 | sk.Support = [(newDP,'')] #Sketch is mapped to plane keeping its attachment offset 109 | elif msgbRep == sbut: #If DP@Sketch mode 110 | newDP = skBody.newObject('PartDesign::Plane','DP' + sk.Name) #Create a datum plane 111 | if sk.Label != sk.Name: ##Eventually clarify its label 112 | newDP.Label = 'DP' + sk.Label 113 | newDP.Support = None ##Plane is placed absolute 114 | newDP.MapMode = 'Deactivated' 115 | newDP.Placement = skNatPl #Plane placement is set to former sketch one 116 | sk.Support = [(newDP,'')] ##Sketch is mapped to plane resetting its attachment offset 117 | sk.AttachmentOffset = App.Placement(_0Vec_, App.Rotation(_ZVec_, 0)) 118 | App.ActiveDocument.commitTransaction() #Commit transaction for undo management 119 | cslM("SketchUnmap Macro ended correctly") 120 | -------------------------------------------------------------------------------- /Sketcher/SketcherBlockAll.FCMacro: -------------------------------------------------------------------------------- 1 | # Add a block constraint on all the geometry elements of a sketch. 2 | 3 | __Name__ = 'Sketcher Block All' 4 | __Comment__ = 'Add a block constraint on all the geometry elements of a sketch.' 5 | __Author__ = 'galou_breizh' 6 | __Version__ = '1.0.0' 7 | __Date__ = '2021-11-20' 8 | __License__ = 'LGPL-2.0-or-later' 9 | __Web__ = '' 10 | __Wiki__ = '' 11 | __Icon__ = '' 12 | __Help__ = '' 13 | __Status__ = '' 14 | __Requires__ = 'FreeCAD >=0.19' 15 | __Communication__ = 'https://github.com/FreeCAD/FreeCAD-macros/issues/' 16 | __Files__ = '' 17 | 18 | from typing import Optional 19 | 20 | import FreeCAD as app 21 | 22 | import FreeCADGui as gui 23 | 24 | import Sketcher 25 | 26 | 27 | def get_sketch() -> Optional[Sketcher.Sketch]: 28 | """Get the opened or selected sketch.""" 29 | active_workbench = gui.activeWorkbench() 30 | if active_workbench.name() == 'SketcherWorkbench': 31 | # ActiveSketch is defined by the Sketcher Workbench. 32 | return ActiveSketch 33 | else: 34 | sel = gui.Selection.getSelection() 35 | if sel and sel[0].TypeId == 'Sketcher::SketchObject': 36 | return sel[0] 37 | 38 | 39 | def block_sketch(sketch: Sketcher.Sketch) -> None: 40 | """Add a 'Block' constraint on all geometry elements.""" 41 | for i in range(len(sketch.Geometry)): 42 | try: 43 | sketch.addConstraint(Sketcher.Constraint('Block', i)) 44 | except: 45 | pass 46 | 47 | 48 | sketch = get_sketch() 49 | if sketch is not None: 50 | block_sketch(sketch) 51 | else: 52 | app.Console.PrintWarning('No sketch selected, doing nothing') 53 | 54 | -------------------------------------------------------------------------------- /Sketcher/SketcherClipView.FCMacro: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | ############################################### 4 | # 5 | # SketcherClipView 6 | # 7 | # This macro creates a temporary cross section at the 8 | # sketch plane in order to 'look into' an object when you 9 | # want to create a feature on the inside (i.e. create a revolved 10 | # groove on the inner side of a tube). 11 | # 12 | # The macro creates a clipping plane at the sketch plane and 13 | # activates it. Running the macro again deletes the clipping plane. 14 | # 15 | # If you map the macro to F7 it behaves almost exactly like 16 | # in Autodesk Inventor, from which I got the inspiration. 17 | # 18 | # (c) Ricardo Beck 19 | ################################################# 20 | 21 | __Name__ = 'Sketcher Clip View' 22 | __Comment__ = 'Creates a temporary clipping plane at the support plane of the sketch' 23 | __License__ = '' 24 | __Web__ = 'https://forum.freecadweb.org/viewtopic.php?t=26608' 25 | __Wiki__ = '' 26 | __Icon__ = 'SketcherClipView.svg' 27 | __Help__ = 'Launch while editing a sketch' 28 | __Author__ = 'Ricardo Beck, galou_breizh' 29 | __Version__ = '1.1.0' 30 | __Date__ = '2018-05-29' 31 | __Status__ = 'Beta' 32 | __Requires__ = 'FreeCAD >= v0.17' 33 | __Files__ = 'SketcherClipView.svg' 34 | 35 | from pivy import coin 36 | 37 | import freecad as fc 38 | import FreeCAD as app 39 | import FreeCADGui as gui 40 | from PySide import QtCore 41 | 42 | 43 | def in_edit_mode(): 44 | """Return True if the Sketcher is in edit mode 45 | 46 | This is done by checking whether ActiveSketch is defined and 47 | ActiveSketch.ViewObject.TempoVis is not None. 48 | """ 49 | in_edit_mode = False 50 | try: 51 | in_edit_mode = (ActiveSketch.ViewObject.TempoVis is not None) 52 | except NameError: 53 | pass 54 | return in_edit_mode 55 | 56 | 57 | def check_leave_edit(): 58 | if not in_edit_mode(): 59 | clean() 60 | 61 | 62 | def clean(): 63 | if hasattr(fc, 'SketcherClipView_clip_plane'): 64 | gui.activeDocument().ActiveView.getSceneGraph().removeChild(fc.SketcherClipView_clip_plane) 65 | del fc.SketcherClipView_clip_plane 66 | del fc.SketcherClipView_timer 67 | 68 | 69 | def main(): 70 | # Check if there is a temporary clipping plane from a previous run of the 71 | # macro. 72 | if hasattr(fc, 'SketcherClipView_clip_plane'): 73 | # Clipping plane found, remove from scenegraph and delete the object. 74 | clean() 75 | return 76 | 77 | if not in_edit_mode(): 78 | return 79 | 80 | # No clipping plane found, create one at the sketch base. 81 | 82 | # Get the placement information of the active sketch. 83 | try: 84 | mat = ActiveSketch.getGlobalPlacement() 85 | except NameError: 86 | # ActiveSketch undefined (i.e. not in sketch editing mode). 87 | return 88 | point = mat.Base 89 | normal = mat.Rotation.multVec(app.Vector(0, 0, 1)) 90 | 91 | # Create coin3d vectors of the sketch position. 92 | coin_normal_vector = coin.SbVec3f(normal.x, normal.y, normal.z) 93 | coin_normal_vector.negate() 94 | coin_base_point = coin.SbVec3f(point.x, point.y, point.z) 95 | 96 | # Offset of the clipping plane so the sketch elements (lines, etc) are not cut off. 97 | sketch_offset_factor = -0.02 98 | 99 | coin_normal_vector_normalized = coin.SbVec3f(coin_normal_vector) 100 | coin_normal_vector_normalized.normalize() 101 | 102 | # Offset the clipping plane base by a fraction of the (normalized) normal vector. 103 | coin_base_point += (coin_normal_vector_normalized * sketch_offset_factor) 104 | 105 | # Create the clipping plane at the calculated position. 106 | fc.SketcherClipView_clip_plane = coin.SoClipPlane() 107 | fc.SketcherClipView_clip_plane.plane.setValue(coin.SbPlane(coin_normal_vector,coin_base_point)) 108 | gui.activeDocument().ActiveView.getSceneGraph().insertChild(fc.SketcherClipView_clip_plane, 0) 109 | 110 | # Switch clipping plane on. 111 | fc.SketcherClipView_clip_plane.on.setValue(True) 112 | 113 | fc.SketcherClipView_timer = QtCore.QTimer() 114 | fc.SketcherClipView_timer.timeout.connect(check_leave_edit) 115 | fc.SketcherClipView_timer.start(100) # Pool every 100 ms. 116 | 117 | if __name__ == '__main__': 118 | main() 119 | -------------------------------------------------------------------------------- /Sketcher/SketcherFixAllPoints.FCMacro: -------------------------------------------------------------------------------- 1 | __Name__ = 'SketcherFixAllPoints' 2 | __Author__ = 'edi' 3 | __Version__ = '0.0.3' 4 | __Date__ = '2021-11-20' 5 | __License__ = 'LGPL v2' 6 | __Web__ = 'https://forum.freecadweb.org/viewtopic.php?f=13&t=52451' 7 | __Icon__ = '' 8 | __Help__ = 'Open a sketch. Start the macro. All geometry is fixed by constraints.' 9 | __Status__ = 'Stable' 10 | __Requires__ = 'e.g. FreeCAD >= v0.19' 11 | __Files__ = 'SketcherFixAllPoints.svg' 12 | """ 13 | This macro is intended to constrain automatically created, incompletely contrained sketches. 14 | The sketches usually have been imported from IGES, STEP or DXF files. 15 | 16 | All existing external constraints will be deleted. 17 | All points defining the sketch obtain a 'DistanceX' and 'DistanceY' constraint. 18 | Points positioned on two elements are connected using a 'Coincident' constraint. 19 | Circles obtain a 'Diameter' constraint. 20 | 21 | Workflow: 22 | - Open a sketch or select a sketch in the model tree 23 | - Start the macro 24 | 25 | Hint: The created sketch may be over constrained. In this case drag an element. 26 | The redundant constraints will be deleted automatically. 27 | 28 | """ 29 | 30 | from typing import List 31 | 32 | import FreeCAD as app 33 | 34 | import FreeCADGui as gui 35 | 36 | g_all_points: List['Point'] = [] 37 | 38 | 39 | class Point: 40 | """Defining a Point object, containing its position and element id's.""" 41 | def __init__(self, pos, geo_id, pt_id): 42 | self.pos = pos 43 | self.ident = [] 44 | self.extend(geo_id, pt_id) 45 | 46 | def extend(self, geo_id, pt_id): 47 | self.ident.append([geo_id, pt_id]) 48 | 49 | 50 | def addPoint(pos, geo_id, pt_id): 51 | """Add a Point to the g_all_points list.""" 52 | pt = Point(pos, geo_id, pt_id) 53 | # Extend an existing point if at the same position. 54 | for existing_pt in g_all_points: 55 | if pt.pos.isEqual(existing_pt.pos, 0.01): 56 | existing_pt.extend(geo_id, pt_id) 57 | return 58 | # No point at the same position, add a point. 59 | g_all_points.append(pt) 60 | 61 | 62 | def get_sketch(): 63 | """Get the opened or selected sketch.""" 64 | active_workbench = gui.activeWorkbench() 65 | if active_workbench.name() == 'SketcherWorkbench': 66 | # ActiveSketch is defined by the Sketcher Workbench. 67 | return ActiveSketch 68 | else: 69 | sel = gui.Selection.getSelection() 70 | if sel and sel[0].TypeId == 'Sketcher::SketchObject': 71 | return sel[0] 72 | 73 | 74 | def delete_constraints(sketch): 75 | """Delete all external constraints.""" 76 | internals = [] 77 | for i in range(sketch.ConstraintCount): 78 | if sketch.Constraints[0].Type == 'InternalAlignment': 79 | internals.append(sketch.Constraints[0]) 80 | sketch.delConstraint(0) 81 | sketch.addConstraint(internals) 82 | 83 | 84 | def get_elements(sketch): 85 | """Add points of next geometry to list of points.""" 86 | for i, geo in enumerate(sketch.Geometry): 87 | if geo.TypeId == 'Part::GeomLineSegment': 88 | addPoint(geo.StartPoint, i, 1) 89 | addPoint(geo.EndPoint, i, 2) 90 | elif geo.TypeId == 'Part::GeomArcOfCircle': 91 | addPoint(geo.StartPoint, i, 1) 92 | addPoint(geo.EndPoint, i, 2) 93 | addPoint(geo.Center, i, 3) 94 | elif geo.TypeId == 'Part::GeomCircle': 95 | addPoint(geo.Center, i, 3) 96 | 97 | 98 | def make_constraints(sketch): 99 | """Create the new constraints.""" 100 | for pt in g_all_points: 101 | sketch.addConstraint(Sketcher.Constraint('DistanceX', 102 | pt.ident[0][0], pt.ident[0][1], pt.pos.x)) 103 | sketch.addConstraint(Sketcher.Constraint('DistanceY', 104 | pt.ident[0][0], pt.ident[0][1], pt.pos.y)) 105 | if len(pt.ident) > 1: 106 | sketch.addConstraint(Sketcher.Constraint('Coincident', 107 | pt.ident[0][0], pt.ident[0][1], pt.ident[1][0], pt.ident[1][1])) 108 | 109 | 110 | def make_diameters(sketch): 111 | """Diameter constraints for all circles.""" 112 | for i, geo in enumerate(sketch.Geometry): 113 | if geo.TypeId == 'Part::GeomCircle': 114 | sketch.addConstraint(Sketcher.Constraint('Diameter', i, geo.Radius * 2)) 115 | 116 | 117 | sketch = get_sketch() 118 | if sketch is not None: 119 | delete_constraints(sketch) 120 | get_elements(sketch) 121 | make_constraints(sketch) 122 | make_diameters(sketch) 123 | app.activeDocument().recompute() 124 | else: 125 | app.Console.PrintWarning('No sketch selected, doing nothing') 126 | -------------------------------------------------------------------------------- /SolidSweep.FCMacro: -------------------------------------------------------------------------------- 1 | import Part, FreeCAD, math, PartGui, FreeCADGui 2 | from FreeCAD import Base 3 | 4 | # get the selected objects, with first selection for the trajectory and second for the section 5 | s = FreeCADGui.Selection.getSelection() 6 | try: 7 | shape1=s[0].Shape 8 | shape2=s[1].Shape 9 | except: 10 | print "Wrong selection" 11 | 12 | traj = Part.Wire([shape1]) 13 | section = Part.Wire([shape2]) 14 | 15 | # create Part object in the current document 16 | myObject=App.ActiveDocument.addObject("Part::Feature","Sweep") 17 | 18 | # variable makeSolid = 1 to create solid, 0 to create surfaces 19 | makeSolid = True #1 20 | isFrenet = True #1 21 | 22 | # create a 3D shape and assigh it to the current document 23 | Sweep = Part.Wire(traj).makePipeShell([section],makeSolid,isFrenet) 24 | myObject.Shape = Sweep 25 | -------------------------------------------------------------------------------- /Spreadsheet/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FreeCAD/FreeCAD-macros/d643f70db1fa5a6c582473694da0a5bc8ff3bc37/Spreadsheet/.gitkeep -------------------------------------------------------------------------------- /TechDraw/LasercutterSVGExport.FCMacro: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | __Name__ = 'LasercutterSVGExport' 4 | __Comment__ = 'Creates contour lines for all selected objects and arranges them on a TechDraw page' 5 | __Author__ = 'christi' 6 | __Version__ = '0.7.0' 7 | __Date__ = '2022-03-03' 8 | __License__ = 'LGPL-3.0-or-later' 9 | __Web__ = 'https://github.com/FreeCAD/FreeCAD-macros/tree/master/TechDraw' 10 | __Wiki__ = 'https://github.com/FreeCAD/FreeCAD-macros/tree/master/TechDraw/LasercutterSVGExport' 11 | __Icon__ = 'LasercutterSVGExport.svg' 12 | __Help__ = 'LasercutterSVGExport/README.md' 13 | __Status__ = 'Alpha' 14 | __Requires__ = 'FreeCAD >= v0.18' 15 | __Communication__ = 'https://forum.freecadweb.org/viewtopic.php?f=15&t=31796' 16 | __Files__ = 'LasercutterSVGExport/LasercutterTechdrawExport.py,LasercutterSVGExport/README.md,LasercutterSVGExport/LasercutterSVGExport_screenshot.png,LasercutterSVGExport/lasercuttersvg.ui,LasercutterSVGExport.svg' 17 | 18 | # compatible to Python 3 19 | 20 | import FreeCAD as app 21 | import FreeCADGui as gui 22 | from LasercutterSVGExport import LasercutterTechdrawExport 23 | 24 | iconPath = os.path.dirname(__file__) 25 | 26 | 27 | class LasercutterSVGExport(): 28 | def Activated(self): 29 | '''Will be called when the feature is executed.''' 30 | self.ui_file = os.path.join(iconPath, 'LasercutterSVGExport', 'lasercuttersvg.ui') 31 | self.form = gui.PySideUic.loadUi(self.ui_file) 32 | self.form.pushButton.pressed.connect(self.makeExport) 33 | self.form.show() 34 | 35 | def IsActive(self): 36 | '''Here you can define if the command must be active or not (greyed) if certain conditions 37 | are met or not. This function is optional.''' 38 | if app.activeDocument(): 39 | return(True) 40 | else: 41 | return(False) 42 | 43 | def makeExport(self): 44 | # Generate commands in the FreeCAD python console to create LasercutterTechdrawExport 45 | gui.doCommand('from LasercutterSVGExport import LasercutterTechdrawExport') 46 | 47 | gui.doCommand('lcparts = []') 48 | selection = gui.Selection.getSelectionEx() 49 | for sel in selection: 50 | gui.doCommand("lcparts.append(FreeCAD.ActiveDocument.getObject('%s'))"%(sel.ObjectName)) 51 | 52 | gui.doCommand(f'LasercutterTechdrawExport.makeLasercutterTechdrawExport(lcparts, BeamWidth={self.form.doubleSpinBox.value()}, doc=FreeCAD.activeDocument())') 53 | self.form.close() 54 | 55 | 56 | if __name__ == '__main__': 57 | lc = LasercutterSVGExport() 58 | lc.Activated() 59 | -------------------------------------------------------------------------------- /TechDraw/LasercutterSVGExport/LasercutterSVGExport_screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FreeCAD/FreeCAD-macros/d643f70db1fa5a6c582473694da0a5bc8ff3bc37/TechDraw/LasercutterSVGExport/LasercutterSVGExport_screenshot.png -------------------------------------------------------------------------------- /TechDraw/LasercutterSVGExport/README.md: -------------------------------------------------------------------------------- 1 | #![alt text](LasercutterTechdrawExport.svg "Logo") FreeCAD Lasercutter SVG Export Macro 2 | 3 | We have a lasercutter that uses .svg files as input. 4 | I would like to generate .svg files from my FreeCAD designs. 5 | The laser beam width has to be considered, but I do not want to add it in my design. 6 | 7 | I have created a script that is doing this task: 8 | 9 | * Select several parts in the FreeCAD design 10 | * Create 3D outline objects from all selected items 11 | * Rotate them into the XY-plane 12 | * Create views in a TechDraw page 13 | * Arrange the views to fit in the page with minimal gaps 14 | 15 | ![alt text](LasercutterSVGExport_screenshot.png "Screenshot") 16 | 17 | ## Installation 18 | In FreeCAD, select the Addon manager from tools menu. Go to the Macros tab and find LasercutterSVGExport in the list. Click Install. 19 | 20 | or 21 | Copy LasercutterSVGExport.FCMacro and the LasercutterSVGExport folder to your FreeCAD Macro directory (on Linux: ~/.FreeCAD/Macro) 22 | In menu Macro select Macros... 23 | Execute LasercutterSVGExport.FCMacro 24 | 25 | ## Usage 26 | Do not add the laserbeam width into your design. This export tool will add the beam width. 27 | 28 | * Select several parts in the FreeCAD design 29 | * Creates outline objects from all selected items 30 | * Rotate them into the XY-plane 31 | * Create views in a TechDraw page 32 | * Arrange the views to fit in the page with minimal gaps 33 | 34 | The tool creates a folder LaserCutterExportObjects that contains an object for each selected part. 35 | There are some parameters which can be changed: 36 | * Part: Selected part 37 | * Beam Width: The width of the laser beam in mm 38 | * Normal: A vector perpendicular to the object 39 | * Method: How to create the outline 40 | *auto*: find the best method automatically 41 | *2D*: works for 2D objects 42 | *3D*: create a 3D outline and then get the biggest face 43 | *face*: find the biggest face and create a 2D offset 44 | *normal*: manually define parameter Normal and use it as a perpendicular vector to the object 45 | 46 | #### Troubleshooting 47 | Find your part in the folder LaserCutterExportObjects and play with the parameters. 48 | 49 | *Got the wrong side of your part:* 50 | Set method to normal and change the parameter Normal to be perpendicular to the wanted side 51 | 52 | *Missing lines or no view at all in Techdraw:* 53 | Change the parameter method. Try out different settings. 54 | 55 | ## Discussion 56 | [Dedicated FreeCAD forum discussion thread](https://forum.freecadweb.org/viewtopic.php?f=35&t=31869) 57 | 58 | ## License 59 | GNU Lesser General Public License v3.0 60 | 61 | 62 | -------------------------------------------------------------------------------- /TechDraw/LasercutterSVGExport/lasercuttersvg.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 246 10 | 178 11 | 12 | 13 | 14 | Create Lasercutter SVG Export Template 15 | 16 | 17 | 18 | 19 | 20 | Laser beam width [mm]: 21 | 22 | 23 | 24 | 25 | 26 | 27 | 0.200000000000000 28 | 29 | 30 | 31 | 32 | 33 | 34 | Create Lasercutter Template 35 | 36 | 37 | 38 | 39 | 40 | 41 | <html><head/><body><p align="center">Select one or more parts in your design.<br/>Then press the button.</p></body></html> 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /TechDraw/TechDrawAnnoTextFromv018.FCMacro: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # *************************************************************************** 5 | # * * 6 | # * Copyright (c) 2022 - Wanderer Fan * 7 | # * * 8 | # * This program is free software; you can redistribute it and/or modify * 9 | # * it under the terms of the GNU Lesser General Public License (LGPL) * 10 | # * as published by the Free Software Foundation; either version 2 of * 11 | # * the License, or (at your option) any later version. * 12 | # * for detail see the LICENCE text file. * 13 | # * * 14 | # * This program is distributed in the hope that it will be useful, * 15 | # * but WITHOUT ANY WARRANTY; without even the implied warranty of * 16 | # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 17 | # * GNU Library General Public License for more details. * 18 | # * * 19 | # * You should have received a copy of the GNU Library General Public * 20 | # * License along with this program; if not, write to the Free Software * 21 | # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * 22 | # * USA * 23 | # * * 24 | # *************************************************************************** 25 | """Migrates Annotation Text made in FreeCAD v0.18 to v0.19.""" 26 | 27 | __Name__ = 'TechDrawAnnoTextFromv018' 28 | __Comment__ = 'Convert v018 Annotation Text size to v019' 29 | __Author__ = 'WandererFan' 30 | __Version__ = '0.1.0' 31 | __License__ = 'CC-BY-3.0' 32 | __Web__ = 'http://www.freecadweb.org/' 33 | __Wiki__ = 'http://www.freecadweb.org/wiki/Macro_TechDrawAnnoTextFromv018' 34 | __Icon__ = '' 35 | __Help__ = 'Open a v018 file in v019, execute' 36 | __Status__ = 'Alpha' 37 | __Requires__ = '' 38 | __Communication__ = 'https://github.com/FreeCAD/FreeCAD-macros/issues/' 39 | __Files__ = '' 40 | 41 | # font sizes were once set using QFont.setPointSize but are now set using QFont.setPixelSize. 42 | # text set the old way will be bigger than text set the new way since points are bigger than 43 | # pixels. This macro adjusts the text size in old files to give approximately the same size 44 | # result. 45 | 46 | import FreeCAD as App 47 | import TechDraw 48 | 49 | def TechDrawAnnoTextFromv018(): 50 | factor = 96.0/72.0 # pixels/inch vs points/inch 51 | for obj in App.ActiveDocument.Objects: 52 | if obj.isDerivedFrom("TechDraw::DrawViewAnnotation"): 53 | oldSize = obj.TextSize 54 | obj.TextSize = oldSize * factor 55 | if obj.isDerivedFrom("TechDraw::DrawViewDimension"): 56 | oldSize = obj.ViewObject.Fontsize 57 | obj.ViewObject.Fontsize = oldSize * factor 58 | if obj.isDerivedFrom("TechDraw::DrawViewBalloon"): 59 | oldSize = obj.ViewObject.Fontsize 60 | obj.ViewObject.Fontsize = oldSize * factor 61 | 62 | 63 | if __name__ == '__main__': 64 | TechDrawAnnoTextFromv018() 65 | App.ActiveDocument.recompute() 66 | 67 | -------------------------------------------------------------------------------- /TechDraw/TechDrawMigrateFromv018.FCMacro: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # *************************************************************************** 5 | # * * 6 | # * Copyright (c) 2022 - Wanderer Fan * 7 | # * * 8 | # * This program is free software; you can redistribute it and/or modify * 9 | # * it under the terms of the GNU Lesser General Public License (LGPL) * 10 | # * as published by the Free Software Foundation; either version 2 of * 11 | # * the License, or (at your option) any later version. * 12 | # * for detail see the LICENCE text file. * 13 | # * * 14 | # * This program is distributed in the hope that it will be useful, * 15 | # * but WITHOUT ANY WARRANTY; without even the implied warranty of * 16 | # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 17 | # * GNU Library General Public License for more details. * 18 | # * * 19 | # * You should have received a copy of the GNU Library General Public * 20 | # * License along with this program; if not, write to the Free Software * 21 | # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * 22 | # * USA * 23 | # * * 24 | # *************************************************************************** 25 | """Migrates drawings made in FreeCAD v0.18 to v0.19.""" 26 | 27 | __Name__ = 'TechDrawMigrateFromv018' 28 | __Comment__ = 'Convert v018 Rotation property to v019' 29 | __Author__ = 'WandererFan' 30 | __Version__ = '0.1.0' 31 | __License__ = 'CC-BY-3.0' 32 | __Web__ = 'http://www.freecadweb.org/' 33 | __Wiki__ = 'http://www.freecadweb.org/wiki/Macro_TechDrawViewSet' 34 | __Icon__ = '' 35 | __Help__ = 'Open a v018 file in v019, execute' 36 | __Status__ = 'Alpha' 37 | __Requires__ = '' 38 | __Communication__ = 'https://github.com/FreeCAD/FreeCAD-macros/issues/' 39 | __Files__ = '' 40 | 41 | 42 | # convert v0.18 Rotation values to v0.19 43 | 44 | import FreeCAD as App 45 | import TechDraw 46 | 47 | def TechDrawMigrateFromv018(): 48 | increment = App.Units.Quantity(270, App.Units.Unit("deg")) 49 | for obj in App.ActiveDocument.Objects: 50 | if obj.isDerivedFrom("TechDraw::DrawViewSection"): 51 | if obj.Rotation != 0.0: 52 | obj.Rotation = obj.Rotation + increment 53 | obj.touch() 54 | for inObj in obj.InList: 55 | if obj.isDerivedFrom("TechDraw::DrawViewDimension"): 56 | inObj.touch() 57 | elif obj.isDerivedFrom("TechDraw::DrawViewPart"): 58 | if obj.Rotation != 0.0: 59 | obj.Rotation = -obj.Rotation 60 | obj.touch() 61 | 62 | 63 | if __name__ == '__main__': 64 | TechDrawMigrateFromv018() 65 | App.ActiveDocument.recompute() 66 | 67 | -------------------------------------------------------------------------------- /TechDraw/TechDrawViewSet.FCMacro: -------------------------------------------------------------------------------- 1 | # TechDrawViewSet 2 | # 3 | # This allows you to take a view of your part from the the 3D viewer 4 | # and insert it into a TechDraw document. It's useful for when you 5 | # need a tech drawing of your part from a weird angle. 6 | # 7 | # 1. Create a TechDrawing and insert a view of your part. 8 | # 2. Open your part up in the 3D viewer 9 | # 3. Orient it however you want it 10 | # 4. While keeping the 3D view active/visible, select the TechDraw 11 | # Object/View from step 1. 12 | # 5. Run the macro. This will automatically adjust the direction of 13 | # techDraw object to match the 3D view. 14 | 15 | __Name__ = 'TechDraw View Set' 16 | __Comment__ = 'Set the parameters of the current view to correspond to the 3D Window' 17 | __Author__ = 'MechaMecca, galou_breizh' 18 | __Version__ = '0.1.0' 19 | __License__ = 'CC-BY-3.0' 20 | __Web__ = 'http://www.freecadweb.org/wiki/Macro_TechDrawViewSet' 21 | __Wiki__ = 'http://www.freecadweb.org/wiki/Macro_TechDrawViewSet' 22 | __Icon__ = '' 23 | __Help__ = 'Select a TechDraw View while staying in the 3D viewer, execute' 24 | __Status__ = 'Alpha' 25 | __Requires__ = '' 26 | __Communication__ = 'https://github.com/FreeCAD/FreeCAD-macros/issues/' 27 | __Files__ = '' 28 | 29 | import freecad as fc 30 | 31 | 32 | def set_view(): 33 | gui_doc = fc.gui.activeDocument() 34 | if gui_doc is None: 35 | fc.app.Console.PrintWarning('No active document\n') 36 | return 37 | 38 | try: 39 | cam_orientation = gui_doc.ActiveView.getCameraOrientation() 40 | except AttributeError: 41 | fc.app.Console.PrintWarning('The current tab is not a 3D view\n') 42 | return 43 | 44 | if not fc.gui.Selection.getSelection(): 45 | fc.app.Console.PrintWarning('Nothing selected\n') 46 | return 47 | 48 | view_dir = cam_orientation.multVec(fc.app.Vector(0, 0, 1)) 49 | for tech_view in fc.gui.Selection.getSelection(): 50 | try: 51 | tech_view.Direction = view_dir 52 | except AttributeError: 53 | fc.app.Console.PrintMessage('"{}" is not a TechDraw View, ignoring\n'.format(tech_view.Label)) 54 | 55 | if __name__ == '__main__': 56 | set_view() 57 | -------------------------------------------------------------------------------- /Utility/GroupSorting.FCMacro: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # (c) 18Turbo, 2022 3 | 4 | __Name__ = 'GroupSorting (Ordenación)' 5 | __Comment__ = 'The macro sorts the groups and pieces of a piece.\nSelect groups with key, and execute the macro.' 6 | __Author__ = '18Turbo' 7 | __Version__ = '0.1.0' 8 | __Date__ = '2022-10-04' 9 | __License__ = 'LGPL-2.0-or-later' 10 | __Web__ = 'https://github.com/18turbo/OrdenacionRapidaFreeCAD' 11 | __Wiki__ = 'https://github.com/18turbo/OrdenacionRapidaFreeCAD' 12 | __Icon__ = 'GroupSorting.svg' 13 | __Help__ = '' 14 | __Status__ = 'beta' 15 | __Requires__ = 'FreeCAD >= v0.20' 16 | __Contact__ = 'https://github.com/18turbo' 17 | __Communication__ = '' 18 | __Files__ = 'GroupSorting.svg' 19 | 20 | 21 | # (Spanish:) 22 | # 23 | # Autor: 18Turbo 24 | # Fecha Incial: 22-07-2022 25 | # Versión: 0.1.0 BETA 26 | # 27 | # Funcionalidad: Ordena grupos según sean seleccionados. 28 | # 29 | # Notas Importantes: hay que saber que los grupos sin padre tienen un orden específico en FreeCAD y no se pueden ordenar con este script 30 | # porque aunque se hace el proceso correcto, FreeCAD no permite la ordenación de estos de una forma simple y escapa 31 | # a los propósitos de este script. 32 | # 33 | # 34 | # Notas de versiones: 35 | # v.0.1.0: Adaptación al Addon Manager 36 | # v.0.0.5: Depurado para que funcione con grupos sin padre, es decir, con el documento como padre, aunque FreeCAD no lo permita. Así, 37 | # si en una versión superior se permite, el script no hay que modificarlo. 38 | # v.0.0.4: Versión Alfa funcional. 39 | # 40 | # 41 | # 42 | 43 | import FreeCAD as app 44 | import FreeCADGui as gui 45 | 46 | cIntro = '\n' 47 | 48 | strLang = [ 49 | [ # Español 50 | ## Strings for translation into any language 51 | "Los grupos no tienen un padre y FreeCAD no permite la ordenación en este nivel", 52 | "Debes seleccionar Grupos con el mismo padre", 53 | "Debes elegir más de un objeto" 54 | 55 | ], 56 | [ # English 57 | ## Strings for translation into any language (English Strings) 58 | "Groups do not have a parent and FreeCAD does not allow sorting at this level", 59 | "You must select Groups with the same parent", 60 | "You must choose more than one object" 61 | 62 | ] 63 | ] 64 | 65 | def tr(text): 66 | # Translate 67 | if (text not in strLang[0]) or (g_num_lang < 0) or (g_num_lang >= len(strLang)): 68 | return text 69 | primerIdioma = strLang[0] 70 | indice = primerIdioma.index(text) 71 | if (indice == -1) or (indice >= len(strLang[g_num_lang])): 72 | return text 73 | return strLang[g_num_lang][indice] 74 | 75 | 76 | lang = gui.getLocale() 77 | # Default to English. 78 | g_num_lang = 1 79 | if 'Spanish' in lang: 80 | g_num_lang = 0 81 | #elif 'Italian' in lang: 82 | # g_num_lang = 2 83 | 84 | objetosSeleccionados = Gui.Selection.getSelection() 85 | 86 | if len(objetosSeleccionados) > 1: 87 | # Comprobando que todos tienen el mismo padre, siempre que no partan de la raíz del documento 88 | distintoPadre = False 89 | padre = "" 90 | if app.ActiveDocument.getObject(objetosSeleccionados[0].Name).InList: # Esto es que tiene padre 91 | tienePadre = True 92 | for objeto in objetosSeleccionados: 93 | if (app.ActiveDocument.getObject(objeto.Name).InList[0].Name != padre): 94 | if (padre == ""): 95 | padre = app.ActiveDocument.getObject(objeto.Name).InList[0].Name 96 | else: 97 | distintoPadre = True 98 | break; 99 | else: 100 | tienePadre = False 101 | for objeto in objetosSeleccionados: 102 | if (app.ActiveDocument.getObject(objeto.Name).InList): 103 | distintoPadre = True 104 | app.Console.PrintWarning(tr("Los grupos no tienen un padre y FreeCAD no permite la ordenación en este nivel") + cIntro) 105 | 106 | if not distintoPadre: 107 | if tienePadre: 108 | nombrePadre = app.ActiveDocument.getObject(objeto.Name).InList[0].Name 109 | 110 | listaNombres = [] 111 | app.activeDocument().Tip = app.activeDocument().addObject('App::DocumentObjectGroup','GrupoAuxiliarOrdenacion') 112 | 113 | for objetoA in objetosSeleccionados: 114 | # Siempre que el objeto sea un grupo 115 | if (objetoA.Name.find('Group') > -1) or (objetoA.Name.find('Part') > -1): 116 | app.ActiveDocument.getObject(objetoA.Name).removeObject(app.ActiveDocument.getObject(objetoA.Name)) 117 | app.ActiveDocument.getObject("GroupAuxSorting18T").addObject(app.ActiveDocument.getObject(objetoA.Name)) 118 | listaNombres.append(objetoA.Name) 119 | 120 | for nombreObjeto in listaNombres: 121 | app.ActiveDocument.getObject("GroupAuxSorting18T").removeObject(app.ActiveDocument.getObject(nombreObjeto)) 122 | if tienePadre: 123 | app.ActiveDocument.getObject(nombrePadre).addObject(app.ActiveDocument.getObject(nombreObjeto)) 124 | 125 | app.activeDocument().removeObject("GroupAuxSorting18T") 126 | app.activeDocument().recompute() 127 | else: 128 | app.Console.PrintWarning(tr("Debes seleccionar Grupos con el mismo padre") + cIntro) 129 | else: 130 | app.Console.PrintWarning(tr("Debes elegir más de un objeto") + cIntro) -------------------------------------------------------------------------------- /Utility/HighlightCommon.FCMacro: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from __future__ import unicode_literals 4 | 5 | __Name__ = 'Highlight Common parts' 6 | __Comment__ = 'Compute the common parts between selected shapes' 7 | __Author__ = 'JMG, galou and other contributors' 8 | __Version__ = '2.3.2' 9 | __Date__ = '2021-07-01' 10 | __License__ = 'CC0-1.0' 11 | __Web__ = 'https://freecadweb.org/wiki/Macro_HighlightCommon' 12 | __Wiki__ = 'https://freecadweb.org/wiki/Macro_HighlightCommon' 13 | __Icon__ = 'HighlightCommon.png' 14 | __Help__ = 'Select at least two objects and run' 15 | __Status__ = 'Production' 16 | __Requires__ = 'FreeCAD V0.17+' 17 | __Communication__ = 'https://github.com/FreeCAD/FreeCAD-macros/issues/' 18 | __Files__ = 'HighlightCommon.png' 19 | 20 | from PySide.QtGui import QMessageBox # FreeCAD's special PySide! 21 | 22 | import FreeCAD as app 23 | import FreeCADGui as gui 24 | 25 | 26 | PREFERENCE_PATH = 'User parameter:BaseApp/Preferences/Mod/HighlightCommon' 27 | 28 | 29 | def load_and_save_settings(): 30 | param_change = 'change_transparency' 31 | param_transparency = 'transparency' 32 | p = app.ParamGet(PREFERENCE_PATH) 33 | change_transparency = p.GetBool(param_change, True) 34 | transparency = p.GetInt(param_transparency, 80) 35 | p.SetBool(param_change, change_transparency) 36 | p.SetInt(param_transparency, transparency) 37 | return change_transparency, transparency 38 | 39 | 40 | def main(): 41 | # Store active doc in case it may change during run. 42 | doc = app.activeDocument() 43 | 44 | if doc is None: 45 | return 46 | 47 | change_transparency, transparency = load_and_save_settings() 48 | 49 | colli_grp = None # Group object to group collisions. 50 | colli_max = 0 # Maximum collision volume. 51 | 52 | # Open a transaction in undo pile. 53 | doc.openTransaction('Seeking collisions') 54 | 55 | objectsToEnumerate = gui.Selection.getSelection() 56 | if len(objectsToEnumerate) < 2: 57 | objectsToEnumerate = doc.Objects 58 | 59 | # Ensure list of unique objects. 60 | object_list = [] 61 | for obj in objectsToEnumerate: 62 | if ((obj not in object_list) 63 | and hasattr(obj, 'Shape') 64 | and hasattr(obj, 'getGlobalPlacement') 65 | and hasattr(obj, 'Label')): 66 | object_list.append(obj) 67 | 68 | # Going through selected objects (object A). 69 | for i, object_a in enumerate(object_list): 70 | shape_a = object_a.Shape.copy() 71 | shape_a.Placement = object_a.getGlobalPlacement() 72 | label_a = object_a.Label 73 | 74 | # Making selected objects transparent. 75 | if change_transparency: 76 | try: 77 | object_a.ViewObject.Transparency = transparency 78 | except AttributeError: 79 | pass 80 | 81 | # Comparing object A with all 82 | # following ones in the list (object B). 83 | for object_b in object_list[(i + 1):]: 84 | 85 | shape_b = object_b.Shape.copy() 86 | shape_b.Placement = object_b.getGlobalPlacement() 87 | label_b = object_b.Label 88 | common = shape_a.common(shape_b) 89 | 90 | # Making selected objects transparent. 91 | if change_transparency: 92 | try: 93 | object_b.ViewObject.Transparency = transparency 94 | except AttributeError: 95 | pass 96 | 97 | # If object A & object B have a collision 98 | # display a message with collision volume and 99 | # add a new representative shape in the group. 100 | if common.Volume > 1e-6: 101 | app.Console.PrintMessage( 102 | 'Volume of the intersection between {} and {}: {:.3f} mm³\n'.format( 103 | label_a, 104 | label_b, 105 | common.Volume)) 106 | colli_max = common.Volume if common.Volume > colli_max else colli_max 107 | if not colli_grp: 108 | # Create group if it doesn't already exist. 109 | colli_grp = doc.addObject('App::DocumentObjectGroup', 110 | 'Collisions') 111 | intersection_object = doc.addObject( 112 | 'Part::Feature') 113 | intersection_object.Label = '{} - {}'.format( 114 | label_a, label_b) 115 | intersection_object.Shape = common 116 | intersection_object.ViewObject.ShapeColor = (1.0, 0.0, 0.0, 1.0) 117 | colli_grp.addObject(intersection_object) 118 | else: 119 | # If no collision, just inform the user. 120 | app.Console.PrintMessage( 121 | 'No intersection between {} and {}\n'.format( 122 | label_a, 123 | label_b)) 124 | 125 | # If collisions have been found, commit the undo transaction and 126 | # give a summary (count + max value) to the user. 127 | if colli_grp: 128 | doc.commitTransaction() 129 | app.Console.PrintMessage( 130 | '{} collision(s) found between selected objects\nMaximum collision: {:.3f} mm³\n'.format( 131 | len(colli_grp.Group), 132 | colli_max)) 133 | doc.recompute() 134 | else: 135 | # If no collision has been found, just inform the user about it. 136 | doc.abortTransaction() 137 | if len(object_list) >= 2: 138 | app.Console.PrintWarning('No collision found between selected objects\n') 139 | else: 140 | app.Console.PrintWarning('No suitable objects selected, select at least two objects\n') 141 | 142 | 143 | if __name__ == '__main__': 144 | main() 145 | -------------------------------------------------------------------------------- /Utility/HighlightCommon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FreeCAD/FreeCAD-macros/d643f70db1fa5a6c582473694da0a5bc8ff3bc37/Utility/HighlightCommon.png -------------------------------------------------------------------------------- /Utility/HighlightDifference.FCMacro: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Compute the difference between two shapes. Additions are marked red, removals 4 | are marked green. Both original parts will be half transparent. The volume of 5 | the additions and removals are printed in the console. 6 | """ 7 | 8 | from __future__ import unicode_literals 9 | 10 | __Name__ = 'Highlight Difference' 11 | __Comment__ = 'Compute the difference between two shapes' 12 | __Author__ = 'galou and other contributors' 13 | __Version__ = '2.2.0' 14 | __Date__ = '2021-07-01' 15 | __License__ = 'CC0-1.0' 16 | __Web__ = 'https://freecadweb.org/wiki/Macro_HighlightDifference' 17 | __Wiki__ = 'https://freecadweb.org/wiki/Macro_HighlightDifference' 18 | __Icon__ = 'HighlightDifference.svg' 19 | __Help__ = 'Select two objects and run' 20 | __Status__ = 'Production' 21 | __Requires__ = 'FreeCAD V0.17+' 22 | __Communication__ = 'https://github.com/FreeCAD/FreeCAD-macros/issues/' 23 | __Files__ = 'HighlightDifference.svg' 24 | 25 | from PySide import QtCore # FreeCAD's special PySide! 26 | from PySide.QtGui import QMessageBox # FreeCAD's special PySide! 27 | 28 | import FreeCAD as app 29 | import FreeCADGui as gui 30 | 31 | 32 | PREFERENCE_PATH = 'User parameter:BaseApp/Preferences/Mod/HighlightDifference' 33 | 34 | 35 | def error_dialog(msg): 36 | """Create a simple dialog QMessageBox with an error message.""" 37 | app.Console.PrintError(msg + '\n') 38 | diag = QMessageBox(QMessageBox.Icon.Critical, 39 | 'Error in macro highlight_difference', 40 | msg) 41 | diag.setWindowModality(QtCore.Qt.ApplicationModal) 42 | diag.exec_() 43 | 44 | 45 | def load_and_save_settings(): 46 | param_change = 'change_transparency' 47 | param_transparency = 'transparency' 48 | p = app.ParamGet(PREFERENCE_PATH) 49 | change_transparency = p.GetBool(param_change, True) 50 | transparency = p.GetInt(param_transparency, 80) 51 | p.SetBool(param_change, change_transparency) 52 | p.SetInt(param_transparency, transparency) 53 | return change_transparency, transparency 54 | 55 | 56 | def main(): 57 | if len(gui.Selection.getSelection()) < 2: 58 | error_dialog('Select two objects') 59 | return 60 | 61 | object_a = gui.Selection.getSelection()[0] 62 | object_b = gui.Selection.getSelection()[1] 63 | try: 64 | shape_a = object_a.Shape 65 | shape_b = object_b.Shape 66 | label_a = object_a.Label 67 | label_b = object_b.Label 68 | except AttributeError: 69 | error_dialog('No suitable objects selected, select two objects\n') 70 | return 71 | 72 | shape_addition = shape_a.cut(shape_b) 73 | if shape_addition.Volume < 1e-6: 74 | app.Console.PrintMessage('No addition from {} to {}\n'.format( 75 | label_a, label_b)) 76 | else: 77 | app.Console.PrintMessage( 78 | 'Volume of the addition from {} to {}: {}\n'.format( 79 | label_a, label_b, shape_addition.Volume)) 80 | 81 | shape_removal = shape_b.cut(shape_a) 82 | if shape_removal.Volume < 1e-6: 83 | app.Console.PrintMessage('No removal from {} to {}\n'.format( 84 | label_a, label_b)) 85 | else: 86 | app.Console.PrintMessage( 87 | 'Volume of the removal from {} to {}: {}\n'.format( 88 | label_a, label_b, shape_removal.Volume)) 89 | 90 | if (shape_addition.Volume < 1e-6) and (shape_removal.Volume < 1e-6): 91 | app.Console.PrintMessage('{} and {} have the same shape\n'.format( 92 | label_a, label_b)) 93 | 94 | added = app.ActiveDocument.addObject('Part::Feature') 95 | added.Label = 'Addition ({} − {})'.format(label_a, label_b) 96 | added.Shape = shape_addition 97 | added.ViewObject.ShapeColor = (1.0, 0.0, 0.0, 1.0) 98 | removed = app.ActiveDocument.addObject('Part::Feature') 99 | removed.Label = 'Removal ({} − {})'.format(label_b, label_a) 100 | removed.Shape = shape_removal 101 | removed.ViewObject.ShapeColor = (0.0, 0.5, 0.0, 1.0) 102 | 103 | change_transparency, transparency = load_and_save_settings() 104 | if change_transparency: 105 | try: 106 | object_a.ViewObject.Transparency = transparency 107 | object_b.ViewObject.Transparency = transparency 108 | except AttributeError: 109 | pass 110 | 111 | 112 | if __name__ == '__main__': 113 | main() 114 | -------------------------------------------------------------------------------- /Utility/HighlightDifference.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 21 | 23 | 41 | 43 | 44 | 46 | image/svg+xml 47 | 49 | 50 | 51 | 52 | 53 | 58 | + 69 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /Utility/MessageBox.FCMacro: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | __Name__ = 'Message Box' 5 | __Comment__ = 'Show how to give information to the user in macros' 6 | __Web__ = 'http://freecadweb.org/wiki/Macro_MessageBox' 7 | __Wiki__ = 'http://freecadweb.org/wiki/Macro_MessageBox' 8 | __Icon__ = "" 9 | __Help__ = 'Run and be warned' 10 | __Author__ = 'galou, Mario52 and other contributors' 11 | __Version__ = 1.0 12 | __Status__ = 'Production' 13 | __Requires__ = '' 14 | 15 | from PySide import QtCore, QtGui 16 | 17 | 18 | def errorDialog(msg): 19 | """Create a simple dialog QMessageBox""" 20 | # The first argument indicates the icon used: one of 21 | # QtGui.QMessageBox.{NoIcon, Information, Warning, Critical, Question} 22 | diag = QtGui.QMessageBox(QtGui.QMessageBox.Warning, 23 | u'Warning in macro MessageBox', msg) 24 | diag.setWindowModality(QtCore.Qt.ApplicationModal) 25 | diag.exec_() 26 | 27 | # To display multiple lines in a dialog box Qt, '\n' can be added between 28 | # lines. '\t' inserts a tab. The backslash is a then a special character which 29 | # must be escaped, i.e. insert '\\' to display a backslash. Single quotes must 30 | # also be escaped in a single-quote-delimited string and double-quotes in a 31 | # double-quote-delimited string. See Python doc for details. 32 | msg = 'Example of warning message\nYou should\'t have done this.' 33 | errorDialog(msg) 34 | raise(Exception(msg)) 35 | -------------------------------------------------------------------------------- /Utility/PiecesTemplates/Groups.txt: -------------------------------------------------------------------------------- 1 | Images 2 | References 3 | Sketches 4 | Curves 5 | Solids 6 | -------------------------------------------------------------------------------- /Utility/PlacementAbsolufy.FCMacro: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | ##################################### 3 | # Copyright (c) openBrain 2019 4 | # Licensed under LGPL v2 5 | # 6 | # This macro will reset position of all part containers to document origin while keeping the absolute object positions 7 | # 8 | # Version history : 9 | # *0.2 : some typo improvement + commenting for official PR 10 | # *0.1 : alpha release, almost no test performed 11 | # 12 | ##################################### 13 | 14 | __Name__ = 'PlacementAbsolufy' 15 | __Comment__ = 'Reset part containers to global origin while keeping object positions' 16 | __Author__ = 'openBrain' 17 | __Version__ = '0.2' 18 | __Date__ = '2019-06-10' 19 | __License__ = 'LGPL v2' 20 | __Web__ = 'https://www.freecadweb.org/wiki/Macro_PlacementAbsolufy' 21 | __Wiki__ = 'https://www.freecadweb.org/wiki/Macro_PlacementAbsolufy' 22 | __Icon__ = '' 23 | __Help__ = 'Run the macro with model active in the GUI' 24 | __Status__ = 'Alpha' 25 | __Requires__ = 'FreeCAD >= 0.17' 26 | __Communication__ = 'https://forum.freecadweb.org/viewtopic.php?f=3&t=36869' 27 | __Files__ = '' 28 | 29 | currState = {} #initialize a dictionary to store current object placements 30 | 31 | for obj in App.ActiveDocument.Objects: #going through active document objects 32 | if "Placement" in obj.PropertiesList: #if object has a Placement property 33 | currState[obj] = obj.getGlobalPlacement() #store the object pointer with its global placement 34 | 35 | App.ActiveDocument.openTransaction("Absolufy") #open a transaction for undo management 36 | 37 | for obj, plac in currState.items(): #going through all moveable objects 38 | if obj.isDerivedFrom("App::Part"): #if object is a part container 39 | obj.Placement = App.Placement(App.Vector(0,0,0),App.Rotation(0,0,0)) #reset its placement to global document origin 40 | elif obj.TypeId[:5] == "App::": #if object is another App type (typically an origin axis or plane) 41 | None #do nothing 42 | else: #for all other objects 43 | obj.Placement = plac #replace them at their global (absolute) placement 44 | 45 | App.ActiveDocument.commitTransaction() #commit transaction 46 | -------------------------------------------------------------------------------- /Utility/SelectVisible.FCMacro: -------------------------------------------------------------------------------- 1 | # FreeCAD Macro SelectVisible 2 | 3 | __Name__ = 'Select Visible' 4 | __Comment__ = 'All visible objects in the tree will be selected' 5 | __Web__ = 'http://www.freecadweb.org/wiki/Macro_SelectVisible' 6 | __Wiki__ = 'http://www.freecadweb.org/wiki/Macro_SelectVisible' 7 | __Icon__ = 'SelectVisible.svg' 8 | __Help__ = 'All visible objects in the tree and only these will be selected' 9 | __Author__ = 'galou_breizh' 10 | __Version__ = '1.0' 11 | __Status__ = 'Production' 12 | __Requires__ = '' 13 | 14 | import FreeCAD as App 15 | import FreeCADGui as Gui 16 | 17 | doc = App.activeDocument() 18 | 19 | if not doc: 20 | App.Console.PrintWarning('SelectVisible: no active document') 21 | else: 22 | Gui.Selection.clearSelection() 23 | for o in doc.Objects: 24 | if o.ViewObject.Visibility: 25 | Gui.Selection.addSelection(o) 26 | -------------------------------------------------------------------------------- /Utility/Transparencies.FCMacro: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # (c) 18Turbo, 2022 3 | 4 | __Name__ = 'Transparencies' 5 | __Comment__ = 'The selected object becomes 50% transparent (if they have no transparency), and is detransparency otherwise.' 6 | __Author__ = '18Turbo (Rafael M.) and Rafael García' 7 | __Version__ = '0.0.2' 8 | __Date__ = '2022-10-18' 9 | __License__ = 'LGPL-2.0-or-later' 10 | __Web__ = 'https://github.com/18turbo/TransparenciaObjetosFreeCAD' 11 | __Wiki__ = 'https://github.com/18turbo/TransparenciaObjetosFreeCAD' 12 | __Icon__ = 'Transparencies.svg' 13 | __Help__ = '' 14 | __Status__ = 'beta' 15 | __Requires__ = 'FreeCAD >= v0.19' 16 | __Contact__ = 'https://github.com/18turbo' 17 | __Communication__ = '' 18 | __Files__ = 'Transparencies.svg' 19 | 20 | # Agradecimientos: Rafael García 21 | 22 | #El objeto seleccionado se vuelve un 50% transparente si no tiene transparencia y se le quita la transparencia si la tiene. 23 | 24 | creadaTransparencia = False 25 | 26 | objetosSeleccionados = Gui.Selection.getSelection() 27 | 28 | for objeto in objetosSeleccionados: 29 | transparenciaObjeto = objeto.ViewObject.Transparency 30 | if (transparenciaObjeto >= 0 and transparenciaObjeto<10): # Si no hay transparencia (o muy poca) 31 | objeto.ViewObject.Transparency = 50 32 | creadaTransparencia = True 33 | 34 | if creadaTransparencia == False: 35 | # Si no se ha creado ninguna transparencia, es que se debe poner todo opaco 36 | for objeto in objetosSeleccionados: 37 | objeto.ViewObject.Transparency = 0 -------------------------------------------------------------------------------- /Utility/Transparencies.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Utility/treeHelper/treeHelper.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 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 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /Utility/treeHelper/ui_treewindow.py: -------------------------------------------------------------------------------- 1 | # Form implementation generated from reading ui file 'tree_window_.ui' 2 | # 3 | # Created by: PyQt5 UI code generator 5.15.7 4 | # 5 | # WARNING: Any manual changes made to this file will be lost when pyuic5 is 6 | # run again. Do not edit this file unless you know what you are doing. 7 | 8 | 9 | from PySide2 import QtCore, QtGui, QtWidgets 10 | 11 | 12 | class Ui_DockWidget: 13 | def setupUi(self, DockWidget): 14 | DockWidget.setObjectName("Tree helper") 15 | DockWidget.resize(400, 300) 16 | self.dockWidgetContents = QtWidgets.QWidget() 17 | self.dockWidgetContents.setObjectName("dockWidgetContents") 18 | self.gridLayout = QtWidgets.QGridLayout(self.dockWidgetContents) 19 | self.gridLayout.setObjectName("gridLayout") 20 | self.treeWidget = QtWidgets.QTreeWidget(self.dockWidgetContents) 21 | self.treeWidget.setObjectName("treeWidget") 22 | self.gridLayout.addWidget(self.treeWidget, 0, 0, 1, 1) 23 | DockWidget.setWidget(self.dockWidgetContents) 24 | 25 | self.retranslateUi(DockWidget) 26 | QtCore.QMetaObject.connectSlotsByName(DockWidget) 27 | 28 | def retranslateUi(self, DockWidget): 29 | _translate = QtCore.QCoreApplication.translate 30 | DockWidget.setWindowTitle(_translate("DockWidget", "Tree helper")) 31 | self.treeWidget.headerItem().setText(0, _translate("DockWidget", "Elementos")) 32 | -------------------------------------------------------------------------------- /apothemBasedPrism.py: -------------------------------------------------------------------------------- 1 | # # # # # # # # # # # 2 | # 3 | # Apothem Based Prism 4 | # 5 | # This script will take the input of the distance between flats, (apothem, aka inradius), 6 | # and the number of sidesfor a regular polygon along with a height and produce a 7 | # correctly sized prism derived from the circumradius. 8 | # 9 | # # # # # # # # # # # 10 | 11 | __Name__ = 'Apothem Based Prism' 12 | __Comment__ = '' 13 | __License__ = '' 14 | __Web__ = 'http://www.freecadweb.org/wiki/Macro_Apothem_Based_Prism_GUI' 15 | __Wiki__ = 'http://www.freecadweb.org/wiki/Macro_Apothem_Based_Prism_GUI' 16 | __Icon__ = '' 17 | __Help__ = '' 18 | __Author__ = '' 19 | __Version__ = '' 20 | __Status__ = '' 21 | __Requires__ = '' 22 | __Files__ = '' 23 | 24 | 25 | import FreeCAD, FreeCADGui, Part, PartGui, math 26 | from FreeCAD import Base 27 | from PySide import QtGui, QtCore 28 | from math import cos, radians 29 | App = FreeCAD 30 | Gui = FreeCADGui 31 | 32 | class p(): 33 | 34 | 35 | def priSm(self): 36 | 37 | try: 38 | dbf = float(self.d1.text()) 39 | nos = int(self.d2.text()) 40 | hth = float(self.d3.text()) 41 | aR = dbf / 2 42 | op1 = 180/float(nos) 43 | coS = cos(math.radians(op1)) 44 | cR = aR / coS 45 | prism=App.ActiveDocument.addObject("Part::Prism","Prism") 46 | prism.Polygon=nos 47 | prism.Circumradius=cR 48 | prism.Height=hth 49 | prism.Placement=Base.Placement(Base.Vector(0.00,0.00,0.00),Base.Rotation(0.00,0.00,0.00,1.00)) 50 | prism.Label='Prism' 51 | App.ActiveDocument.recompute() 52 | Gui.SendMsgToActiveView("ViewFit") 53 | except: 54 | FreeCAD.Console.PrintError("Unable to complete task") 55 | 56 | self.close() 57 | 58 | def close(self): 59 | self.dialog.hide() 60 | 61 | 62 | # 63 | # Make dialog box and get input for distance between flats, number of sides, and height 64 | # 65 | 66 | def __init__(self): 67 | self.dialog = None 68 | 69 | self.dialog = QtGui.QDialog() 70 | self.dialog.resize(280,110) 71 | 72 | self.dialog.setWindowTitle("Apothem Based Prism") 73 | la = QtGui.QVBoxLayout(self.dialog) 74 | 75 | iN1 = QtGui.QLabel("Distance Between Flats") 76 | la.addWidget(iN1) 77 | self.d1 = QtGui.QLineEdit() 78 | la.addWidget(self.d1) 79 | 80 | iN2 = QtGui.QLabel("Number Of Sides (Best results - use even numbers)") 81 | la.addWidget(iN2) 82 | self.d2 = QtGui.QLineEdit() 83 | la.addWidget(self.d2) 84 | 85 | iN3 = QtGui.QLabel("Prism Height") 86 | la.addWidget(iN3) 87 | self.d3 = QtGui.QLineEdit() 88 | la.addWidget(self.d3) 89 | 90 | okbox = QtGui.QDialogButtonBox(self.dialog) 91 | okbox.setOrientation(QtCore.Qt.Horizontal) 92 | okbox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) 93 | la.addWidget(okbox) 94 | QtCore.QObject.connect(okbox, QtCore.SIGNAL("accepted()"), self.priSm) 95 | QtCore.QObject.connect(okbox, QtCore.SIGNAL("rejected()"), self.close) 96 | QtCore.QMetaObject.connectSlotsByName(self.dialog) 97 | self.dialog.show() 98 | self.dialog.exec_() 99 | 100 | 101 | p() 102 | -------------------------------------------------------------------------------- /icons/Camera/FCCamera_00.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FreeCAD/FreeCAD-macros/d643f70db1fa5a6c582473694da0a5bc8ff3bc37/icons/Camera/FCCamera_00.png -------------------------------------------------------------------------------- /icons/Camera/FCCamera_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FreeCAD/FreeCAD-macros/d643f70db1fa5a6c582473694da0a5bc8ff3bc37/icons/Camera/FCCamera_01.png -------------------------------------------------------------------------------- /icons/Camera/FCCamera_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FreeCAD/FreeCAD-macros/d643f70db1fa5a6c582473694da0a5bc8ff3bc37/icons/Camera/FCCamera_02.png -------------------------------------------------------------------------------- /icons/Camera/FCCamera_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FreeCAD/FreeCAD-macros/d643f70db1fa5a6c582473694da0a5bc8ff3bc37/icons/Camera/FCCamera_03.png -------------------------------------------------------------------------------- /icons/Camera/FCCamera_04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FreeCAD/FreeCAD-macros/d643f70db1fa5a6c582473694da0a5bc8ff3bc37/icons/Camera/FCCamera_04.png -------------------------------------------------------------------------------- /icons/Camera/FCCamera_05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FreeCAD/FreeCAD-macros/d643f70db1fa5a6c582473694da0a5bc8ff3bc37/icons/Camera/FCCamera_05.png -------------------------------------------------------------------------------- /icons/Camera/FCCamera_06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FreeCAD/FreeCAD-macros/d643f70db1fa5a6c582473694da0a5bc8ff3bc37/icons/Camera/FCCamera_06.png -------------------------------------------------------------------------------- /icons/Camera/FCCamera_07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FreeCAD/FreeCAD-macros/d643f70db1fa5a6c582473694da0a5bc8ff3bc37/icons/Camera/FCCamera_07.png -------------------------------------------------------------------------------- /icons/Camera/FCCamera_08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FreeCAD/FreeCAD-macros/d643f70db1fa5a6c582473694da0a5bc8ff3bc37/icons/Camera/FCCamera_08.png -------------------------------------------------------------------------------- /icons/Camera/FCCamera_Axis_rotation_X.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FreeCAD/FreeCAD-macros/d643f70db1fa5a6c582473694da0a5bc8ff3bc37/icons/Camera/FCCamera_Axis_rotation_X.png -------------------------------------------------------------------------------- /icons/Camera/FCCamera_Axis_rotation_Y.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FreeCAD/FreeCAD-macros/d643f70db1fa5a6c582473694da0a5bc8ff3bc37/icons/Camera/FCCamera_Axis_rotation_Y.png -------------------------------------------------------------------------------- /icons/Camera/FCCamera_Axis_rotation_Z.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FreeCAD/FreeCAD-macros/d643f70db1fa5a6c582473694da0a5bc8ff3bc37/icons/Camera/FCCamera_Axis_rotation_Z.png -------------------------------------------------------------------------------- /icons/HyperbolaIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FreeCAD/FreeCAD-macros/d643f70db1fa5a6c582473694da0a5bc8ff3bc37/icons/HyperbolaIcon.png -------------------------------------------------------------------------------- /icons/MeasureCircle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FreeCAD/FreeCAD-macros/d643f70db1fa5a6c582473694da0a5bc8ff3bc37/icons/MeasureCircle.png -------------------------------------------------------------------------------- /icons/RotateView/out.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FreeCAD/FreeCAD-macros/d643f70db1fa5a6c582473694da0a5bc8ff3bc37/icons/RotateView/out.png -------------------------------------------------------------------------------- /icons/RotateView/right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FreeCAD/FreeCAD-macros/d643f70db1fa5a6c582473694da0a5bc8ff3bc37/icons/RotateView/right.png -------------------------------------------------------------------------------- /icons/RotateView/right_abs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FreeCAD/FreeCAD-macros/d643f70db1fa5a6c582473694da0a5bc8ff3bc37/icons/RotateView/right_abs.png -------------------------------------------------------------------------------- /icons/RotateView/up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FreeCAD/FreeCAD-macros/d643f70db1fa5a6c582473694da0a5bc8ff3bc37/icons/RotateView/up.png -------------------------------------------------------------------------------- /icons/SelectVisible.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FreeCAD/FreeCAD-macros/d643f70db1fa5a6c582473694da0a5bc8ff3bc37/icons/SelectVisible.png -------------------------------------------------------------------------------- /makecamera2dview.py: -------------------------------------------------------------------------------- 1 | #------------------------------------------------- 2 | #-- makecamera2dview 3 | #-- 4 | #-- microelly 2015 5 | #-- 6 | #-- GNU Lesser General Public License (LGPL) 7 | #------------------------------------------------- 8 | # create a 2dview object from the camera position 9 | 10 | __Name__ = 'Make Camera 2D View' 11 | __Comment__ = 'create a 2DView object from the camera position' 12 | __Web__ = "" 13 | __Wiki__ = "" 14 | __Icon__ = "" 15 | __Help__ = "select an object" 16 | __Author__ = "microelly" 17 | __Version__ = "0.1.1" 18 | __Status__ = 'alpha' 19 | __Requires__ = 'numpy' 20 | 21 | import Draft 22 | import FreeCAD as app 23 | import FreeCADGui as gui 24 | import PySide # FreeCAD's PySide! 25 | 26 | 27 | import numpy as np 28 | from pivy import coin 29 | 30 | 31 | def errorDialog(msg): 32 | diag = PySide.QtGui.QMessageBox(PySide.QtGui.QMessageBox.Critical, "Error Message", msg) 33 | diag.setWindowFlags(PySide.QtCore.Qt.WindowStaysOnTopHint) 34 | diag.exec_() 35 | 36 | 37 | try: 38 | sel = gui.Selection.getSelection()[0] 39 | c = Draft.clone(sel) 40 | except Exception: 41 | errorDialog("Select one object") 42 | raise Exception() 43 | 44 | 45 | camera = gui.ActiveDocument.ActiveView.getCameraNode() 46 | # camera.position.setValue(app.Vector(100,50,10)) 47 | camera.pointAt(coin.SbVec3f(0,0,0), coin.SbVec3f(0,0,1)) 48 | 49 | al = str(camera.position.getValue().toString()).split(' ') 50 | vec2 = app.Vector(float(al[0]), float(al[1]), float(al[2])) 51 | 52 | yaw_deg = np.degrees(np.arctan2(vec2.x, vec2.y)) 53 | pitch_deg = np.degrees(np.arctan2(vec2.z, np.sqrt(vec2.x**2 + vec2.y**2))) 54 | 55 | pla1 = app.Placement(app.Vector(0, 0, 0), app.Rotation(0, 0, -90)) 56 | pla2 = app.Placement(app.Vector(0, 0, 0), app.Rotation(0, 180+yaw_deg, 0)).multiply(pla1) 57 | pla3 = app.Placement(app.Vector(0, 0, 0), app.Rotation(0, 0, pitch_deg)).multiply(pla2) 58 | 59 | c.Placement = pla3 60 | c.ViewObject.Visibility = False 61 | v = Draft.makeShape2DView(c) 62 | -------------------------------------------------------------------------------- /myMacroDir/__init__.py: -------------------------------------------------------------------------------- 1 | import myMacro 2 | -------------------------------------------------------------------------------- /myMacroDir/myMacro.py: -------------------------------------------------------------------------------- 1 | # 2 | # your copyright info here 3 | # 4 | 5 | # meta data for macro management 6 | 7 | __Comment__ = 'My macro is a super macro and can be used whenever other macros fail ' 8 | __Web__ = "http://forum.freecadweb.org/viewtopic.php?f=8&t=11302" 9 | __Wiki__ = "http://www.freecadweb.org/wiki/Macro_FreeCAD_to_Kerkythea" 10 | __Icon__ = "Part_Common.svg" 11 | __Help__ = "This is the help text of this macro" 12 | __Author__ = "Freek Ad" 13 | __Version__ = 0.1 14 | __Status__ = 'alpha' 15 | __Requires__ = '' 16 | 17 | 18 | 19 | import FreeCAD 20 | 21 | 22 | # 23 | # the macro should have a test method to check the success of the installation 24 | # 25 | def test(): 26 | errorMsg="There are some errors: a, b, c" 27 | warnMsg="There are some warnings: d, e" 28 | infoMsg="There is a info: f" 29 | errors=3 30 | warns=2 31 | infos=1 32 | result=[errors,errorMsg,warns,warnMsg,infos,infoMsg] 33 | return result 34 | 35 | # 36 | # the macro should have a main method - the macro itself 37 | # 38 | def main(): 39 | t=FreeCAD.ParamGet('User parameter:BaseApp/Preferences/Macro') 40 | mp=t.GetString("MacroPath") 41 | FreeCAD.Console.PrintMessage("\n"*4+"H E L L O W O R L D,\n I'm myMacro.py located in "+ mp+ "/MyMacroDir" + "\n"*4) 42 | 43 | 44 | 45 | 46 | --------------------------------------------------------------------------------