├── .gitignore
├── LICENSE
├── README.md
├── __init__.py
├── blender_helper.py
├── changelog.md
├── dev
├── .gitignore
├── forum_post_list_results.md
├── freecad_linking_example
│ ├── assembly.FCStd
│ ├── assembly.png
│ ├── assembly_export.FCStd
│ ├── assembly_export.blend
│ ├── assembly_tree.png
│ ├── base_dimensions.FCStd
│ ├── box_panel.FCStd
│ ├── front_panel.FCStd
│ └── lamp.FCStd
├── freecad_test_ArchWB
│ ├── ArchTest.FCStd
│ ├── ArchTest.blend
│ ├── building.FCStd
│ ├── building.blend
│ ├── cube_with_color_faces.FCStd
│ ├── simple_wall.FCStd
│ ├── simple_wall_with_door.FCStd
│ ├── simple_wall_with_door.blend
│ ├── simple_window.FCStd
│ ├── simple_window.blend
│ └── test_MultiMaterial.FCStd
├── freecad_test_DraftLinkedArray
│ ├── Test_DraftLinkedArray.FCStd
│ ├── Test_DraftLinkedArray.png
│ ├── Test_DraftLinkedArray_ObjectTree.png
│ ├── justopen.py
│ └── justopen_copy_paste.py
├── freecad_test_DraftLinkedArray_Visibility
│ ├── DraftLinkedArray_Visibility.FCStd
│ └── DraftLinkedArray_Visibility2.FCStd
├── freecad_test_MyLittleWorld
│ ├── MyLittleWorld.FCStd
│ ├── MyLittleWorld.blend
│ ├── MyLittleWorld.png
│ ├── MyLittleWorld_ObjectTree.png
│ ├── list_objects.py
│ └── list_objects_copypaste.py
├── freecad_test_ParentChildPositions
│ ├── TestParentChildPositions.FCStd
│ ├── TestParentChildPositions.blend
│ ├── TestParentChildPositions.png
│ └── TestParentChildPositions_ObjectTree.png
├── freecad_test_body_objects
│ ├── BodyTest.FCStd
│ ├── BodyTest.blend
│ ├── BodyTest.png
│ ├── BodyTest_Minimal.FCStd
│ ├── BodyTest_Minimal.png
│ ├── BodyTest__ObjectTree.png
│ ├── list_objects.py
│ ├── list_objects__minimal.py
│ ├── list_objects_copypaste.py
│ ├── list_objects_copypaste__minimal.py
│ └── python_import_debug.blend
├── hdr
│ └── hdrihaven.com
│ │ └── lakeside_1k.hdr
├── import_tests.py
├── list_objects.py
├── list_objects_simple.py
├── macro_copySimpleExtended.py
├── script_test.blend
└── temp_python3_cmd_copyAndpaste.py
├── freecad_helper.py
├── import_fcstd
├── __init__.py
├── guidata.py
├── helper.py
└── material.py
└── pylama.ini
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | *.egg-info/
24 | .installed.cfg
25 | *.egg
26 | MANIFEST
27 |
28 | # PyInstaller
29 | # Usually these files are written by a python script from a template
30 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
31 | *.manifest
32 | *.spec
33 |
34 | # Installer logs
35 | pip-log.txt
36 | pip-delete-this-directory.txt
37 |
38 | # Unit test / coverage reports
39 | htmlcov/
40 | .tox/
41 | .coverage
42 | .coverage.*
43 | .cache
44 | nosetests.xml
45 | coverage.xml
46 | *.cover
47 | .hypothesis/
48 | .pytest_cache/
49 |
50 | # Translations
51 | *.mo
52 | *.pot
53 |
54 | # Django stuff:
55 | *.log
56 | local_settings.py
57 | db.sqlite3
58 |
59 | # Flask stuff:
60 | instance/
61 | .webassets-cache
62 |
63 | # Scrapy stuff:
64 | .scrapy
65 |
66 | # Sphinx documentation
67 | docs/_build/
68 |
69 | # PyBuilder
70 | target/
71 |
72 | # Jupyter Notebook
73 | .ipynb_checkpoints
74 |
75 | # pyenv
76 | .python-version
77 |
78 | # celery beat schedule file
79 | celerybeat-schedule
80 |
81 | # SageMath parsed files
82 | *.sage.py
83 |
84 | # Environments
85 | .env
86 | .venv
87 | env/
88 | venv/
89 | ENV/
90 | env.bak/
91 | venv.bak/
92 |
93 | # Spyder project settings
94 | .spyderproject
95 | .spyproject
96 |
97 | # Rope project settings
98 | .ropeproject
99 |
100 | # mkdocs documentation
101 | /site
102 |
103 | # mypy
104 | .mypy_cache/
105 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Stefan Krüger
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | # io_import_fcstd
5 | blender importer for FreeCAD files
6 |
7 | based on gist [yorikvanhavre/FreeCAD .FCStd importer for Blender 2.80](https://gist.github.com/yorikvanhavre/680156f59e2b42df8f5f5391cae2660b)
8 |
9 | # description
10 |
11 | This script imports FreeCAD .FCStd files into Blender.
12 | This is a work in progress, so not all geometry elements of FreeCAD
13 | might be supported at this point.
14 | And the `update` option will make some mess on older already imported objects..
15 |
16 |
17 | The development of this addon happens on the FreeCAD forum at
18 | [FreeCAD .FCStd importer for Blender 2.80](https://forum.freecadweb.org/viewtopic.php?f=22&t=39778)
19 |
20 | # download
21 | if you feel comfortable with git clone the repository -
22 | it is the easiest way to stay up to date ;-)
23 |
24 | otherwise you can use the download button to get a zip file.
25 | than you can install it in blender by going to
26 | `edit` - `preferences` - `Add-ons`
27 | and there is a button `Install...` in the top right corner.
28 | select the downloaded zip..
29 | then enable it (you can search for FreeCAD)
30 | after this please read [setup](#setup)
31 | (check the addon-preferences and set your path to FreeCAD lib folder..)
32 |
33 | # setup
34 |
35 | This addon requires FreeCAD to be installed on your system.
36 | A word of warning, your version of FreeCAD must be compiled
37 | with the same version of python as Blender.
38 |
39 | The first two numbers of the python version must be the same.
40 | For example, if Blender is using Python 3.7.2, your version of FreeCAD must
41 | use Python 3.7 too (the third number after 3.7 can be different)
42 |
43 | Once you have a Python3 version of FreeCAD installed, the FreeCAD
44 | Python module must be known to Blender.
45 |
46 |
47 | Set the correct path to FreeCAD.so (or FreeCAD.pyd on windows) in
48 | the Addons preferences in user settings, there is a setting for
49 | that under the addon panel.
50 | (these settings are only shown if the addon is enabled)
51 |
59 |
60 |
61 | A simple way to test if everything is OK is to enter the following line
62 | in the Python console of Blender.
63 | If no error message appears, everything is fine:
64 |
65 | `import FreeCAD`
66 |
67 |
68 | # working with...
69 | currently i only test under linux 64bit.
70 | please let me know in the issues if it is / is not working for you on other setups.
71 |
72 |
73 | Blender
74 | ```
75 | version: 3.1.0, branch: master, commit date: 2022-03-08 18:16, hash: c77597cd0e15, type: release
76 | build date: 2022-03-09, 00:34:48
77 | platform: 'Linux-5.11.0-50-generic-x86_64-with-glibc2.33'
78 | ```
79 | and
80 | FreeCAD 0.19
81 | ```python
82 | OS: Ubuntu 21.04 (KDE/plasma)
83 | Word size of OS: 64-bit
84 | Word size of FreeCAD: 64-bit
85 | Version: 0.19.
86 | Build type: Release
87 | Branch: unknown
88 | Hash: 0d9536ed3e8c7f40197b5606e1b7873625e1d6fe
89 | Python version: 3.9.5
90 | Qt version: 5.15.2
91 | Coin version: 4.0.0
92 | OCC version: 7.5.2
93 | Locale: English/United States (en_US)
94 |
95 |
96 | # build in python console:
97 | Python 3.9.5 (default, Nov 18 2021, 16:00:48)
98 | [GCC 10.3.0] on linux
99 | Type 'help', 'copyright', 'credits' or 'license' for more information.
100 | >>> App.Version()[4]
101 | '2021/07/21 08:10:00'
102 | ```
103 | and
104 | FreeCAD-daily 0.20
105 | ```python
106 | OS: Ubuntu 21.04 (KDE/plasma)
107 | Word size of FreeCAD: 64-bit
108 | Version: 0.20.
109 | Build type: Release
110 | Branch: unknown
111 | Hash: 4acef3f14fe694f28f7935108d36341b8df83a39
112 | Python version: 3.9.5
113 | Qt version: 5.15.2
114 | Coin version: 4.0.0
115 | OCC version: 7.5.2
116 | Locale: English/United States (en_US)
117 |
118 | # build in python console:
119 | Python 3.9.5 (default, Nov 18 2021, 16:00:48)
120 | [GCC 10.3.0] on linux
121 | >>> App.Version()[4]
122 | '2022/01/26 04:18:00'
123 | ```
124 |
125 | # TODO
126 | see the [issues for open points](https://github.com/s-light/io_import_fcstd/issues).
127 |
128 | - support clones
129 | - support texts, dimensions, etc (non-part/mesh objects)
130 |
--------------------------------------------------------------------------------
/__init__.py:
--------------------------------------------------------------------------------
1 | import math
2 | import bpy
3 |
4 | # ImportHelper is a helper class, defines filename and
5 | # invoke() function which calls the file selector.
6 | # from bpy_extras.io_utils import ImportHelper
7 | # not sure what this brings us...
8 |
9 | from . import import_fcstd
10 |
11 | bl_info = {
12 | "name": "FreeCAD Importer",
13 | "category": "Import-Export",
14 | "author": "Yorik van Havre; Stefan Krüger",
15 | "version": (6, 2, 1),
16 | "blender": (2, 80, 0),
17 | "location": "File > Import > FreeCAD",
18 | "description": "Imports a .FCStd file from FreeCAD",
19 | "warning": (
20 | "This addon needs FreeCAD installed on your system. "
21 | "Only Part- and Mesh-based objects supported at the moment. "
22 | "It is currently in an Experimental State.."
23 | ),
24 | }
25 |
26 | # brut force path loading:
27 | # import sys; sys.path.append("/path/to/FreeCAD.so")
28 |
29 |
30 | # ==============================================================================
31 | # Blender Operator class
32 | # ==============================================================================
33 |
34 | # https://docs.blender.org/api/current/bpy.types.AddonPreferences.html#module-bpy.types
35 |
36 |
37 | class IMPORT_OT_FreeCAD_Preferences(bpy.types.AddonPreferences):
38 | """A preferences settings dialog to set the path to the FreeCAD module."""
39 |
40 | # this must match the add-on name, use '__package__'
41 | # when defining this in a submodule of a python package.
42 | # bl_idname = __name__
43 | bl_idname = __package__
44 |
45 | # TODO: implement 'auto-magically' finding lib folder
46 | # as a first just implement a list of predefined paths
47 | # and use the first that exists
48 |
49 | # TODO: implement file-choose dialog for the path
50 |
51 | filepath_freecad: bpy.props.StringProperty(
52 | subtype="FILE_PATH",
53 | name="Path to FreeCAD lib",
54 | description=(
55 | "Path to \n" "FreeCAD.so (Mac/Linux) \n" "or \n" "FreeCAD.pyd (Windows)"
56 | ),
57 | default="/usr/lib/freecad-daily-python3/lib/FreeCAD.so",
58 | )
59 | filepath_system_packages: bpy.props.StringProperty(
60 | subtype="FILE_PATH",
61 | name="Path to system python modules",
62 | description=(
63 | "find with help of python console inside of FreeCAD:\n"
64 | ">>> import six\n"
65 | ">>> six.__file__\n"
66 | "'/usr/lib/python3/dist-packages/six.py'\n"
67 | "use first part: '/usr/lib/python3/dist-packages/'"
68 | ),
69 | default="/usr/lib/python3/dist-packages/",
70 | )
71 |
72 | def draw(self, context):
73 | """Draw Preferences."""
74 | layout = self.layout
75 | layout.label(
76 | text=(
77 | "FreeCAD must be installed on your system, and its path set below."
78 | " Make sure both FreeCAD and Blender use the same Python version "
79 | "(check their Python console)"
80 | )
81 | )
82 | layout.prop(self, "filepath_freecad")
83 | layout.prop(self, "filepath_system_packages")
84 |
85 |
86 | # class IMPORT_OT_FreeCAD(bpy.types.Operator, ImportHelper):
87 | class IMPORT_OT_FreeCAD(bpy.types.Operator):
88 | """Imports the contents of a FreeCAD .FCStd file."""
89 |
90 | bl_idname = "io_import_fcstd.import_freecad"
91 | bl_label = "Import FreeCAD FCStd file"
92 | bl_options = {"REGISTER", "UNDO"}
93 |
94 | # ImportHelper mixin class uses this
95 | filename_ext = ".fcstd"
96 |
97 | # https://blender.stackexchange.com/a/7891/16634
98 | # see Text -> Templates -> Python -> Operator File Export
99 | filter_glob: bpy.props.StringProperty(
100 | default="*.FCStd; *.fcstd", options={"HIDDEN"},
101 | )
102 |
103 | # Properties assigned by the file selection window.
104 | directory: bpy.props.StringProperty(
105 | maxlen=1024, subtype="FILE_PATH", options={"HIDDEN", "SKIP_SAVE"},
106 | )
107 | files: bpy.props.CollectionProperty(
108 | type=bpy.types.OperatorFileListElement, options={"HIDDEN", "SKIP_SAVE"}
109 | )
110 |
111 | # user import options
112 | option_skiphidden: bpy.props.BoolProperty(
113 | name="Skip hidden objects",
114 | default=True,
115 | description="Only import objects that where visible in FreeCAD",
116 | )
117 | option_filter_sketch: bpy.props.BoolProperty(
118 | name="Filter Sketch objects",
119 | default=True,
120 | description="Filter Sketch objects out.",
121 | )
122 | option_update: bpy.props.BoolProperty(
123 | name="Update existing objects",
124 | default=True,
125 | description=(
126 | "Keep objects with same names in current scene and "
127 | "their materials, only replace the geometry"
128 | ),
129 | )
130 | option_update_only_modified_meshes: bpy.props.BoolProperty(
131 | name="Update only modified meshes",
132 | default=True,
133 | description=(
134 | "Only replace the geometry if the the source in FreeCAD has changed."
135 | ),
136 | )
137 | option_placement: bpy.props.BoolProperty(
138 | name="Use Placements",
139 | default=True,
140 | description="Set Blender pivot points to the FreeCAD placements",
141 | )
142 | option_tessellation: bpy.props.FloatProperty(
143 | name="Tessellation value",
144 | default=0.10,
145 | description="The tessellation value to apply when triangulating shapes",
146 | )
147 | option_auto_smooth_use: bpy.props.BoolProperty(
148 | name="Auto Smooth",
149 | default=True,
150 | description="activate auto_smooth on every imported mesh",
151 | )
152 | option_auto_smooth_angle: bpy.props.FloatProperty(
153 | name="Auto Smooth Angle",
154 | default=math.radians(85),
155 | soft_min=math.radians(1),
156 | soft_max=math.radians(180),
157 | subtype="ANGLE",
158 | unit="ROTATION",
159 | description="set auto_smooth_angle on every imported mesh",
160 | )
161 | option_scale: bpy.props.FloatProperty(
162 | name="Scaling value",
163 | precision=4,
164 | default=0.001,
165 | # soft_min=0.0001,
166 | # soft_max=1,
167 | description=(
168 | "A scaling value to apply to imported objects. "
169 | "Default value of 0.001 means one Blender unit = 1 meter"
170 | ),
171 | )
172 | option_sharemats: bpy.props.BoolProperty(
173 | name="Share similar materials",
174 | default=True,
175 | description=("Objects with same color/transparency will use the same material"),
176 | )
177 | # option_create_tree: bpy.props.BoolProperty(
178 | # name="Recreate FreeCAD Object-Tree",
179 | # default=True,
180 | # description=(
181 | # "Try to recreate the same parent-child relationships "
182 | # "as in the FreeCAD Object-Tree."
183 | # )
184 | # )
185 | option_obj_name_prefix: bpy.props.StringProperty(
186 | name="Prefix object names",
187 | maxlen=42,
188 | default="",
189 | description=("prefix for every object name." ""),
190 | )
191 | option_prefix_with_filename: bpy.props.BoolProperty(
192 | name="Prefix object names with filename",
193 | default=False,
194 | description=(
195 | "recommend for multi-file import. \n"
196 | "otherwise it can create name confusions."
197 | ""
198 | ),
199 | )
200 | option_links_as_col: bpy.props.BoolProperty(
201 | name="App::Link as Collection-Instances",
202 | default=False,
203 | description=(
204 | "create App::Link objects as Collection-Instances. \n"
205 | "therefore create Link-Targets as Collections. \n"
206 | "this means the instances can only have the original "
207 | "material of the Link-Target.\n"
208 | "\n"
209 | "if you deactivate this the importer creates `real objects` "
210 | "for every App::Link object - they share the mesh. "
211 | "this can get very deep tree if the Link-Targets are "
212 | "App::Part objects themself.."
213 | ""
214 | ),
215 | )
216 |
217 | def invoke(self, context, event):
218 | """Invoke is called when the user picks our Import menu entry."""
219 | context.window_manager.fileselect_add(self)
220 | return {"RUNNING_MODAL"}
221 |
222 | def get_preferences(self):
223 | """Get addon preferences."""
224 | print("__package__: '{}'".format(__package__))
225 | user_preferences = bpy.context.preferences
226 | addon_prefs = user_preferences.addons[__package__].preferences
227 | return addon_prefs
228 |
229 | def get_path_to_freecad(self):
230 | """Get FreeCAD path from addon preferences."""
231 | # get the FreeCAD path specified in addon preferences
232 | addon_prefs = self.get_preferences()
233 | path = addon_prefs.filepath_freecad
234 | print("addon_prefs path_to freecad", path)
235 | return path
236 |
237 | def get_path_to_system_packages(self):
238 | """Get FreeCAD path from addon preferences."""
239 | # get the FreeCAD path specified in addon preferences
240 | addon_prefs = self.get_preferences()
241 | path = addon_prefs.filepath_system_packages
242 | print("addon_prefs path_to system_packages", path)
243 | return path
244 |
245 | # def get_path_to(self, target):
246 | # """Get FreeCAD mod path from addon preferences."""
247 | # # get the FreeCAD path specified in addon preferences
248 | # addon_prefs = self.get_preferences()
249 | # i currently dont know how to do this...
250 | # path = addon_prefs["filepath_" + target]
251 | # print("addon_prefs path to " + target + " ", path)
252 | # return path
253 |
254 | def execute(self, context):
255 | """Call when the user is done using the modal file-select window."""
256 | path_to_freecad = self.get_path_to_freecad()
257 | # path_to_system_packages = self.get_path_to("system_packages")
258 | path_to_system_packages = self.get_path_to_system_packages()
259 | dir = self.directory
260 | for file in self.files:
261 | filestr = str(file.name)
262 | if filestr.lower().endswith(".fcstd"):
263 | my_importer = import_fcstd.ImportFcstd(
264 | update=self.option_update,
265 | update_only_modified_meshes=self.option_update_only_modified_meshes,
266 | placement=self.option_placement,
267 | scale=self.option_scale,
268 | tessellation=self.option_tessellation,
269 | auto_smooth_use=self.option_auto_smooth_use,
270 | auto_smooth_angle=self.option_auto_smooth_angle,
271 | skiphidden=self.option_skiphidden,
272 | filter_sketch=self.option_filter_sketch,
273 | sharemats=self.option_sharemats,
274 | update_materials=False,
275 | obj_name_prefix=self.option_obj_name_prefix,
276 | obj_name_prefix_with_filename=self.option_prefix_with_filename,
277 | links_as_collectioninstance=self.option_links_as_col,
278 | path_to_freecad=path_to_freecad,
279 | path_to_system_packages=path_to_system_packages,
280 | report=self.report,
281 | )
282 | return my_importer.import_fcstd(filename=dir + filestr)
283 | return {"FINISHED"}
284 |
285 |
286 | # ==============================================================================
287 | # Register plugin with Blender
288 | # ==============================================================================
289 |
290 | classes = (
291 | IMPORT_OT_FreeCAD,
292 | IMPORT_OT_FreeCAD_Preferences,
293 | )
294 |
295 |
296 | def menu_func_import(self, context):
297 | """Needed if you want to add into a dynamic menu."""
298 | self.layout.operator(IMPORT_OT_FreeCAD.bl_idname, text="FreeCAD (.FCStd)")
299 |
300 |
301 | def register():
302 | """Register."""
303 | from bpy.utils import register_class
304 |
305 | for cls in classes:
306 | register_class(cls)
307 | bpy.types.TOPBAR_MT_file_import.append(menu_func_import)
308 |
309 |
310 | def unregister():
311 | """Unregister."""
312 | from bpy.utils import unregister_class
313 |
314 | for cls in reversed(classes):
315 | unregister_class(cls)
316 | bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)
317 |
318 |
319 | if __name__ == "__main__":
320 | register()
321 |
--------------------------------------------------------------------------------
/blender_helper.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 | """Random Helper Functions & Classe for Blender Python Scripting."""
5 |
6 | # import re
7 |
8 | try:
9 | import bpy
10 | except ModuleNotFoundError as e:
11 | print("Blender 'bpy' not available.", e)
12 | bpy = None
13 |
14 |
15 | # based on
16 | # https://www.geeksforgeeks.org/print-colors-python-terminal/
17 | # Python program to print
18 | # colored text and background
19 | class colors:
20 | """
21 | ASCII Color and Control Characters.
22 |
23 | reset all colors with colors.reset;
24 | two sub classes
25 | fg for foreground
26 | bg for background;
27 | use as colors.subclass.colorname:
28 | `colors.fg.red`
29 | `colors.bg.green`
30 | the generics
31 | bold, disable, underline, reverse, strike through, invisible
32 | work with the main class:
33 | `colors.bold`
34 | """
35 |
36 | reset = '\033[0m'
37 | bold = '\033[01m'
38 | disable = '\033[02m'
39 | underline = '\033[04m'
40 | reverse = '\033[07m'
41 | strikethrough = '\033[09m'
42 | invisible = '\033[08m'
43 |
44 | class fg:
45 | """Forderground Colors."""
46 |
47 | black = '\033[30m'
48 | red = '\033[31m'
49 | green = '\033[32m'
50 | orange = '\033[33m'
51 | blue = '\033[34m'
52 | purple = '\033[35m'
53 | cyan = '\033[36m'
54 | lightgrey = '\033[37m'
55 | darkgrey = '\033[90m'
56 | lightred = '\033[91m'
57 | lightgreen = '\033[92m'
58 | yellow = '\033[93m'
59 | lightblue = '\033[94m'
60 | pink = '\033[95m'
61 | lightcyan = '\033[96m'
62 |
63 | class bg:
64 | """Background Colors."""
65 |
66 | black = '\033[40m'
67 | red = '\033[41m'
68 | green = '\033[42m'
69 | orange = '\033[43m'
70 | blue = '\033[44m'
71 | purple = '\033[45m'
72 | cyan = '\033[46m'
73 | lightgrey = '\033[47m'
74 |
75 | @classmethod
76 | def get_flat_list(cls, obj_dict=None):
77 | """Get a flattend list of all control characters in dict."""
78 | result = []
79 | if obj_dict is None:
80 | obj_dict = cls.__dict__
81 | # print("*"*42)
82 | # print("obj_dict", obj_dict)
83 | # print("*"*42)
84 | for attr_name, attr_value in obj_dict.items():
85 | if not attr_name.startswith("__"):
86 | # if type(attr_value) is str:
87 | # value_str = attr_value.replace("\x1b", "\\x1b")
88 | # else:
89 | # value_str = attr_value
90 | # print(
91 | # "'{}' '{}': {} "
92 | # "".format(
93 | # attr_name,
94 | # type(attr_value),
95 | # value_str,
96 | # ),
97 | # end=""
98 | # )
99 | if type(attr_value) is str:
100 | # print(" STRING ")
101 | result.append(attr_value)
102 | elif type(attr_value) is type:
103 | # print(" TYPE ")
104 | result.extend(
105 | cls.get_flat_list(attr_value.__dict__)
106 | )
107 | else:
108 | # print(" UNKNOWN ")
109 | pass
110 | # print("*"*42)
111 | return result
112 |
113 |
114 | def filter_ASCII_controlls(data):
115 | """Remove ASCII controll characters."""
116 | code_list = colors.get_flat_list()
117 | for el in code_list:
118 | data = data.replace(el, "")
119 | return data
120 |
121 |
122 | def test_filtering():
123 | """Test for filter_ASCII_controlls."""
124 | test_string = (
125 | colors.fg.lightblue +
126 | "Hello " +
127 | colors.fg.green +
128 | "World " +
129 | colors.fg.orange +
130 | ":-)" +
131 | colors.reset
132 | )
133 | print("test_string", test_string)
134 | test_filtered = filter_ASCII_controlls(test_string)
135 | print("test_filtered", test_filtered)
136 |
137 |
138 | def print_colored(mode, data, pre_line=""):
139 | """Print with coloring similar to blenders info area."""
140 | printcolor = colors.reset
141 | if mode == {'INFO'}:
142 | printcolor = colors.fg.lightblue
143 | elif mode == {'WARNING'}:
144 | printcolor = colors.fg.orange
145 | elif mode == {'ERROR'}:
146 | printcolor = colors.fg.red
147 | print("{}{}{}{}".format(str(pre_line), printcolor, data, colors.reset))
148 |
149 |
150 | # https://blender.stackexchange.com/a/142317/16634
151 | def print_blender_console(mode, data, pre_line=""):
152 | """Print to blenders console area."""
153 | if bpy:
154 | message_type = mode.pop()
155 | if message_type == 'WARNING':
156 | message_type = 'INFO'
157 | elif message_type == 'INFO':
158 | message_type = 'OUTPUT'
159 | else:
160 | message_type = 'INFO'
161 | data = filter_ASCII_controlls(str(data))
162 | data = filter_ASCII_controlls(str(pre_line)) + data
163 | for window in bpy.context.window_manager.windows:
164 | screen = window.screen
165 | for area in screen.areas:
166 | if area.type == 'CONSOLE':
167 | override = {
168 | 'window': window,
169 | 'screen': screen,
170 | 'area': area
171 | }
172 | bpy.ops.console.scrollback_append(
173 | override, text=data, type=message_type)
174 |
175 |
176 | def print_console(mode, data, pre_line=""):
177 | """Multi-Print to blenders console area and system console."""
178 | print_colored(mode, data, pre_line=pre_line)
179 | print_blender_console(mode, data, pre_line=pre_line)
180 |
181 |
182 | def print_multi(*, mode, data, pre_line="", report=None):
183 | """Multi-Print to blenders console or info area and system console."""
184 | # print(
185 | # "print_multi "
186 | # "mode:'{}' pre_line:'{}' data:'{}'"
187 | # "".format(mode, pre_line, data)
188 | # )
189 | print_colored(mode, data, pre_line)
190 | if report:
191 | data = filter_ASCII_controlls(str(data))
192 | report(mode, data)
193 | else:
194 | print_blender_console(mode, data, pre_line)
195 |
196 |
197 | # def print_blender_info(mode, data):
198 | # message_type = mode.pop()
199 | # if message_type is 'WARNING':
200 | # message_type = 'ERROR'
201 | # if bpy:
202 | # data = filter_ASCII_controlls(str(data))
203 | # for window in bpy.context.window_manager.windows:
204 | # screen = window.screen
205 | # for area in screen.areas:
206 | # if area.type == 'CONSOLE':
207 | # override = {
208 | # 'window': window,
209 | # 'screen': screen,
210 | # 'area': area
211 | # }
212 | # bpy.ops.console.scrollback_append(
213 | # override, text=data, type=message_type)
214 |
215 |
216 | def purge_block(data_blocks):
217 | """Remove all unused object blocks."""
218 | counter = 0
219 | for block in data_blocks:
220 | if block.users == 0:
221 | data_blocks.remove(block)
222 | counter += 1
223 | return counter
224 |
225 |
226 | def purge_all_unused():
227 | """Remove all unused data blocks."""
228 | counter = 0
229 | # keep this order.
230 | # as the lower things are contained in the higher ones..
231 | counter += purge_block(bpy.data.objects)
232 | counter += purge_block(bpy.data.meshes)
233 | counter += purge_block(bpy.data.materials)
234 | counter += purge_block(bpy.data.textures)
235 | counter += purge_block(bpy.data.images)
236 | return counter
237 |
--------------------------------------------------------------------------------
/changelog.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | # Change-Log
5 |
6 | - v7.0.0 - 2019-11-??
7 | - ?
8 | - v6.1.1 - 2019-10-31
9 | - added App::Part handling
10 | - added App::Link handling
11 | - v6.0.0 - 2019-10-02
12 | - forked from gist to github repository
13 | - v5.0.0 - 2019-08-13
14 | - small fixes
15 | - better info messages if things go wrong
16 | - v4.0.0 - 2019-02-07
17 | - API changes
18 | - support of transparency
19 | - v3.0.0 - 2019-02-06
20 | - ported to Blender 2.80
21 | - v2.0.0 - 2018-06-21
22 | - option to turn
23 | - cycles mat on/off
24 | - per-face material support
25 | - use of polygons when possible
26 | - shared materials
27 | - v1.0.0 - 2018-06-12
28 | - initial release - basically working
29 |
--------------------------------------------------------------------------------
/dev/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # project specific
3 | temp
4 | temp.py
5 | temp1.py
6 | temp2.py
7 | temp.blend
8 |
9 | # blender
10 |
11 | render
12 |
13 | *.blend1
14 | *.blend2
15 | *.blend3
16 | *.blend4
17 | *.blend5
18 | *.blend6
19 | *.blend7
20 | *.blend8
21 | *.blend9
22 | *.blend10
23 |
24 |
25 |
26 | # For PCBs designed using KiCad: http://www.kicad-pcb.org/
27 | # Format documentation: http://kicad-pcb.org/help/file-formats/
28 |
29 | _saved_*
30 |
31 | # Temporary files
32 | *.000
33 | *.bak
34 | *.bck
35 | *.kicad_pcb-bak
36 | *~
37 | _autosave-*
38 | *.tmp
39 | *-cache.lib
40 | *-rescue.lib
41 | *-save.pro
42 | *-save.kicad_pcb
43 |
44 | # Netlist files (exported from Eeschema)
45 | *.net
46 |
47 | # Autorouter files (exported from Pcbnew)
48 | *.dsn
49 | *.ses
50 |
51 | # Exported BOM files
52 | *.xml
53 | *.csv
54 |
55 |
56 |
57 |
58 |
59 | FreeCAD backup files
60 | *.FCStd1
61 | *.fcstd1
62 | *.FCStd2
63 | *.fcstd2
64 |
65 | # open office / libre office lock files
66 | *.~lock.*
67 |
68 | # OwnCloud conflicts:
69 | *_conflict-*
70 |
71 | # project specific ignores:
72 | # for pcb-pool production files
73 | /pcb-pool*
74 | # for datasheets or other documents that are not allowed to be synced..
75 | /no_git_sync*
76 | /datasheets*
77 | # for old outdated things
78 | /old*
79 | # for zip backups:
80 | /backup*
81 |
82 |
83 | # Byte-compiled / optimized / DLL files
84 | __pycache__/
85 | *.py[cod]
86 | *$py.class
87 |
88 | # C extensions
89 | *.so
90 |
91 | # Distribution / packaging
92 | .Python
93 | build/
94 | develop-eggs/
95 | dist/
96 | downloads/
97 | eggs/
98 | .eggs/
99 | lib/
100 | lib64/
101 | # parts/
102 | sdist/
103 | var/
104 | wheels/
105 | *.egg-info/
106 | .installed.cfg
107 | *.egg
108 | MANIFEST
109 |
110 | # PyInstaller
111 | # Usually these files are written by a python script from a template
112 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
113 | *.manifest
114 | *.spec
115 |
116 | # Installer logs
117 | pip-log.txt
118 | pip-delete-this-directory.txt
119 |
120 | # Unit test / coverage reports
121 | htmlcov/
122 | .tox/
123 | .coverage
124 | .coverage.*
125 | .cache
126 | nosetests.xml
127 | coverage.xml
128 | *.cover
129 | .hypothesis/
130 | .pytest_cache/
131 |
132 | # Translations
133 | *.mo
134 | *.pot
135 |
136 | # Django stuff:
137 | *.log
138 | local_settings.py
139 | db.sqlite3
140 |
141 | # Flask stuff:
142 | instance/
143 | .webassets-cache
144 |
145 | # Scrapy stuff:
146 | .scrapy
147 |
148 | # Sphinx documentation
149 | docs/_build/
150 |
151 | # PyBuilder
152 | target/
153 |
154 | # Jupyter Notebook
155 | .ipynb_checkpoints
156 |
157 | # pyenv
158 | .python-version
159 |
160 | # celery beat schedule file
161 | celerybeat-schedule
162 |
163 | # SageMath parsed files
164 | *.sage.py
165 |
166 | # Environments
167 | .env
168 | .venv
169 | env/
170 | venv/
171 | ENV/
172 | env.bak/
173 | venv.bak/
174 |
175 | # Spyder project settings
176 | .spyderproject
177 | .spyproject
178 |
179 | # Rope project settings
180 | .ropeproject
181 |
182 | # mkdocs documentation
183 | /site
184 |
185 | # mypy
186 | .mypy_cache/
187 |
--------------------------------------------------------------------------------
/dev/forum_post_list_results.md:
--------------------------------------------------------------------------------
1 | hello all,
2 |
3 | i have setup a test at
4 | https://github.com/s-light/io_import_fcstd/blob/master/dev/list_objects.py#L98
5 |
6 | it uses ~4 different concepts to find the 'root' objects. (mostly from this thread..)
7 | you can find them at
8 | https://github.com/s-light/io_import_fcstd/blob/master/freecad_helper.py#L43
9 |
10 | in my FreeCAD test file (freecad_linking_example/assembly.FCStd) i have used a bunch of different objects-
11 | Part (grouping) / Part (Mesh) / one hidden / PartDesign Body / Links & Clones:
12 | [img]https://raw.githubusercontent.com/s-light/io_import_fcstd/master/dev/freecad_linking_example/assembly_tree.png[/img]
13 |
14 | the really funny thing is - the results are different from inside FreeCAD and from external Python console:
15 |
16 | Intern:
17 | [code]
18 | >>> print("FreeCAD version:", FreeCAD.Version())
19 | FreeCAD version: ['0', '19', '', 'https://code.launchpad.net/~vcs-imports/freecad/trunk', '2019/10/04 07:36:31']
20 | >>> run_tests(FreeCAD.ActiveDocument)
21 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
22 | get_filtered_objects 29
23 | Name Label TypeId p [Parents] i [InList] o [OutList] g [Group]
24 | obj: Part my_final_assembly App::Part p 0 i 0 o 7 g 6
25 | obj: Link box_panel_part App::Link p 1 i 1 o 3 g 1
26 | obj: Link001 front_panel_part App::Link p 1 i 1 o 1 g 1
27 | obj: Link_i0 Link_i0 App::LinkElement p 2 i 2 o 3 g 1
28 | obj: Link_i1 Link_i1 App::LinkElement p 2 i 2 o 3 g 1
29 | obj: Link002 lamp_part App::Link p 2 i 2 o 1 g 2
30 | obj: Array lamp_Array Part::FeaturePython p 1 i 1 o 3
31 | obj: Body floor_body PartDesign::Body p 1 i 2 o 5 g 3
32 | obj: Sketch floor_Sketch Sketcher::SketchObject p 1 i 2 o 1
33 | obj: Pad floor_Pad PartDesign::Pad p 1 i 3 o 1
34 | obj: Box Cube_Hidden Part::Box p 0 i 0 o 0
35 | obj: Sphere world_sphere Part::Sphere p 0 i 0 o 0
36 | obj: Part001 octagon_part App::Part p 0 i 0 o 2 g 1
37 | obj: Body001 octagon_Body PartDesign::Body p 1 i 1 o 5 g 3
38 | obj: Sketch001 octagon_sketch Sketcher::SketchObject p 1 i 2 o 1
39 | obj: Pad001 octagon_Pad PartDesign::Pad p 1 i 3 o 1
40 | obj: Fillet octagon_Fillet PartDesign::Fillet p 2 i 2 o 2
41 | obj: Fillet001 Fillet001 PartDesign::Fillet p 2 i 2 o 2
42 | obj: Cone blue_cone Part::Cone p 0 i 0 o 0
43 | obj: Box001 cube_colorfull Part::Box p 0 i 1 o 0
44 | obj: Part002 floor_part App::Part p 0 i 0 o 2 g 1
45 | obj: Body002 root_body PartDesign::Body p 0 i 0 o 5 g 3
46 | obj: Sketch002 Sketch002 Sketcher::SketchObject p 1 i 2 o 1
47 | obj: Pad002 Pad002 PartDesign::Pad p 1 i 3 o 1
48 | obj: Fillet002 Fillet002 PartDesign::Fillet p 2 i 2 o 2
49 | obj: Body003 cube_part_clone_Body PartDesign::Body p 0 i 0 o 3 g 1
50 | obj: Clone cube_part_clone PartDesign::FeatureBase p 2 i 2 o 1
51 | obj: Body004 floor_body_clone_Body PartDesign::Body p 0 i 0 o 3 g 1
52 | obj: Clone001 floor_body_clone PartDesign::FeatureBase p 2 i 2 o 1
53 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
54 | get_root_objects 10
55 | Name Label TypeId p [Parents] i [InList] o [OutList] g [Group]
56 | obj: Part my_final_assembly App::Part p 0 i 0 o 7 g 6
57 | obj: Box Cube_Hidden Part::Box p 0 i 0 o 0
58 | obj: Sphere world_sphere Part::Sphere p 0 i 0 o 0
59 | obj: Part001 octagon_part App::Part p 0 i 0 o 2 g 1
60 | obj: Cone blue_cone Part::Cone p 0 i 0 o 0
61 | obj: Box001 cube_colorfull Part::Box p 0 i 1 o 0
62 | obj: Part002 floor_part App::Part p 0 i 0 o 2 g 1
63 | obj: Body002 root_body PartDesign::Body p 0 i 0 o 5 g 3
64 | obj: Body003 cube_part_clone_Body PartDesign::Body p 0 i 0 o 3 g 1
65 | obj: Body004 floor_body_clone_Body PartDesign::Body p 0 i 0 o 3 g 1
66 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
67 | doc.RootObjects 9
68 | Name Label TypeId p [Parents] i [InList] o [OutList] g [Group]
69 | obj: Part my_final_assembly App::Part p 0 i 0 o 7 g 6
70 | obj: Box Cube_Hidden Part::Box p 0 i 0 o 0
71 | obj: Sphere world_sphere Part::Sphere p 0 i 0 o 0
72 | obj: Part001 octagon_part App::Part p 0 i 0 o 2 g 1
73 | obj: Cone blue_cone Part::Cone p 0 i 0 o 0
74 | obj: Part002 floor_part App::Part p 0 i 0 o 2 g 1
75 | obj: Body002 root_body PartDesign::Body p 0 i 0 o 5 g 3
76 | obj: Body003 cube_part_clone_Body PartDesign::Body p 0 i 0 o 3 g 1
77 | obj: Body004 floor_body_clone_Body PartDesign::Body p 0 i 0 o 3 g 1
78 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
79 | get_toplevel_objects 10
80 | Name Label TypeId p [Parents] i [InList] o [OutList] g [Group]
81 | obj: Part my_final_assembly App::Part p 0 i 0 o 7 g 6
82 | obj: Box Cube_Hidden Part::Box p 0 i 0 o 0
83 | obj: Sphere world_sphere Part::Sphere p 0 i 0 o 0
84 | obj: Part001 octagon_part App::Part p 0 i 0 o 2 g 1
85 | obj: Cone blue_cone Part::Cone p 0 i 0 o 0
86 | obj: Box001 cube_colorfull Part::Box p 0 i 1 o 0
87 | obj: Part002 floor_part App::Part p 0 i 0 o 2 g 1
88 | obj: Body002 root_body PartDesign::Body p 0 i 0 o 5 g 3
89 | obj: Body003 cube_part_clone_Body PartDesign::Body p 0 i 0 o 3 g 1
90 | obj: Body004 floor_body_clone_Body PartDesign::Body p 0 i 0 o 3 g 1
91 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
92 | tests done :-)
93 | >>>
94 | >>>
95 | >>>
96 | [/code]
97 |
98 | Extern:
99 | [code]
100 | stefan@stefan-Latitude-E6510:~/mydata/github/blender/io_import_fcstd/dev (master +)$ python3 -i ./list_objects.py
101 | Blender 'bpy' not available. No module named 'bpy'
102 | /home/stefan/mydata/github/blender/io_import_fcstd/dev/list_objects.py
103 | script_dir /home/stefan/mydata/github/blender/io_import_fcstd
104 | base_dir /home/stefan/mydata/github/blender/io_import_fcstd
105 | Configured FreeCAD path: /usr/lib/freecad-daily-python3/lib
106 | Sheet Metal workbench loaded
107 | FreeCAD version: ['0', '19', '', 'https://code.launchpad.net/~vcs-imports/freecad/trunk', '2019/10/04 07:36:31']
108 | ******************************************
109 | run import_tests
110 | FreeCAD document: ./dev/freecad_linking_example/assembly.FCStd
111 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
112 | get_filtered_objects 16
113 | Name Label TypeId p [Parents] i [InList] o [OutList] g [Group]
114 | obj: Part my_final_assembly App::Part p 0 i 0 o 7 g 6
115 | obj: Link box_panel_part App::Link p 1 i 1 o 3 g 0
116 | obj: Link001 front_panel_part App::Link p 1 i 1 o 1 g 0
117 | obj: Link_i0 Link_i0 App::LinkElement p 2 i 2 o 3 g 0
118 | obj: Link_i1 Link_i1 App::LinkElement p 2 i 2 o 3 g 0
119 | obj: Link002 lamp_part App::Link p 1 i 2 o 1 g 0
120 | obj: Array lamp_Array Part::FeaturePython p 1 i 1 o 3
121 | obj: Sketch floor_Sketch Sketcher::SketchObject p 0 i 0 o 1
122 | obj: Box Cube_Hidden Part::Box p 0 i 0 o 0
123 | obj: Sphere world_sphere Part::Sphere p 0 i 0 o 0
124 | obj: Part001 octagon_part App::Part p 0 i 0 o 1 g 0
125 | obj: Sketch001 octagon_sketch Sketcher::SketchObject p 0 i 0 o 1
126 | obj: Cone blue_cone Part::Cone p 0 i 0 o 0
127 | obj: Box001 cube_colorfull Part::Box p 0 i 0 o 0
128 | obj: Part002 floor_part App::Part p 0 i 0 o 1 g 0
129 | obj: Sketch002 Sketch002 Sketcher::SketchObject p 0 i 0 o 1
130 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
131 | get_root_objects 7
132 | Name Label TypeId p [Parents] i [InList] o [OutList] g [Group]
133 | obj: Part my_final_assembly App::Part p 0 i 0 o 7 g 6
134 | obj: Box Cube_Hidden Part::Box p 0 i 0 o 0
135 | obj: Sphere world_sphere Part::Sphere p 0 i 0 o 0
136 | obj: Part001 octagon_part App::Part p 0 i 0 o 1 g 0
137 | obj: Cone blue_cone Part::Cone p 0 i 0 o 0
138 | obj: Box001 cube_colorfull Part::Box p 0 i 0 o 0
139 | obj: Part002 floor_part App::Part p 0 i 0 o 1 g 0
140 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
141 | doc.RootObjects 15
142 | Name Label TypeId p [Parents] i [InList] o [OutList] g [Group]
143 | obj: Part my_final_assembly App::Part p 0 i 0 o 7 g 6
144 | obj: Origin001 Origin001 App::Origin p 0 i 0 o 6
145 | obj: Sketch floor_Sketch Sketcher::SketchObject p 0 i 0 o 1
146 | obj: Box Cube_Hidden Part::Box p 0 i 0 o 0
147 | obj: Sphere world_sphere Part::Sphere p 0 i 0 o 0
148 | obj: Part001 octagon_part App::Part p 0 i 0 o 1 g 0
149 | obj: Origin003 Origin003 App::Origin p 0 i 0 o 6
150 | obj: Sketch001 octagon_sketch Sketcher::SketchObject p 0 i 0 o 1
151 | obj: Cone blue_cone Part::Cone p 0 i 0 o 0
152 | obj: Box001 cube_colorfull Part::Box p 0 i 0 o 0
153 | obj: Part002 floor_part App::Part p 0 i 0 o 1 g 0
154 | obj: Origin005 Origin005 App::Origin p 0 i 0 o 6
155 | obj: Sketch002 Sketch002 Sketcher::SketchObject p 0 i 0 o 1
156 | obj: Origin006 Origin006 App::Origin p 0 i 0 o 6
157 | obj: Origin007 Origin007 App::Origin p 0 i 0 o 6
158 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
159 | get_toplevel_objects 15
160 | Name Label TypeId p [Parents] i [InList] o [OutList] g [Group]
161 | obj: Part my_final_assembly App::Part p 0 i 0 o 7 g 6
162 | obj: Origin001 Origin001 App::Origin p 0 i 0 o 6
163 | obj: Sketch floor_Sketch Sketcher::SketchObject p 0 i 0 o 1
164 | obj: Box Cube_Hidden Part::Box p 0 i 0 o 0
165 | obj: Sphere world_sphere Part::Sphere p 0 i 0 o 0
166 | obj: Part001 octagon_part App::Part p 0 i 0 o 1 g 0
167 | obj: Origin003 Origin003 App::Origin p 0 i 0 o 6
168 | obj: Sketch001 octagon_sketch Sketcher::SketchObject p 0 i 0 o 1
169 | obj: Cone blue_cone Part::Cone p 0 i 0 o 0
170 | obj: Box001 cube_colorfull Part::Box p 0 i 0 o 0
171 | obj: Part002 floor_part App::Part p 0 i 0 o 1 g 0
172 | obj: Origin005 Origin005 App::Origin p 0 i 0 o 6
173 | obj: Sketch002 Sketch002 Sketcher::SketchObject p 0 i 0 o 1
174 | obj: Origin006 Origin006 App::Origin p 0 i 0 o 6
175 | obj: Origin007 Origin007 App::Origin p 0 i 0 o 6
176 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
177 | tests done :-)
178 | ******************************************
179 | >>>
180 |
181 | [/code]
182 |
183 | interestingly i can't seem to access the 'Body' objects from the commandline version:
184 | intern
185 | [code]
186 | >>> doc = FreeCAD.ActiveDocument
187 | >>> doc.FileName
188 | '/home/stefan/mydata/github/blender/io_import_fcstd/dev/freecad_linking_example/assembly.FCStd'
189 | >>> obj = doc.getObjectsByLabel("octagon_Body")[0]
190 | >>> obj
191 |
192 | >>> obj.Label
193 | 'octagon_Body'
194 | >>> [/code]
195 |
196 | extern
197 | [code]
198 | >>> doc.FileName
199 | './freecad_linking_example/assembly.FCStd'
200 | >>> obj = doc.getObjectsByLabel("octagon_Body")[0]
201 | Traceback (most recent call last):
202 | File "", line 1, in
203 | IndexError: list index out of range
204 | >>> doc.getObjectsByLabel("octagon_Body")
205 | []
206 | >>>
207 | [/code]
208 |
209 | is this expected??
210 |
211 | sunny greetings
212 | stefan
213 |
214 | EDIT: rework output so its readable in the forum.
215 |
--------------------------------------------------------------------------------
/dev/freecad_linking_example/assembly.FCStd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s-light/io_import_fcstd/ace3fa082fd0b60721a8338680889fafb7c64024/dev/freecad_linking_example/assembly.FCStd
--------------------------------------------------------------------------------
/dev/freecad_linking_example/assembly.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s-light/io_import_fcstd/ace3fa082fd0b60721a8338680889fafb7c64024/dev/freecad_linking_example/assembly.png
--------------------------------------------------------------------------------
/dev/freecad_linking_example/assembly_export.FCStd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s-light/io_import_fcstd/ace3fa082fd0b60721a8338680889fafb7c64024/dev/freecad_linking_example/assembly_export.FCStd
--------------------------------------------------------------------------------
/dev/freecad_linking_example/assembly_export.blend:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s-light/io_import_fcstd/ace3fa082fd0b60721a8338680889fafb7c64024/dev/freecad_linking_example/assembly_export.blend
--------------------------------------------------------------------------------
/dev/freecad_linking_example/assembly_tree.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s-light/io_import_fcstd/ace3fa082fd0b60721a8338680889fafb7c64024/dev/freecad_linking_example/assembly_tree.png
--------------------------------------------------------------------------------
/dev/freecad_linking_example/base_dimensions.FCStd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s-light/io_import_fcstd/ace3fa082fd0b60721a8338680889fafb7c64024/dev/freecad_linking_example/base_dimensions.FCStd
--------------------------------------------------------------------------------
/dev/freecad_linking_example/box_panel.FCStd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s-light/io_import_fcstd/ace3fa082fd0b60721a8338680889fafb7c64024/dev/freecad_linking_example/box_panel.FCStd
--------------------------------------------------------------------------------
/dev/freecad_linking_example/front_panel.FCStd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s-light/io_import_fcstd/ace3fa082fd0b60721a8338680889fafb7c64024/dev/freecad_linking_example/front_panel.FCStd
--------------------------------------------------------------------------------
/dev/freecad_linking_example/lamp.FCStd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s-light/io_import_fcstd/ace3fa082fd0b60721a8338680889fafb7c64024/dev/freecad_linking_example/lamp.FCStd
--------------------------------------------------------------------------------
/dev/freecad_test_ArchWB/ArchTest.FCStd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s-light/io_import_fcstd/ace3fa082fd0b60721a8338680889fafb7c64024/dev/freecad_test_ArchWB/ArchTest.FCStd
--------------------------------------------------------------------------------
/dev/freecad_test_ArchWB/ArchTest.blend:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s-light/io_import_fcstd/ace3fa082fd0b60721a8338680889fafb7c64024/dev/freecad_test_ArchWB/ArchTest.blend
--------------------------------------------------------------------------------
/dev/freecad_test_ArchWB/building.FCStd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s-light/io_import_fcstd/ace3fa082fd0b60721a8338680889fafb7c64024/dev/freecad_test_ArchWB/building.FCStd
--------------------------------------------------------------------------------
/dev/freecad_test_ArchWB/building.blend:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s-light/io_import_fcstd/ace3fa082fd0b60721a8338680889fafb7c64024/dev/freecad_test_ArchWB/building.blend
--------------------------------------------------------------------------------
/dev/freecad_test_ArchWB/cube_with_color_faces.FCStd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s-light/io_import_fcstd/ace3fa082fd0b60721a8338680889fafb7c64024/dev/freecad_test_ArchWB/cube_with_color_faces.FCStd
--------------------------------------------------------------------------------
/dev/freecad_test_ArchWB/simple_wall.FCStd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s-light/io_import_fcstd/ace3fa082fd0b60721a8338680889fafb7c64024/dev/freecad_test_ArchWB/simple_wall.FCStd
--------------------------------------------------------------------------------
/dev/freecad_test_ArchWB/simple_wall_with_door.FCStd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s-light/io_import_fcstd/ace3fa082fd0b60721a8338680889fafb7c64024/dev/freecad_test_ArchWB/simple_wall_with_door.FCStd
--------------------------------------------------------------------------------
/dev/freecad_test_ArchWB/simple_wall_with_door.blend:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s-light/io_import_fcstd/ace3fa082fd0b60721a8338680889fafb7c64024/dev/freecad_test_ArchWB/simple_wall_with_door.blend
--------------------------------------------------------------------------------
/dev/freecad_test_ArchWB/simple_window.FCStd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s-light/io_import_fcstd/ace3fa082fd0b60721a8338680889fafb7c64024/dev/freecad_test_ArchWB/simple_window.FCStd
--------------------------------------------------------------------------------
/dev/freecad_test_ArchWB/simple_window.blend:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s-light/io_import_fcstd/ace3fa082fd0b60721a8338680889fafb7c64024/dev/freecad_test_ArchWB/simple_window.blend
--------------------------------------------------------------------------------
/dev/freecad_test_ArchWB/test_MultiMaterial.FCStd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s-light/io_import_fcstd/ace3fa082fd0b60721a8338680889fafb7c64024/dev/freecad_test_ArchWB/test_MultiMaterial.FCStd
--------------------------------------------------------------------------------
/dev/freecad_test_DraftLinkedArray/Test_DraftLinkedArray.FCStd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s-light/io_import_fcstd/ace3fa082fd0b60721a8338680889fafb7c64024/dev/freecad_test_DraftLinkedArray/Test_DraftLinkedArray.FCStd
--------------------------------------------------------------------------------
/dev/freecad_test_DraftLinkedArray/Test_DraftLinkedArray.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s-light/io_import_fcstd/ace3fa082fd0b60721a8338680889fafb7c64024/dev/freecad_test_DraftLinkedArray/Test_DraftLinkedArray.png
--------------------------------------------------------------------------------
/dev/freecad_test_DraftLinkedArray/Test_DraftLinkedArray_ObjectTree.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s-light/io_import_fcstd/ace3fa082fd0b60721a8338680889fafb7c64024/dev/freecad_test_DraftLinkedArray/Test_DraftLinkedArray_ObjectTree.png
--------------------------------------------------------------------------------
/dev/freecad_test_DraftLinkedArray/justopen.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 | """
5 | Just open the file.
6 |
7 | use from commandline or copy und paste content into running python3 instance
8 | """
9 |
10 | import sys
11 |
12 | path_to_freecad = "/usr/lib/freecad-daily-python3/lib/"
13 | try:
14 | sys.path.append(path_to_freecad)
15 | import FreeCAD
16 | print("FreeCAD version:", FreeCAD.Version())
17 | except ModuleNotFoundError as e:
18 | print("FreeCAD import failed.", e)
19 |
20 |
21 | def open_file(filename):
22 | docname = ""
23 | try:
24 | print("FreeCAD document:", filename)
25 | print("open file..")
26 | doc = FreeCAD.open(filename)
27 | print("file is opened.")
28 | docname = doc.Name
29 | if not doc:
30 | print("Unable to open the given FreeCAD file")
31 | else:
32 | print("file contains {} objects.".format(len(doc.Objects)))
33 | except Exception as e:
34 | raise e
35 | finally:
36 | if docname:
37 | print("close file..")
38 | FreeCAD.closeDocument(docname)
39 | print("file is closed.")
40 |
41 |
42 | # ******************************************
43 | if __name__ == '__main__':
44 | "Main Tests."
45 | print("*"*42)
46 | filename = "./Test_DraftLinkedArray.FCStd"
47 | open_file(filename)
48 | print("*"*42)
49 |
--------------------------------------------------------------------------------
/dev/freecad_test_DraftLinkedArray/justopen_copy_paste.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 | """
5 | Just opens a file.
6 |
7 | copy und paste content into running python3 instance
8 | """
9 |
10 | import sys
11 | sys.path.append("/usr/lib/freecad-daily-python3/lib/")
12 | import FreeCAD
13 |
14 | print("FreeCAD version:", FreeCAD.Version())
15 | print("*"*42)
16 | doc = FreeCAD.open("./Test_DraftLinkedArray.FCStd")
17 |
18 |
19 | print("file contains {} objects.".format(len(doc.Objects)))
20 | FreeCAD.closeDocument(doc.Name)
21 | print("file closed.")
22 |
--------------------------------------------------------------------------------
/dev/freecad_test_DraftLinkedArray_Visibility/DraftLinkedArray_Visibility.FCStd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s-light/io_import_fcstd/ace3fa082fd0b60721a8338680889fafb7c64024/dev/freecad_test_DraftLinkedArray_Visibility/DraftLinkedArray_Visibility.FCStd
--------------------------------------------------------------------------------
/dev/freecad_test_DraftLinkedArray_Visibility/DraftLinkedArray_Visibility2.FCStd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s-light/io_import_fcstd/ace3fa082fd0b60721a8338680889fafb7c64024/dev/freecad_test_DraftLinkedArray_Visibility/DraftLinkedArray_Visibility2.FCStd
--------------------------------------------------------------------------------
/dev/freecad_test_MyLittleWorld/MyLittleWorld.FCStd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s-light/io_import_fcstd/ace3fa082fd0b60721a8338680889fafb7c64024/dev/freecad_test_MyLittleWorld/MyLittleWorld.FCStd
--------------------------------------------------------------------------------
/dev/freecad_test_MyLittleWorld/MyLittleWorld.blend:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s-light/io_import_fcstd/ace3fa082fd0b60721a8338680889fafb7c64024/dev/freecad_test_MyLittleWorld/MyLittleWorld.blend
--------------------------------------------------------------------------------
/dev/freecad_test_MyLittleWorld/MyLittleWorld.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s-light/io_import_fcstd/ace3fa082fd0b60721a8338680889fafb7c64024/dev/freecad_test_MyLittleWorld/MyLittleWorld.png
--------------------------------------------------------------------------------
/dev/freecad_test_MyLittleWorld/MyLittleWorld_ObjectTree.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s-light/io_import_fcstd/ace3fa082fd0b60721a8338680889fafb7c64024/dev/freecad_test_MyLittleWorld/MyLittleWorld_ObjectTree.png
--------------------------------------------------------------------------------
/dev/freecad_test_MyLittleWorld/list_objects.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 | """
5 | List Objects.
6 |
7 | Stand-alone
8 | """
9 |
10 | import sys
11 | import os
12 |
13 |
14 | path_to_freecad = "/usr/lib/freecad-daily-python3/lib/FreeCAD.so"
15 |
16 |
17 | def append_freecad_path():
18 | """Append the FreeCAD path."""
19 | global path_to_freecad
20 | if os.path.exists(path_to_freecad):
21 | if os.path.isfile(path_to_freecad):
22 | path_to_freecad = os.path.dirname(path_to_freecad)
23 | print("Configured FreeCAD path:", path_to_freecad)
24 | if path_to_freecad not in sys.path:
25 | sys.path.append(path_to_freecad)
26 | else:
27 | print("FreeCAD path is not correct.")
28 |
29 |
30 | try:
31 | try:
32 | import FreeCAD
33 | except ModuleNotFoundError:
34 | append_freecad_path()
35 | import FreeCAD # noqa
36 | print("FreeCAD version:", FreeCAD.Version())
37 | except ModuleNotFoundError as e:
38 | print("FreeCAD import failed.", e)
39 |
40 |
41 | # ******************************************
42 |
43 | def print_obj_header(
44 | pre_line="",
45 | show_lists=False,
46 | show_list_details=False,
47 | ):
48 | """Print header for objects list."""
49 | print(
50 | pre_line +
51 | "{:<15} {:<25} {:<25}"
52 | "".format("Label", "Name", "TypeId"),
53 | end=''
54 | )
55 | if show_lists:
56 | print(
57 | "{:>2} {:>2} {:>2} {:>2}"
58 | "".format(
59 | "P",
60 | "I",
61 | "O",
62 | "G",
63 | ),
64 | end=''
65 | )
66 | if show_list_details:
67 | print(
68 | (
69 | " {:<10}" * 4
70 | ).format(
71 | '[Parents]',
72 | '[InList]',
73 | '[OutList]',
74 | '[Group]'
75 | ),
76 | end=''
77 | )
78 | print()
79 |
80 |
81 | def print_obj(
82 | obj,
83 | pre_line="",
84 | show_lists=False,
85 | show_list_details=False,
86 | end="\n",
87 | ):
88 | """Print object nicely formated."""
89 | print(
90 | pre_line +
91 | "{:<25} {:<15} {:<25}"
92 | "".format(obj.Label, obj.Name, obj.TypeId),
93 | end=''
94 | )
95 | if show_lists:
96 | group_count = '_'
97 | if hasattr(obj, 'Group'):
98 | group_count = len(obj.Group)
99 | print(
100 | "{:>2} {:>2} {:>2} {:>2}"
101 | "".format(
102 | len(obj.Parents),
103 | len(obj.InList),
104 | len(obj.OutList),
105 | group_count
106 | ),
107 | end=''
108 | )
109 | if show_list_details:
110 | group = None
111 | if hasattr(obj, 'Group'):
112 | group = obj.Group
113 | print(
114 | (
115 | " {:<10}" * 4
116 | ).format(
117 | str(obj.Parents),
118 | str(obj.InList),
119 | str(obj.OutList),
120 | str(group)
121 | ),
122 | end=''
123 | )
124 | print("", end=end)
125 |
126 |
127 | def print_objects(
128 | objects,
129 | pre_line="",
130 | pre_list_entry="* ",
131 | show_lists=False,
132 | show_list_details=False,
133 | ):
134 | """Print objects list."""
135 | pre_list_entry_space = " "*len(pre_list_entry)
136 | print_obj_header(
137 | pre_line=pre_line + pre_list_entry_space,
138 | show_lists=show_lists,
139 | show_list_details=show_list_details,
140 | )
141 | for obj in objects:
142 | print_obj(
143 | obj,
144 | pre_line=pre_line + pre_list_entry,
145 | show_lists=show_lists,
146 | show_list_details=show_list_details,
147 | )
148 |
149 |
150 | def print_obj_with_label(doc, label):
151 | """Print object with given label."""
152 | obj = doc.getObjectsByLabel(label)
153 | # print(obj)
154 | if len(obj) > 0:
155 | obj = obj[0]
156 | print_obj(obj)
157 | else:
158 | print("object with label '{}' not found.".format(label))
159 |
160 |
161 | # ******************************************
162 | #
163 | # Main experimetns
164 | #
165 | # ******************************************
166 |
167 | doc = FreeCAD.open(
168 | "./MyLittleWorld.FCStd"
169 | )
170 | docname = doc.Name
171 |
172 | # ******************************************
173 | print("~"*42)
174 | objects = doc.Objects
175 | print("doc.Objects", len(objects))
176 | print_objects(objects)
177 | print("~"*42)
178 |
179 | print_obj_with_label(doc, "World_Body")
180 | print_obj_with_label(doc, "Sun_Sphere")
181 | print_obj_with_label(doc, "Seagull_Body")
182 |
183 | print("~"*42)
184 | print("tests done :-)")
185 |
186 | FreeCAD.closeDocument(docname)
187 |
--------------------------------------------------------------------------------
/dev/freecad_test_MyLittleWorld/list_objects_copypaste.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 | """
5 | List Objects.
6 |
7 | Copy&Paste this complete script into the FreeCAD Python console!
8 | """
9 |
10 | import FreeCAD
11 |
12 |
13 | def print_obj_header(
14 | pre_line="",
15 | show_lists=False,
16 | show_list_details=False,
17 | ):
18 | """Print header for objects list."""
19 | print(
20 | pre_line +
21 | "{:<15} {:<25} {:<25}"
22 | "".format("Label", "Name", "TypeId"),
23 | end=''
24 | )
25 | if show_lists:
26 | print(
27 | "{:>2} {:>2} {:>2} {:>2}"
28 | "".format(
29 | "P",
30 | "I",
31 | "O",
32 | "G",
33 | ),
34 | end=''
35 | )
36 | if show_list_details:
37 | print(
38 | (
39 | " {:<10}" * 4
40 | ).format(
41 | '[Parents]',
42 | '[InList]',
43 | '[OutList]',
44 | '[Group]'
45 | ),
46 | end=''
47 | )
48 | print()
49 |
50 |
51 | def print_obj(
52 | obj,
53 | pre_line="",
54 | show_lists=False,
55 | show_list_details=False,
56 | end="\n",
57 | ):
58 | """Print object nicely formated."""
59 | print(
60 | pre_line +
61 | "{:<25} {:<15} {:<25}"
62 | "".format(obj.Label, obj.Name, obj.TypeId),
63 | end=''
64 | )
65 | if show_lists:
66 | group_count = '_'
67 | if hasattr(obj, 'Group'):
68 | group_count = len(obj.Group)
69 | print(
70 | "{:>2} {:>2} {:>2} {:>2}"
71 | "".format(
72 | len(obj.Parents),
73 | len(obj.InList),
74 | len(obj.OutList),
75 | group_count
76 | ),
77 | end=''
78 | )
79 | if show_list_details:
80 | group = None
81 | if hasattr(obj, 'Group'):
82 | group = obj.Group
83 | print(
84 | (
85 | " {:<10}" * 4
86 | ).format(
87 | str(obj.Parents),
88 | str(obj.InList),
89 | str(obj.OutList),
90 | str(group)
91 | ),
92 | end=''
93 | )
94 | print("", end=end)
95 |
96 |
97 | def print_objects(
98 | objects,
99 | pre_line="",
100 | pre_list_entry="* ",
101 | show_lists=False,
102 | show_list_details=False,
103 | ):
104 | """Print objects list."""
105 | pre_list_entry_space = " "*len(pre_list_entry)
106 | print_obj_header(
107 | pre_line=pre_line + pre_list_entry_space,
108 | show_lists=show_lists,
109 | show_list_details=show_list_details,
110 | )
111 | for obj in objects:
112 | print_obj(
113 | obj,
114 | pre_line=pre_line + pre_list_entry,
115 | show_lists=show_lists,
116 | show_list_details=show_list_details,
117 | )
118 |
119 |
120 | def print_obj_with_label(doc, label):
121 | """Print object with given label."""
122 | obj = doc.getObjectsByLabel(label)
123 | # print(obj)
124 | if len(obj) > 0:
125 | obj = obj[0]
126 | print_obj(obj)
127 | else:
128 | print("object with label '{}' not found.".format(label))
129 |
130 |
131 | # ******************************************
132 | #
133 | # Main experimetns
134 | #
135 | # ******************************************
136 |
137 | doc = FreeCAD.ActiveDocument
138 | docname = doc.Name
139 |
140 | # ******************************************
141 | objects = doc.Objects
142 | print("doc.Objects", len(objects))
143 | print_objects(objects)
144 |
145 | # ******************************************
146 |
147 | print_obj_with_label(doc, "World_Body")
148 | print_obj_with_label(doc, "Sun_Sphere")
149 | print_obj_with_label(doc, "Seagull_Body")
150 |
151 | # ******************************************
152 | print("tests done :-)")
153 |
--------------------------------------------------------------------------------
/dev/freecad_test_ParentChildPositions/TestParentChildPositions.FCStd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s-light/io_import_fcstd/ace3fa082fd0b60721a8338680889fafb7c64024/dev/freecad_test_ParentChildPositions/TestParentChildPositions.FCStd
--------------------------------------------------------------------------------
/dev/freecad_test_ParentChildPositions/TestParentChildPositions.blend:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s-light/io_import_fcstd/ace3fa082fd0b60721a8338680889fafb7c64024/dev/freecad_test_ParentChildPositions/TestParentChildPositions.blend
--------------------------------------------------------------------------------
/dev/freecad_test_ParentChildPositions/TestParentChildPositions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s-light/io_import_fcstd/ace3fa082fd0b60721a8338680889fafb7c64024/dev/freecad_test_ParentChildPositions/TestParentChildPositions.png
--------------------------------------------------------------------------------
/dev/freecad_test_ParentChildPositions/TestParentChildPositions_ObjectTree.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s-light/io_import_fcstd/ace3fa082fd0b60721a8338680889fafb7c64024/dev/freecad_test_ParentChildPositions/TestParentChildPositions_ObjectTree.png
--------------------------------------------------------------------------------
/dev/freecad_test_body_objects/BodyTest.FCStd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s-light/io_import_fcstd/ace3fa082fd0b60721a8338680889fafb7c64024/dev/freecad_test_body_objects/BodyTest.FCStd
--------------------------------------------------------------------------------
/dev/freecad_test_body_objects/BodyTest.blend:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s-light/io_import_fcstd/ace3fa082fd0b60721a8338680889fafb7c64024/dev/freecad_test_body_objects/BodyTest.blend
--------------------------------------------------------------------------------
/dev/freecad_test_body_objects/BodyTest.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s-light/io_import_fcstd/ace3fa082fd0b60721a8338680889fafb7c64024/dev/freecad_test_body_objects/BodyTest.png
--------------------------------------------------------------------------------
/dev/freecad_test_body_objects/BodyTest_Minimal.FCStd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s-light/io_import_fcstd/ace3fa082fd0b60721a8338680889fafb7c64024/dev/freecad_test_body_objects/BodyTest_Minimal.FCStd
--------------------------------------------------------------------------------
/dev/freecad_test_body_objects/BodyTest_Minimal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s-light/io_import_fcstd/ace3fa082fd0b60721a8338680889fafb7c64024/dev/freecad_test_body_objects/BodyTest_Minimal.png
--------------------------------------------------------------------------------
/dev/freecad_test_body_objects/BodyTest__ObjectTree.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s-light/io_import_fcstd/ace3fa082fd0b60721a8338680889fafb7c64024/dev/freecad_test_body_objects/BodyTest__ObjectTree.png
--------------------------------------------------------------------------------
/dev/freecad_test_body_objects/list_objects.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 | """
5 | List Objects.
6 |
7 | Stand-alone
8 | """
9 |
10 | import sys
11 | import os
12 |
13 |
14 | path_to_freecad = "/usr/lib/freecad-daily-python3/lib/FreeCAD.so"
15 |
16 |
17 | def append_freecad_path():
18 | """Append the FreeCAD path."""
19 | global path_to_freecad
20 | if os.path.exists(path_to_freecad):
21 | if os.path.isfile(path_to_freecad):
22 | path_to_freecad = os.path.dirname(path_to_freecad)
23 | print("Configured FreeCAD path:", path_to_freecad)
24 | if path_to_freecad not in sys.path:
25 | sys.path.append(path_to_freecad)
26 | else:
27 | print("FreeCAD path is not correct.")
28 |
29 |
30 | try:
31 | try:
32 | import FreeCAD
33 | except ModuleNotFoundError:
34 | append_freecad_path()
35 | import FreeCAD # noqa
36 | print("FreeCAD version:", FreeCAD.Version())
37 | except ModuleNotFoundError as e:
38 | print("FreeCAD import failed.", e)
39 |
40 |
41 | # ******************************************
42 |
43 | def print_obj_header(
44 | pre_line="",
45 | show_lists=False,
46 | show_list_details=False,
47 | ):
48 | """Print header for objects list."""
49 | print(
50 | pre_line +
51 | "{:<15} {:<25} {:<25}"
52 | "".format("Label", "Name", "TypeId"),
53 | end=''
54 | )
55 | if show_lists:
56 | print(
57 | "{:>2} {:>2} {:>2} {:>2}"
58 | "".format(
59 | "P",
60 | "I",
61 | "O",
62 | "G",
63 | ),
64 | end=''
65 | )
66 | if show_list_details:
67 | print(
68 | (
69 | " {:<10}" * 4
70 | ).format(
71 | '[Parents]',
72 | '[InList]',
73 | '[OutList]',
74 | '[Group]'
75 | ),
76 | end=''
77 | )
78 | print()
79 |
80 |
81 | def print_obj(
82 | obj,
83 | pre_line="",
84 | show_lists=False,
85 | show_list_details=False,
86 | end="\n",
87 | ):
88 | """Print object nicely formated."""
89 | print(
90 | pre_line +
91 | "{:<25} {:<15} {:<25}"
92 | "".format(obj.Label, obj.Name, obj.TypeId),
93 | end=''
94 | )
95 | if show_lists:
96 | group_count = '_'
97 | if hasattr(obj, 'Group'):
98 | group_count = len(obj.Group)
99 | print(
100 | "{:>2} {:>2} {:>2} {:>2}"
101 | "".format(
102 | len(obj.Parents),
103 | len(obj.InList),
104 | len(obj.OutList),
105 | group_count
106 | ),
107 | end=''
108 | )
109 | if show_list_details:
110 | group = None
111 | if hasattr(obj, 'Group'):
112 | group = obj.Group
113 | print(
114 | (
115 | " {:<10}" * 4
116 | ).format(
117 | str(obj.Parents),
118 | str(obj.InList),
119 | str(obj.OutList),
120 | str(group)
121 | ),
122 | end=''
123 | )
124 | print("", end=end)
125 |
126 |
127 | def print_objects(
128 | objects,
129 | pre_line="",
130 | pre_list_entry="* ",
131 | show_lists=False,
132 | show_list_details=False,
133 | ):
134 | """Print objects list."""
135 | pre_list_entry_space = " "*len(pre_list_entry)
136 | print_obj_header(
137 | pre_line=pre_line + pre_list_entry_space,
138 | show_lists=show_lists,
139 | show_list_details=show_list_details,
140 | )
141 | for obj in objects:
142 | print_obj(
143 | obj,
144 | pre_line=pre_line + pre_list_entry,
145 | show_lists=show_lists,
146 | show_list_details=show_list_details,
147 | )
148 |
149 |
150 | def print_obj_with_label(doc, label):
151 | """Print object with given label."""
152 | obj = doc.getObjectsByLabel(label)
153 | # print(obj)
154 | if len(obj) > 0:
155 | obj = obj[0]
156 | print_obj(obj)
157 | else:
158 | print("object with label '{}' not found.".format(label))
159 |
160 |
161 | # ******************************************
162 | #
163 | # Main experimetns
164 | #
165 | # ******************************************
166 |
167 | doc = FreeCAD.open(
168 | "./BodyTest.FCStd"
169 | )
170 | docname = doc.Name
171 |
172 | # ******************************************
173 | print("~"*42)
174 | objects = doc.Objects
175 | print("doc.Objects", len(objects))
176 | print_objects(objects)
177 | print("~"*42)
178 |
179 | print_obj_with_label(doc, "BodyTest_Part")
180 | print_obj_with_label(doc, "Body_Hex")
181 | print_obj_with_label(doc, "Sphere_Sun")
182 |
183 | print("~"*42)
184 | print("tests done :-)")
185 |
186 | FreeCAD.closeDocument(docname)
187 |
--------------------------------------------------------------------------------
/dev/freecad_test_body_objects/list_objects__minimal.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 | """
5 | List Objects.
6 |
7 | Stand-alone - run from commandline or copy & paste into python interpreter
8 | """
9 |
10 | import sys
11 | path_to_freecad = "/usr/lib/freecad-daily-python3/lib/"
12 | sys.path.append(path_to_freecad)
13 |
14 | import FreeCAD
15 | print("FreeCAD version:", FreeCAD.Version())
16 |
17 | doc = FreeCAD.open("./BodyTest_Minimal.FCStd")
18 | docname = doc.Name
19 | objects = FreeCAD.ActiveDocument.Objects
20 |
21 | print("doc.Objects", len(objects))
22 |
23 | for o in objects:
24 | print(o, o.Name)
25 |
26 | FreeCAD.closeDocument(docname)
27 |
--------------------------------------------------------------------------------
/dev/freecad_test_body_objects/list_objects_copypaste.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 | """
5 | List Objects.
6 |
7 | Copy&Paste this complete script into the FreeCAD Python console!
8 | """
9 |
10 | import FreeCAD
11 |
12 |
13 | def print_obj_header(
14 | pre_line="",
15 | show_lists=False,
16 | show_list_details=False,
17 | ):
18 | """Print header for objects list."""
19 | print(
20 | pre_line +
21 | "{:<15} {:<25} {:<25}"
22 | "".format("Label", "Name", "TypeId"),
23 | end=''
24 | )
25 | if show_lists:
26 | print(
27 | "{:>2} {:>2} {:>2} {:>2}"
28 | "".format(
29 | "P",
30 | "I",
31 | "O",
32 | "G",
33 | ),
34 | end=''
35 | )
36 | if show_list_details:
37 | print(
38 | (
39 | " {:<10}" * 4
40 | ).format(
41 | '[Parents]',
42 | '[InList]',
43 | '[OutList]',
44 | '[Group]'
45 | ),
46 | end=''
47 | )
48 | print()
49 |
50 |
51 | def print_obj(
52 | obj,
53 | pre_line="",
54 | show_lists=False,
55 | show_list_details=False,
56 | end="\n",
57 | ):
58 | """Print object nicely formated."""
59 | print(
60 | pre_line +
61 | "{:<25} {:<15} {:<25}"
62 | "".format(obj.Label, obj.Name, obj.TypeId),
63 | end=''
64 | )
65 | if show_lists:
66 | group_count = '_'
67 | if hasattr(obj, 'Group'):
68 | group_count = len(obj.Group)
69 | print(
70 | "{:>2} {:>2} {:>2} {:>2}"
71 | "".format(
72 | len(obj.Parents),
73 | len(obj.InList),
74 | len(obj.OutList),
75 | group_count
76 | ),
77 | end=''
78 | )
79 | if show_list_details:
80 | group = None
81 | if hasattr(obj, 'Group'):
82 | group = obj.Group
83 | print(
84 | (
85 | " {:<10}" * 4
86 | ).format(
87 | str(obj.Parents),
88 | str(obj.InList),
89 | str(obj.OutList),
90 | str(group)
91 | ),
92 | end=''
93 | )
94 | print("", end=end)
95 |
96 |
97 | def print_objects(
98 | objects,
99 | pre_line="",
100 | pre_list_entry="* ",
101 | show_lists=False,
102 | show_list_details=False,
103 | ):
104 | """Print objects list."""
105 | pre_list_entry_space = " "*len(pre_list_entry)
106 | print_obj_header(
107 | pre_line=pre_line + pre_list_entry_space,
108 | show_lists=show_lists,
109 | show_list_details=show_list_details,
110 | )
111 | for obj in objects:
112 | print_obj(
113 | obj,
114 | pre_line=pre_line + pre_list_entry,
115 | show_lists=show_lists,
116 | show_list_details=show_list_details,
117 | )
118 |
119 |
120 | def print_obj_with_label(doc, label):
121 | """Print object with given label."""
122 | obj = doc.getObjectsByLabel(label)
123 | # print(obj)
124 | if len(obj) > 0:
125 | obj = obj[0]
126 | print_obj(obj)
127 | else:
128 | print("object with label '{}' not found.".format(label))
129 |
130 |
131 | # ******************************************
132 | #
133 | # Main experimetns
134 | #
135 | # ******************************************
136 |
137 | doc = FreeCAD.ActiveDocument
138 | docname = doc.Name
139 |
140 | # ******************************************
141 | objects = doc.Objects
142 | print("doc.Objects", len(objects))
143 | print_objects(objects)
144 |
145 | # ******************************************
146 |
147 | print_obj_with_label(doc, "BodyTest_Part")
148 | print_obj_with_label(doc, "Body_Hex")
149 | print_obj_with_label(doc, "Sphere_Sun")
150 |
151 | # ******************************************
152 | print("tests done :-)")
153 |
--------------------------------------------------------------------------------
/dev/freecad_test_body_objects/list_objects_copypaste__minimal.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 | """
5 | List Objects.
6 |
7 | Copy&Paste this complete script into the FreeCAD Python console!
8 | """
9 |
10 |
11 |
12 |
13 |
14 | import FreeCAD
15 | print("FreeCAD version:", FreeCAD.Version())
16 |
17 | objects = FreeCAD.ActiveDocument.Objects
18 |
19 | print("doc.Objects", len(objects))
20 |
21 | for o in objects:
22 | print(o, o.Name)
23 |
--------------------------------------------------------------------------------
/dev/freecad_test_body_objects/python_import_debug.blend:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s-light/io_import_fcstd/ace3fa082fd0b60721a8338680889fafb7c64024/dev/freecad_test_body_objects/python_import_debug.blend
--------------------------------------------------------------------------------
/dev/hdr/hdrihaven.com/lakeside_1k.hdr:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s-light/io_import_fcstd/ace3fa082fd0b60721a8338680889fafb7c64024/dev/hdr/hdrihaven.com/lakeside_1k.hdr
--------------------------------------------------------------------------------
/dev/import_tests.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 | """
5 | Import Tests.
6 |
7 | this script can be called from commandline or from within blender.
8 | therefore we need some heavy tricks with the sys.path..
9 | """
10 |
11 | import sys
12 | import os
13 | import importlib
14 |
15 | print("$"*42)
16 | print("import_tests.py")
17 | print("$"*42)
18 |
19 | try:
20 | import bpy
21 | except ModuleNotFoundError as e:
22 | print("Blender 'bpy' not available.", e)
23 | bpy = None
24 |
25 |
26 | # prepare directory helpers
27 | # print(sys.argv[0])
28 | print(os.path.realpath(__file__))
29 | script_dir = "."
30 | if bpy:
31 | temp_path = os.path.join(__file__, "..")
32 | temp_path = os.path.join(temp_path, "..")
33 | script_dir = os.path.realpath(temp_path)
34 | script_dir = os.path.realpath(script_dir)
35 | print("script_dir", script_dir)
36 | if script_dir not in sys.path:
37 | sys.path.append(script_dir)
38 |
39 | base_dir = ".."
40 | if bpy:
41 | base_dir = os.path.join(script_dir, "..")
42 | base_dir = os.path.realpath(base_dir)
43 | print("base_dir", base_dir)
44 | if base_dir not in sys.path:
45 | sys.path.append(base_dir)
46 |
47 | outside_package_dir = os.path.join(base_dir, "..")
48 | outside_package_dir = os.path.realpath(outside_package_dir)
49 | print("outside_package_dir", outside_package_dir)
50 | if outside_package_dir not in sys.path:
51 | sys.path.append(outside_package_dir)
52 |
53 | # print("sys.path:")
54 | # for p in sys.path:
55 | # print(p)
56 |
57 |
58 | # fallback path to FreeCAD daily
59 | path_to_freecad = "/usr/lib/freecad-daily-python3/lib/FreeCAD.so"
60 | path_to_system_packages = "/usr/lib/python3/dist-packages/"
61 |
62 |
63 | def append_path(path, sub=""):
64 | if path and sub:
65 | path = os.path.join(path, sub)
66 | print("full path:", path)
67 | if path and os.path.exists(path):
68 | if os.path.isfile(path):
69 | path = os.path.dirname(path)
70 | print("Configured path:", path)
71 | if path not in sys.path:
72 | sys.path.append(path)
73 | else:
74 | print(
75 | "Path does not exist. Please check! "
76 | "'{}'".format(path)
77 | )
78 |
79 |
80 | def get_preferences():
81 | """Get addon preferences."""
82 | __package__ = "io_import_fcstd"
83 | print("__package__: '{}'".format(__package__))
84 | addon_prefs = None
85 | # pref = bpy.context.preferences.addons["io_import_fcstd"].preferences
86 | if bpy:
87 | user_preferences = bpy.context.preferences
88 | addon_prefs = user_preferences.addons[__package__].preferences
89 | return addon_prefs
90 |
91 |
92 | def get_path_to_freecad():
93 | """Get FreeCAD path from addon preferences."""
94 | # get the FreeCAD path specified in addon preferences
95 | addon_prefs = get_preferences()
96 | path = addon_prefs.filepath_freecad
97 | print("addon_prefs path_to freecad", path)
98 | return path
99 |
100 |
101 | def get_path_to_system_packages():
102 | """Get FreeCAD path from addon preferences."""
103 | # get the FreeCAD path specified in addon preferences
104 | addon_prefs = get_preferences()
105 | path = addon_prefs.filepath_system_packages
106 | print("addon_prefs path_to system_packages", path)
107 | return path
108 |
109 |
110 | def append_freecad_path():
111 | """Append the FreeCAD path."""
112 | global path_to_freecad
113 | global path_to_system_packages
114 | if bpy:
115 | # specified in addon preferences
116 | user_preferences = bpy.context.preferences
117 | addon_prefs = user_preferences.addons["io_import_fcstd"].preferences
118 | path_to_freecad = addon_prefs.filepath_freecad
119 | path_to_freecad = get_path_to_freecad()
120 | path_to_system_packages = get_path_to_system_packages()
121 | append_path(path_to_freecad)
122 |
123 |
124 | try:
125 | append_freecad_path()
126 | import FreeCAD
127 | print("FreeCAD version:", FreeCAD.Version())
128 | except ModuleNotFoundError as e:
129 | print("FreeCAD import failed.", e)
130 |
131 | # try:
132 | # import Part
133 | # # import Draft
134 | # # import PartDesignGui
135 | # except ModuleNotFoundError as e:
136 | # print("FreeCAD Workbench import failed:", e)
137 |
138 | print("*"*42)
139 | print("from io_import_fcstd import import_fcstd")
140 | # pylama:ignore=E402
141 | from io_import_fcstd import import_fcstd
142 | importlib.reload(import_fcstd)
143 | importlib.reload(import_fcstd.helper)
144 | importlib.reload(import_fcstd.guidata)
145 | importlib.reload(import_fcstd.material)
146 | importlib.reload(import_fcstd.fc_helper)
147 | importlib.reload(import_fcstd.b_helper)
148 |
149 | # ******************************************
150 | #
151 | # Main experimetns
152 | #
153 | # ******************************************
154 |
155 | # def run_tests(doc):
156 | # print("~"*42)
157 | # print("get_filtered_objects")
158 | # freecad_helper.print_objects(freecad_helper.get_filtered_objects(doc))
159 | #
160 | # print("~"*42)
161 | # print("get_root_objects")
162 | # objects = freecad_helper.get_root_objects(
163 | # doc,
164 | # filter_list=['Sketcher::SketchObject', ]
165 | # )
166 | # freecad_helper.print_objects(objects)
167 | #
168 | # print("~"*42)
169 | # print("doc.RootObjects")
170 | # freecad_helper.print_objects(doc.RootObjects)
171 | #
172 | # print("~"*42)
173 | # print("get_toplevel_objects")
174 | # freecad_helper.print_objects(freecad_helper.get_toplevel_objects(doc))
175 | #
176 | # print("~"*42)
177 | # print("tests done :-)")
178 |
179 |
180 | def freecad_python_console_copy_and_paste():
181 | """Copy into FreeCAD python console..."""
182 | # sys.path.append("/home/stefan/mydata/github/blender/io_import_fcstd")
183 | sys.path.append(outside_package_dir)
184 | import freecad_helper as fch # noqa
185 |
186 |
187 | # ******************************************
188 | def main_test():
189 | """Run Tests."""
190 | print("*"*42)
191 | print("run import_tests")
192 |
193 | # get open document name
194 | doc_filename = os.path.splitext(os.path.basename(bpy.data.filepath))
195 | if doc_filename[1].endswith("blend"):
196 | doc_filename = doc_filename[0]
197 | doc_filename += ".FCStd"
198 | filename = os.path.join(".", doc_filename)
199 | filename = os.path.join(script_dir, filename)
200 | else:
201 | # fallback
202 | # filename = "./dev/freecad_linking_example/assembly.FCStd"
203 | filename = "./dev/freecad_linking_example/assembly__export.FCStd"
204 | filename = os.path.join(base_dir, filename)
205 |
206 | filename = os.path.realpath(filename)
207 | print("FreeCAD document to import:", filename)
208 |
209 | print(
210 | "purge '{}' unused data blocks."
211 | "".format(import_fcstd.b_helper.purge_all_unused())
212 | )
213 | print(
214 | "purge '{}' unused data blocks."
215 | "".format(import_fcstd.b_helper.purge_all_unused())
216 | )
217 |
218 | my_importer = import_fcstd.ImportFcstd(
219 | # update=True,
220 | # placement=True,
221 | # scale=0.001,
222 | # tessellation=0.10,
223 | # auto_smooth_use=True,
224 | # auto_smooth_angle=math.radians(85),
225 | # skiphidden=True,
226 | # filter_sketch=True,
227 | # sharemats=True,
228 | # update_materials=False,
229 | # obj_name_prefix="",
230 | # obj_name_prefix_with_filename=False,
231 | links_as_collectioninstance=False,
232 | path_to_freecad=path_to_freecad,
233 | path_to_system_packages=path_to_system_packages,
234 | # report=None
235 | )
236 | my_importer.import_fcstd(filename=filename)
237 |
238 | if bpy:
239 | print("\n"*2)
240 |
241 |
242 | if __name__ == '__main__':
243 | main_test()
244 |
--------------------------------------------------------------------------------
/dev/list_objects.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 | """
5 | List Objects
6 |
7 | this script can be called from commandline or from within blender.
8 | therefore we need some heavy tricks with the sys.path..
9 | """
10 |
11 | import sys
12 | import os
13 | import importlib
14 |
15 | try:
16 | import bpy
17 |
18 | print("\n" * 2)
19 | except ModuleNotFoundError as e:
20 | print("Blender 'bpy' not available.", e)
21 | bpy = None
22 |
23 |
24 | # prepare directory helpers
25 | # print(sys.argv[0])
26 | print(os.path.realpath(__file__))
27 | script_dir = ".."
28 | if bpy:
29 | temp_path = os.path.join(__file__, "..")
30 | temp_path = os.path.join(temp_path, "..")
31 | script_dir = os.path.realpath(temp_path)
32 | script_dir = os.path.realpath(script_dir)
33 | print("script_dir", script_dir)
34 |
35 | base_dir = ".."
36 | if bpy:
37 | base_dir = os.path.join(script_dir, "..")
38 | base_dir = os.path.realpath(base_dir)
39 | print("base_dir", base_dir)
40 |
41 | # Adds base_dir to python modules path.
42 | if base_dir not in sys.path:
43 | sys.path.append(base_dir)
44 | # print("sys.path:")
45 | # for p in sys.path:
46 | # print(p)
47 |
48 |
49 | # fallback path to FreeCAD daily
50 | path_to_freecad = "/usr/lib/freecad-daily-python3/lib/FreeCAD.so"
51 |
52 |
53 | def append_freecad_path():
54 | """Append the FreeCAD path."""
55 | global path_to_freecad
56 | if bpy:
57 | # specified in addon preferences
58 | user_preferences = bpy.context.preferences
59 | addon_prefs = user_preferences.addons["io_import_fcstd"].preferences
60 | path_to_freecad = addon_prefs.filepath
61 | if os.path.exists(path_to_freecad):
62 | if os.path.isfile(path_to_freecad):
63 | path_to_freecad = os.path.dirname(path_to_freecad)
64 | print("Configured FreeCAD path:", path_to_freecad)
65 | if path_to_freecad not in sys.path:
66 | sys.path.append(path_to_freecad)
67 | else:
68 | if bpy:
69 | print("FreeCAD path is not configured in preferences correctly.")
70 | else:
71 | print("FreeCAD path is not correct.")
72 |
73 |
74 | try:
75 | append_freecad_path()
76 | import FreeCAD
77 |
78 | print("FreeCAD version:", FreeCAD.Version())
79 | except ModuleNotFoundError as e:
80 | print("FreeCAD import failed.", e)
81 |
82 | # try:
83 | # import Part
84 | # # import Draft
85 | # # import PartDesignGui
86 | # except ModuleNotFoundError as e:
87 | # print("FreeCAD Workbench import failed:", e)
88 |
89 |
90 | import freecad_helper # noqa
91 |
92 | importlib.reload(freecad_helper)
93 |
94 |
95 | # ******************************************
96 | #
97 | # Main experimetns
98 | #
99 | # ******************************************
100 |
101 | doc = None
102 | t1 = None
103 | t2 = None
104 |
105 |
106 | def start_test():
107 | global doc
108 | doc = FreeCAD.open("./freecad_linking_example/assembly.FCStd")
109 | global t1
110 | t1 = doc.getObjectsByLabel("my_final_assembly")[0]
111 | global t2
112 | t2 = doc.getObjectsByLabel("octagon_part")[0]
113 |
114 |
115 | def run_tests(doc):
116 | print("~" * 42)
117 | objects = freecad_helper.get_filtered_objects(doc)
118 | print("get_filtered_objects", len(objects))
119 | freecad_helper.print_objects(objects, show_lists=True, show_list_details=True)
120 |
121 | print("~" * 42)
122 | objects, objects_withHost = freecad_helper.get_root_objects(
123 | doc, filter_list=["Sketcher::SketchObject"]
124 | )
125 | print("get_root_objects", len(objects))
126 | freecad_helper.print_objects(objects)
127 | print("get_root_objects withHost", len(objects_withHost))
128 | freecad_helper.print_objects(objects_withHost)
129 |
130 | print("~" * 42)
131 | objects = doc.RootObjects
132 | print("doc.RootObjects", len(objects))
133 | freecad_helper.print_objects(objects)
134 |
135 | print("~" * 42)
136 | objects = freecad_helper.get_toplevel_objects(doc)
137 | print("get_toplevel_objects", len(objects))
138 | freecad_helper.print_objects(objects)
139 |
140 | print("~" * 42)
141 | print("tests done :-)")
142 |
143 |
144 | # ******************************************
145 | def main_test():
146 | "Main Tests."
147 | if bpy:
148 | print("\n" * 2)
149 | print("*" * 42)
150 | print("run import_tests")
151 |
152 | # Context Managers not implemented..
153 | # see https://docs.python.org/3.8/reference/compound_stmts.html#with
154 | # with FreeCAD.open(self.config["filename"]) as doc:
155 | # so we use the classic try finally block:
156 | docname = ""
157 | try:
158 | # filename_relative = "./dev/freecad_linking_example/assembly.FCStd"
159 | # filename_relative = (
160 | # "./dev/freecad_test_ParentChildPositions/TestParentChildPositions.FCStd"
161 | # )
162 | filename_relative = "./dev/freecad_test_ArchWB/simple_wall_with_door.FCStd"
163 | print("FreeCAD document:", filename_relative)
164 | filename = os.path.join(base_dir, filename_relative)
165 | print("open file..")
166 | doc = FreeCAD.open(filename)
167 | print("file is opened")
168 | docname = doc.Name
169 | if not doc:
170 | print("Unable to open the given FreeCAD file")
171 | else:
172 | run_tests(doc)
173 | except Exception as e:
174 | raise e
175 | finally:
176 | if docname:
177 | FreeCAD.closeDocument(docname)
178 | print("*" * 42)
179 | if bpy:
180 | print("\n" * 2)
181 |
182 |
183 | if __name__ == "__main__":
184 | main_test()
185 |
--------------------------------------------------------------------------------
/dev/list_objects_simple.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 | """
5 | List Objects.
6 |
7 | Stand-alone / Copy&Paste version
8 | """
9 |
10 | import sys
11 | import os
12 |
13 |
14 | def append_freecad_path():
15 | """Append the FreeCAD path."""
16 | path_to_freecad = "/usr/lib/freecad-daily-python3/lib/FreeCAD.so"
17 | if os.path.exists(path_to_freecad):
18 | if os.path.isfile(path_to_freecad):
19 | path_to_freecad = os.path.dirname(path_to_freecad)
20 | print("Configured FreeCAD path:", path_to_freecad)
21 | if path_to_freecad not in sys.path:
22 | sys.path.append(path_to_freecad)
23 | else:
24 | print("FreeCAD path is not correct.")
25 |
26 |
27 | try:
28 | try:
29 | import FreeCAD
30 | except ModuleNotFoundError:
31 | append_freecad_path()
32 | import FreeCAD
33 | print("FreeCAD version:", FreeCAD.Version())
34 | except ModuleNotFoundError as e:
35 | print("FreeCAD import failed.", e)
36 |
37 |
38 | # ******************************************
39 |
40 | def print_obj_header(
41 | pre_line="",
42 | show_lists=False,
43 | show_list_details=False,
44 | ):
45 | """Print header for objects list."""
46 | print(
47 | pre_line +
48 | "{:<15} {:<25} {:<25}"
49 | "".format("Label", "Name", "TypeId"),
50 | end=''
51 | )
52 | if show_lists:
53 | print(
54 | "{:>2} {:>2} {:>2} {:>2}"
55 | "".format(
56 | "P",
57 | "I",
58 | "O",
59 | "G",
60 | ),
61 | end=''
62 | )
63 | if show_list_details:
64 | print(
65 | (
66 | " {:<10}" * 4
67 | ).format(
68 | '[Parents]',
69 | '[InList]',
70 | '[OutList]',
71 | '[Group]'
72 | ),
73 | end=''
74 | )
75 | print()
76 |
77 |
78 | def print_obj(
79 | obj,
80 | pre_line="",
81 | show_lists=False,
82 | show_list_details=False,
83 | end="\n",
84 | ):
85 | """Print object nicely formated."""
86 | print(
87 | pre_line +
88 | "{:<25} {:<15} {:<25}"
89 | "".format(obj.Label, obj.Name, obj.TypeId),
90 | end=''
91 | )
92 | if show_lists:
93 | group_count = '_'
94 | if hasattr(obj, 'Group'):
95 | group_count = len(obj.Group)
96 | print(
97 | "{:>2} {:>2} {:>2} {:>2}"
98 | "".format(
99 | len(obj.Parents),
100 | len(obj.InList),
101 | len(obj.OutList),
102 | group_count
103 | ),
104 | end=''
105 | )
106 | if show_list_details:
107 | group = None
108 | if hasattr(obj, 'Group'):
109 | group = obj.Group
110 | print(
111 | (
112 | " {:<10}" * 4
113 | ).format(
114 | str(obj.Parents),
115 | str(obj.InList),
116 | str(obj.OutList),
117 | str(group)
118 | ),
119 | end=''
120 | )
121 | print("", end=end)
122 |
123 |
124 | def print_objects(
125 | objects,
126 | pre_line="",
127 | pre_list_entry="* ",
128 | show_lists=False,
129 | show_list_details=False,
130 | ):
131 | """Print objects list."""
132 | pre_list_entry_space = " "*len(pre_list_entry)
133 | print_obj_header(
134 | pre_line=pre_line + pre_list_entry_space,
135 | show_lists=show_lists,
136 | show_list_details=show_list_details,
137 | )
138 | for obj in objects:
139 | print_obj(
140 | obj,
141 | pre_line=pre_line + pre_list_entry,
142 | show_lists=show_lists,
143 | show_list_details=show_list_details,
144 | )
145 |
146 |
147 | def print_obj_with_label(doc, label):
148 | """Print object with given label."""
149 | obj = doc.getObjectsByLabel(label)
150 | # print(obj)
151 | if len(obj) > 0:
152 | obj = obj[0]
153 | print_obj(obj)
154 | else:
155 | print("object with label '{}' not found.".format(label))
156 |
157 |
158 | # ******************************************
159 | #
160 | # Main experimetns
161 | #
162 | # ******************************************
163 |
164 | doc = FreeCAD.open(
165 | "/home/stefan/mydata/github/blender/"
166 | "io_import_fcstd/"
167 | # "dev/freecad_linking_example/assembly.FCStd"
168 | # "dev/freecad_test_MyLittleWorld/MyLittleWorld.FCStd"
169 | "dev/freecad_test_body_objects/BodyTest.FCStd"
170 | )
171 | docname = doc.Name
172 |
173 | # ******************************************
174 | print("~"*42)
175 | objects = doc.Objects
176 | print("doc.Objects", len(objects))
177 | print_objects(objects)
178 | print("~"*42)
179 |
180 | print_obj_with_label(doc, "my_final_assembly")
181 | print_obj_with_label(doc, "octagon_part")
182 | print_obj_with_label(doc, "octagon_body")
183 |
184 | # t1 = doc.getObjectsByLabel("my_final_assembly")
185 | # t2 = doc.getObjectsByLabel("octagon_part")
186 | # t3 = doc.getObjectsByLabel("octagon_body")
187 |
188 |
189 | print("tests done :-)")
190 |
191 | FreeCAD.closeDocument(docname)
192 |
--------------------------------------------------------------------------------
/dev/macro_copySimpleExtended.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """create a simplifyed copy of selected objects."""
4 |
5 | # FreeCAD basics
6 | import FreeCAD
7 | import FreeCADGui
8 | import ImportGui
9 |
10 | # regex
11 | import re
12 |
13 | import time
14 | import os.path
15 |
16 | #####################
17 | # Begin command Part_SimpleCopy
18 | # doc = App.getDocument('lamp')
19 | # obj = doc.getObject('Body001')
20 | # __shape = Part.getShape(
21 | # obj,
22 | # '',
23 | # needSubElement=False,
24 | # refine=False
25 | # )
26 | # App.ActiveDocument.addObject('Part::Feature', 'Body001').Shape = __shape
27 | # App.ActiveDocument.ActiveObject.Label = obj.Label
28 | # obj_new = doc.getObject('Body001001')
29 | #
30 | # obj_new.ViewObject.ShapeColor = \
31 | # getattr(
32 | # obj.getLinkedObject(True).ViewObject,
33 | # 'ShapeColor',
34 | # obj_new.ViewObject.ShapeColor
35 | # )
36 | # obj_new.ViewObject.LineColor = \
37 | # getattr(
38 | # obj.getLinkedObject(True).ViewObject,
39 | # 'LineColor',
40 | # obj_new.ViewObject.LineColor
41 | # )
42 | # obj_new.ViewObject.PointColor = \
43 | # getattr(
44 | # obj.getLinkedObject(True).ViewObject,
45 | # 'PointColor',
46 | # obj_new.ViewObject.PointColor
47 | # )
48 | # App.ActiveDocument.recompute()
49 | # End command Part_SimpleCopy
50 | #####################
51 |
52 |
53 | def object_create_copy(obj_source):
54 | """Create a copy of an object."""
55 | obj_new = App.ActiveDocument.addObject(
56 | 'Part::Feature',
57 | obj_source.Name + "__sc_export"
58 | )
59 | __shape_refined = Part.getShape(
60 | obj_source,
61 | '',
62 | needSubElement=False,
63 | refine=False
64 | )
65 | obj_new.Shape = __shape_refined
66 | obj_new.Label = obj_source.Label + "__sc_export"
67 | print(obj_source)
68 |
69 | # AttributeError: 'Part.Feature' object has no attribute 'BoundingBox'
70 | obj_new.ViewObject.BoundingBox = obj_source.ViewObject.BoundingBox
71 | obj_new.ViewObject.Deviation = obj_source.ViewObject.Deviation
72 | obj_new.ViewObject.DisplayMode = obj_source.ViewObject.DisplayMode
73 | obj_new.ViewObject.DrawStyle = obj_source.ViewObject.DrawStyle
74 | obj_new.ViewObject.Lighting = obj_source.ViewObject.Lighting
75 | obj_new.ViewObject.LineColor = obj_source.ViewObject.LineColor
76 | obj_new.ViewObject.LineMaterial = obj_source.ViewObject.LineMaterial
77 | obj_new.ViewObject.LineWidth = obj_source.ViewObject.LineWidth
78 | obj_new.ViewObject.PointColor = obj_source.ViewObject.PointColor
79 | obj_new.ViewObject.PointMaterial = obj_source.ViewObject.PointMaterial
80 | obj_new.ViewObject.PointSize = obj_source.ViewObject.PointSize
81 | obj_new.ViewObject.Selectable = obj_source.ViewObject.Selectable
82 | obj_new.ViewObject.ShapeColor = obj_source.ViewObject.ShapeColor
83 | obj_new.ViewObject.ShapeMaterial = obj_source.ViewObject.ShapeMaterial
84 | obj_new.ViewObject.Transparency = obj_source.ViewObject.Transparency
85 | obj_new.ViewObject.Visibility = obj_source.ViewObject.Visibility
86 | return obj_new
87 |
88 |
89 | def find_Parent(obj):
90 | """Find Parent Part object for obj."""
91 | result_obj = None
92 | # this findes the 'last' Part..
93 | # but as fare as i know there should only be one in this list..
94 | for x in obj.InList:
95 | if (
96 | x.isDerivedFrom("App::Part")
97 | ):
98 | result_obj = x
99 | return result_obj
100 |
101 |
102 | def simpleCopySelection():
103 | """Create a simplifyed copy of selected objects."""
104 | # ideas / tests / original:
105 | # push into current group..
106 |
107 | App = FreeCAD
108 | Gui = FreeCADGui
109 |
110 | selection = FreeCADGui.Selection.getSelection()
111 |
112 | for obj in selection:
113 | obj_new = object_create_copy(obj)
114 | obj_new.ViewObject.Visibility = True
115 | obj.ViewObject.Visibility = False
116 | # try to add it at same tree location
117 | obj_parent = find_Parent(obj)
118 | if obj_parent:
119 | obj_parent.addObject(obj_new)
120 |
121 | #
122 |
123 | App.ActiveDocument.recompute()
124 | #
125 |
126 |
127 | # just do it:
128 | simpleCopySelection()
129 |
--------------------------------------------------------------------------------
/dev/script_test.blend:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s-light/io_import_fcstd/ace3fa082fd0b60721a8338680889fafb7c64024/dev/script_test.blend
--------------------------------------------------------------------------------
/dev/temp_python3_cmd_copyAndpaste.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import os
3 |
4 | path = "/usr/lib/freecad/lib"
5 | sys.path.append(path)
6 |
7 | import FreeCAD
8 | print("FreeCAD version:", FreeCAD.Version())
9 |
10 | path_base = FreeCAD.getResourceDir()
11 | path = os.path.join(path_base, "Mod")
12 | sys.path.append(path)
13 |
14 | doc = FreeCAD.open("./BodyTest_Minimal.FCStd")
15 | docname = doc.Name
16 | objects = FreeCAD.ActiveDocument.Objects
17 |
18 | print("doc.Objects", len(objects))
19 |
20 | for o in objects:
21 | print(o, o.Name)
22 |
23 | FreeCAD.closeDocument(docname)
24 |
--------------------------------------------------------------------------------
/freecad_helper.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 | """Helper tools for FreeCAD python scripts."""
5 |
6 |
7 | # import blender_helper as b_helper
8 |
9 |
10 | def print_obj_header(
11 | pre_line="", show_lists=False, show_list_details=False,
12 | ):
13 | """Print header for objects list."""
14 | print(
15 | pre_line + "{:<25} {:<15} {:<25}" "".format("Label", "Name", "TypeId"), end="",
16 | )
17 | if show_lists:
18 | print(
19 | "{:>2} {:>2} {:>2} {:>2} {:>2}" "".format("P", "I", "O", "G", "H",), end=""
20 | )
21 | if show_list_details:
22 | print(
23 | (" {:<30}" * 5).format(
24 | "[Parents]", "[InList]", "[OutList]", "[Group]", "[Hosts]",
25 | ),
26 | # b_helper.colors.fg.lightblue + "[Parents]",
27 | # b_helper.colors.fg.lightred + "[InList]",
28 | # b_helper.colors.fg.yellow + "[OutList]",
29 | # b_helper.colors.fg.pink + "[Group]",
30 | # b_helper.colors.fg.lightgreen + "[Hosts]",
31 | # )
32 | # + b_helper.colors.reset,
33 | end="",
34 | )
35 | print()
36 |
37 |
38 | def format_obj_show_lists(obj, show_list_details):
39 | result = ""
40 | group_count = "_"
41 | if hasattr(obj, "Group"):
42 | group_count = len(obj.Group)
43 | hosts_count = "_"
44 | if hasattr(obj, "Hosts"):
45 | hosts_count = len(obj.Hosts)
46 | result += "{:>2} {:>2} {:>2} {:>2} {:>2}" "".format(
47 | len(obj.Parents), len(obj.InList), len(obj.OutList), group_count, hosts_count,
48 | )
49 | if show_list_details:
50 | group = None
51 | if hasattr(obj, "Group"):
52 | group = obj.Group
53 | hosts = None
54 | if hasattr(obj, "Hosts"):
55 | hosts = obj.Hosts
56 | result += (" {:<30}" * 5).format(
57 | str(obj.Parents), str(obj.InList), str(obj.OutList), str(group), str(hosts),
58 | )
59 | # b_helper.colors.fg.lightblue + str(obj.Parents),
60 | # b_helper.colors.fg.lightred + str(obj.InList),
61 | # b_helper.colors.fg.yellow + str(obj.OutList),
62 | # b_helper.colors.fg.pink + str(group),
63 | # b_helper.colors.fg.lightgreen + str(hosts),
64 | # ) + b_helper.colors.reset
65 | return result
66 |
67 |
68 | def format_obj(
69 | obj, pre_line="", show_lists=False, show_list_details=False, tight_format=False
70 | ):
71 | """Print object nicely formated."""
72 | result = ""
73 | obj_label = "NONE"
74 | if obj:
75 | obj_label = obj.Label
76 | obj_name = "NONE"
77 | if obj:
78 | obj_name = obj.Name
79 | obj_type = "NONE"
80 | if obj:
81 | obj_type = obj.TypeId
82 | obj_format = "{:<25} {:<15} {:<25}"
83 | if tight_format:
84 | obj_format = "'{}' ('{}' <{}>)"
85 | result += pre_line + obj_format.format(obj_label, obj_name, obj_type)
86 | if show_lists:
87 | result += format_obj_show_lists(obj, show_list_details)
88 | return result
89 |
90 |
91 | def print_obj(
92 | obj, pre_line="", show_lists=False, show_list_details=False, end="\n",
93 | ):
94 | print(
95 | format_obj(
96 | obj=obj,
97 | pre_line=pre_line,
98 | show_lists=show_lists,
99 | show_list_details=show_list_details,
100 | ),
101 | end=end,
102 | )
103 |
104 |
105 | def print_objects(
106 | objects,
107 | pre_line="",
108 | pre_list_entry="* ",
109 | show_lists=False,
110 | show_list_details=False,
111 | ):
112 | """Print objects list."""
113 | pre_list_entry_space = " " * len(pre_list_entry)
114 | print_obj_header(
115 | pre_line=pre_line + pre_list_entry_space,
116 | show_lists=show_lists,
117 | show_list_details=show_list_details,
118 | )
119 | for index, obj in enumerate(objects):
120 | # line_color = b_helper.colors.reset
121 | # if index % 2:
122 | # # line_color = b_helper.colors.bg.lightgrey + b_helper.colors.fg.black
123 | # line_color = b_helper.colors.bg.black
124 | print_obj(
125 | obj,
126 | pre_line=pre_line + pre_list_entry,
127 | # pre_line=pre_line + pre_list_entry + line_color,
128 | show_lists=show_lists,
129 | show_list_details=show_list_details,
130 | # end=b_helper.colors.reset + "\n",
131 | )
132 |
133 |
134 | # ****************************************
135 |
136 |
137 | def filtered_objects(objects, typeid_filter_list=None, include_only_visible=False):
138 | """Filter list of objects."""
139 | if typeid_filter_list is None:
140 | typeid_filter_list = [
141 | "App::Line",
142 | "App::Plane",
143 | "App::Origin",
144 | # 'GeoFeature',
145 | # 'PartDesign::CoordinateSystem',
146 | # 'Sketcher::SketchObject',
147 | ]
148 | result_objects = []
149 | for obj in objects:
150 | if obj.TypeId not in typeid_filter_list:
151 | if include_only_visible:
152 | if hasattr(obj, "Visibility"):
153 | if obj.Visibility:
154 | result_objects.append(obj)
155 | else:
156 | print(
157 | "filtered_objects: "
158 | "obj '{}' has no Visibility attribute."
159 | "it is excluded from results."
160 | "".format(obj.Name)
161 | )
162 | else:
163 | result_objects.append(obj)
164 | return result_objects
165 |
166 |
167 | def get_filtered_objects(doc, typeid_filter_list=None):
168 | """Get filterd list of objects."""
169 | result_objects = filtered_objects(doc.Objects, typeid_filter_list)
170 | return result_objects
171 |
172 |
173 | def get_root_objects(doc, filter_list=[]):
174 | """Get root list of objects."""
175 | typeid_filter_list = [
176 | "App::Line",
177 | "App::Plane",
178 | "App::Origin",
179 | ]
180 | typeid_filter_list = typeid_filter_list + filter_list
181 | result_objects = []
182 | result_objects_withHost = []
183 | for obj in doc.Objects:
184 | if obj.TypeId not in typeid_filter_list:
185 | # if len(obj.Parents) == 0 and len(object_get_HostChilds(obj) == 0):
186 | # result_objects.append(obj)
187 | if len(obj.Parents) == 0:
188 | if hasattr(obj, "Hosts"):
189 | if len(obj.Hosts) == 0:
190 | result_objects.append(obj)
191 | else:
192 | result_objects_withHost.append(obj)
193 | else:
194 | result_objects.append(obj)
195 | return result_objects, result_objects_withHost
196 |
197 |
198 | def object_get_HostChilds(obj):
199 | """Return List of Objects that have set Host(s) to this object."""
200 | # source:
201 | # FreeCAD/src/Mod/Arch/ArchComponent.py
202 | # https://github.com/FreeCAD/FreeCAD/blob/master/src/Mod/Arch/ArchComponent.py#L1109
203 | # def getHosts(self,obj)
204 | hosts = []
205 |
206 | for link in obj.InListRecursive:
207 | if hasattr(link, "Host"):
208 | if link.Host:
209 | if link.Host == obj:
210 | hosts.append(link)
211 | elif hasattr(link, "Hosts"):
212 | if link.Hosts:
213 | if obj in link.Hosts:
214 | hosts.append(link)
215 | return hosts
216 |
217 |
218 | # ******************************************
219 | # `is_toplevel_in_list` and `get_toplevel_objects`
220 | # from forum post 'Get highest objects of model' by kbwbe
221 | # https://forum.freecadweb.org/viewtopic.php?p=338214&sid=a6dd59fe66c1d807f8537f192fdb14dc#p338214
222 |
223 |
224 | def is_toplevel_in_list(lst):
225 | """Check if objects in list are at top level."""
226 | if len(lst) == 0:
227 | return True
228 | for ob in lst:
229 | if ob.Name.startswith("Clone"):
230 | continue
231 | if ob.Name.startswith("Part__Mirroring"):
232 | continue
233 | else:
234 | return False
235 | return True
236 |
237 |
238 | def get_toplevel_objects(doc):
239 | """Get top level list of objects."""
240 | topLevelShapes = []
241 | for ob in doc.Objects:
242 | if is_toplevel_in_list(ob.InList):
243 | topLevelShapes.append(ob)
244 | else:
245 | numBodies = 0
246 | numClones = 0
247 | invalidObjects = False
248 | # perhaps pairs of Clone/Bodies
249 | if len(ob.InList) % 2 == 0:
250 | for o in ob.InList:
251 | if o.Name.startswith("Clone"):
252 | numClones += 1
253 | elif o.Name.startswith("Body"):
254 | numBodies += 1
255 | else:
256 | invalidObjects = True
257 | break
258 | if not invalidObjects:
259 | if numBodies == numClones:
260 | topLevelShapes.append(ob.Name)
261 | return topLevelShapes
262 |
--------------------------------------------------------------------------------
/import_fcstd/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 | """Import FreeCAD files to blender."""
5 |
6 | import sys
7 | import bpy
8 | import os
9 | import math
10 |
11 | # import pprint
12 |
13 | from .. import freecad_helper as fc_helper
14 | from .. import blender_helper as b_helper
15 |
16 | from . import helper
17 | from . import guidata
18 | from .material import MaterialManager
19 |
20 |
21 | # set to True to triangulate all faces (will loose multimaterial info)
22 | TRIANGULATE = False
23 |
24 |
25 | class ImportFcstd(object):
26 | """Import fcstd files."""
27 |
28 | def __init__(
29 | self,
30 | *, # this forces named_properties..
31 | # filename=None,
32 | update=True,
33 | update_only_modified_meshes=True,
34 | placement=True,
35 | scale=0.001,
36 | tessellation=0.10,
37 | auto_smooth_use=True,
38 | auto_smooth_angle=math.radians(85),
39 | skiphidden=True,
40 | filter_sketch=True,
41 | sharemats=True,
42 | update_materials=False,
43 | obj_name_prefix="",
44 | obj_name_prefix_with_filename=False,
45 | links_as_collectioninstance=True,
46 | path_to_freecad=None,
47 | path_to_system_packages=None,
48 | report=None,
49 | ):
50 | """Init."""
51 | super(ImportFcstd, self).__init__()
52 | self.config = {
53 | "filename": None,
54 | "update": update,
55 | "update_only_modified_meshes": update_only_modified_meshes,
56 | "placement": placement,
57 | "tessellation": tessellation,
58 | "auto_smooth_use": auto_smooth_use,
59 | "auto_smooth_angle": auto_smooth_angle,
60 | "skiphidden": skiphidden,
61 | "filter_sketch": filter_sketch,
62 | "scale": scale,
63 | "sharemats": sharemats,
64 | "update_materials": update_materials,
65 | "obj_name_prefix_with_filename": obj_name_prefix_with_filename,
66 | "obj_name_prefix": obj_name_prefix,
67 | "links_as_collectioninstance": links_as_collectioninstance,
68 | "report": self.print_report,
69 | }
70 | self.path_to_freecad = path_to_freecad
71 | self.path_to_system_packages = path_to_system_packages
72 | self.report = report
73 |
74 | print("config", self.config)
75 | self.doc = None
76 | self.doc_filename = None
77 | self.guidata = {}
78 |
79 | self.fcstd_collection = None
80 | self.link_targets = None
81 | self.fcstd_empty = None
82 |
83 | self.imported_obj_names = []
84 |
85 | self.typeid_filter_list = [
86 | "GeoFeature",
87 | "PartDesign::CoordinateSystem",
88 | ]
89 | if self.config["filter_sketch"]:
90 | self.typeid_filter_list.append("Sketcher::SketchObject")
91 |
92 | def print_report(self, mode, data, pre_line=""):
93 | """Multi print handling."""
94 | b_helper.print_multi(
95 | mode=mode, data=data, pre_line=pre_line, report=self.report,
96 | )
97 |
98 | def format_obj(self, obj, pre_line="", post_line=""):
99 | """Print object with nice formating."""
100 | message = ""
101 |
102 | message = fc_helper.format_obj(
103 | obj=obj,
104 | pre_line=pre_line,
105 | show_lists=False,
106 | show_list_details=False,
107 | tight_format=True,
108 | )
109 | message += post_line
110 | return message
111 |
112 | def print_obj(self, obj, pre_line="", post_line="", end="\n"):
113 | """Print object with nice formating."""
114 | message = ""
115 |
116 | message = self.format_obj(obj=obj)
117 | message += post_line
118 | # print(message, end=end)
119 | self.config["report"]({"INFO"}, message, pre_line)
120 |
121 | def print_debug_report(self):
122 | """print out some minimal debug things.."""
123 | print("print_debug_report")
124 | import FreeCAD
125 | print("FreeCAD version:", FreeCAD.Version())
126 | objects = FreeCAD.ActiveDocument.Objects
127 | print("doc.Objects", len(objects))
128 | for o in objects:
129 | print(o, o.Name)
130 |
131 | def handle_label_prefix(self, label):
132 | """Handle all label prefix processing."""
133 | if label:
134 | prefix = self.config["obj_name_prefix"]
135 | if self.config["obj_name_prefix_with_filename"]:
136 | prefix = self.doc.Name + "__" + prefix
137 | if prefix:
138 | label = prefix + "__" + label
139 | return label
140 |
141 | def get_obj_label(self, obj):
142 | """Get object label with optional prefix."""
143 | label = None
144 | if obj:
145 | # obj_label = "NONE"
146 | # obj_label = obj.Label
147 | # label = obj_label
148 | label = obj.Label
149 | label = self.handle_label_prefix(label)
150 | return label
151 |
152 | def get_obj_link_target_label(self, obj):
153 | """Get object label with optional prefix."""
154 | label = None
155 | if obj:
156 | label = obj.Label + "__lt"
157 | label = self.handle_label_prefix(label)
158 | return label
159 |
160 | def get_obj_linkedobj_label(self, obj):
161 | """Get linkedobject label with optional prefix."""
162 | label = None
163 | if hasattr(obj, "LinkedObject"):
164 | label = "NONE"
165 | if obj.LinkedObject:
166 | label = obj.LinkedObject.Label
167 | label = self.handle_label_prefix(label)
168 | return label
169 |
170 | def get_obj_combined_label(self, parent_obj, obj):
171 | """Get object label with optional prefix."""
172 | label = None
173 | obj_label = "NONE"
174 | parent_obj_label = "NONE"
175 | if obj:
176 | obj_label = obj.Label
177 | if parent_obj:
178 | parent_obj_label = parent_obj.Label
179 | label = parent_obj_label + "." + obj_label
180 | label = self.handle_label_prefix(label)
181 | return label
182 |
183 | # def get_sub_obj_label(self, pre_line, func_data, parent_obj, obj):
184 | def get_sub_obj_label(self, pre_line, func_data, obj):
185 | """Get sub object label."""
186 | # print(
187 | # pre_line
188 | # + "get_sub_obj_label"
189 | # )
190 | # pre_line += ". "
191 | # print(
192 | # pre_line
193 | # + "func_data['obj_label']: "
194 | # + b_helper.colors.fg.orange
195 | # + "'{}'".format(func_data["obj_label"])
196 | # + b_helper.colors.reset
197 | # )
198 | # print(
199 | # pre_line
200 | # + "func_data['link_source']: "
201 | # + b_helper.colors.fg.orange
202 | # + self.format_obj(func_data["link_source"])
203 | # + b_helper.colors.reset
204 | # )
205 | # print(
206 | # pre_line
207 | # + "parent_obj "
208 | # + b_helper.colors.fg.orange
209 | # + self.format_obj(parent_obj)
210 | # + b_helper.colors.reset
211 | # )
212 | # print(
213 | # pre_line
214 | # + " obj "
215 | # + b_helper.colors.fg.orange
216 | # + self.format_obj(obj)
217 | # + b_helper.colors.reset
218 | # )
219 | # obj_label = self.get_obj_combined_label(parent_obj, obj)
220 | # if func_data["obj_label"]:
221 | # obj_label = (
222 | # func_data["obj_label"]
223 | # + "."
224 | # + obj_label
225 | # )
226 | obj_label = self.get_obj_label(obj)
227 | if func_data["link_source"]:
228 | obj_label = (
229 | # func_data["obj_label"]
230 | self.get_obj_label(func_data["link_source"])
231 | + "."
232 | + obj_label
233 | )
234 | return obj_label
235 |
236 | def fix_link_target_name(self, bobj):
237 | """Fix name of link target object."""
238 | bobj.name = bobj.name + "__lt"
239 | return bobj.name
240 |
241 | def check_obj_visibility(self, obj):
242 | """Check if obj is visible."""
243 | result = True
244 | if obj.Name in self.guidata and "Visibility" in self.guidata[obj.Name]:
245 | if self.guidata[obj.Name]["Visibility"] is False:
246 | result = False
247 | return result
248 |
249 | def check_obj_visibility_with_skiphidden(self, obj, obj_visibility=None):
250 | """Check if obj is visible."""
251 | result = True
252 | if self.config["skiphidden"]:
253 | # print("obj_visibility: '{}'".format(obj_visibility))
254 | if obj_visibility is not None:
255 | result = obj_visibility
256 | else:
257 | result = self.check_obj_visibility(obj)
258 | return result
259 |
260 | def check_collections_for_bobj(self, bobj):
261 | """Search all collections for given bobj."""
262 | found_in_collections = None
263 | for col in bpy.data.collections:
264 | if bobj.name in col.objects:
265 | if found_in_collections is None:
266 | found_in_collections = []
267 | found_in_collections.append(col.name)
268 | return found_in_collections
269 |
270 | # ##########################################
271 | # object handling
272 |
273 | def hascurves(self, shape):
274 | """Check if shape has curves."""
275 | import Part
276 |
277 | for e in shape.Edges:
278 | if not isinstance(e.Curve, (Part.Line, Part.LineSegment)):
279 | return True
280 | return False
281 |
282 | def handle_placement(
283 | self,
284 | pre_line,
285 | obj,
286 | bobj,
287 | # enable_import_scale=False,
288 | enable_scale=True,
289 | relative=False,
290 | negative=False,
291 | ):
292 | """Handle placement."""
293 | if self.config["placement"]:
294 | # print(pre_line)
295 | # print(pre_line + " §§§ §§§ handle_placement: '{}'".format(bobj.name))
296 | # print(pre_line)
297 | new_loc = obj.Placement.Base * self.config["scale"]
298 | # attention: multiply does in-place change.
299 | # so if you call it multiple times on the same value
300 | # you get really strange results...
301 | # new_loc = obj.Placement.Base.multiply(self.config["scale"])
302 | if relative:
303 | # print(
304 | # "x: {} + {} = {}"
305 | # "".format(
306 | # bobj.location.x,
307 | # new_loc.x,
308 | # bobj.location.x + new_loc.x
309 | # )
310 | # )
311 | if negative:
312 | bobj.location.x = bobj.location.x - new_loc.x
313 | bobj.location.y = bobj.location.y - new_loc.y
314 | bobj.location.z = bobj.location.z - new_loc.z
315 | else:
316 | bobj.location.x = bobj.location.x + new_loc.x
317 | bobj.location.y = bobj.location.y + new_loc.y
318 | bobj.location.z = bobj.location.z + new_loc.z
319 | else:
320 | bobj.location = new_loc
321 | m = bobj.rotation_mode
322 | bobj.rotation_mode = "QUATERNION"
323 | if obj.Placement.Rotation.Angle:
324 | # FreeCAD Quaternion is XYZW while Blender is WXYZ
325 | q = (obj.Placement.Rotation.Q[3],) + obj.Placement.Rotation.Q[:3]
326 | bobj.rotation_quaternion = q
327 | bobj.rotation_mode = m
328 | if enable_scale and ("Scale" in obj.PropertiesList):
329 | # object has Scale property so lets use it :-)
330 | bobj.scale = bobj.scale * obj.Scale
331 |
332 | def reset_placement_position(self, bobj):
333 | """Reset placement position."""
334 | bobj.location.x = 0
335 | bobj.location.y = 0
336 | bobj.location.z = 0
337 |
338 | def update_tree_collections(self, func_data):
339 | """Update object tree."""
340 | pre_line = func_data["pre_line"]
341 | bobj = func_data["bobj"]
342 | # col = self.check_collections_for_bobj(bobj)
343 | if func_data["collection"]:
344 | add_to_collection = False
345 | if self.config["update"]:
346 | if bobj.name not in func_data["collection"].objects:
347 | add_to_collection = True
348 | else:
349 | # print(
350 | # pre_line +
351 | # "'{}' already in collection '{}'"
352 | # "".format(bobj.name, func_data["collection"])
353 | # )
354 | pass
355 | else:
356 | add_to_collection = True
357 |
358 | if add_to_collection:
359 | func_data["collection"].objects.link(bobj)
360 | # print(
361 | # pre_line +
362 | # "'{}' add (tree_collections) to '{}' "
363 | # "".format(bobj, func_data["collection"])
364 | # )
365 | if not self.check_collections_for_bobj(bobj):
366 | # link to import collection - so that the object is visible.
367 | collection = self.fcstd_collection
368 | collection.objects.link(bobj)
369 | print(
370 | pre_line + "'{}' add (tree_parents) to '{}' "
371 | "".format(bobj, collection)
372 | )
373 |
374 | def update_tree_parents(self, func_data):
375 | """Update object tree."""
376 | pre_line = func_data["pre_line"]
377 | bobj = func_data["bobj"]
378 | # print(pre_line + "update_tree_parents")
379 | # print(pre_line + " bobj.parent '{}'".format(bobj.parent))
380 | # print(
381 | # pre_line + " func_data[parent_bobj] '{}'".format(func_data["parent_bobj"])
382 | # )
383 | if bobj.parent is None and func_data["parent_bobj"] is not None:
384 | print(
385 | pre_line + "update_tree_parents" + " obj '{}' set parent to '{}' "
386 | "".format(bobj, func_data["parent_bobj"])
387 | )
388 | # print(
389 | # pre_line + " obj '{}' set parent to '{}' "
390 | # "".format(bobj, func_data["parent_bobj"])
391 | # )
392 | bobj.parent = func_data["parent_bobj"]
393 | # TODO: check 'update'
394 |
395 | def create_bmesh_from_func_data(
396 | self, func_data, obj_label, enable_import_scale=True
397 | ):
398 | """Create new object from bmesh."""
399 | bmesh = bpy.data.meshes.new(name=obj_label)
400 | bmesh.from_pydata(func_data["verts"], func_data["edges"], func_data["faces"])
401 | bmesh.update()
402 | # handle import scalling
403 | if enable_import_scale:
404 | scale = self.config["scale"]
405 | for v in bmesh.vertices:
406 | v.co *= scale
407 | bmesh.update()
408 | bmesh["freecad_mesh_hash"] = func_data["freecad_mesh_hash"]
409 | return bmesh
410 |
411 | def create_bobj_from_bmesh(self, func_data, obj_label, bmesh):
412 | """Create new object from bmesh."""
413 | bobj = bpy.data.objects.new(obj_label, bmesh)
414 | # check if we already used the bmesh.
415 | # if bmesh.name in bpy.data.meshes:
416 | # print(
417 | # func_data["pre_line"] +
418 | # " ignore material import. mesh already existed."
419 | # )
420 | # else:
421 | if len(bmesh.materials) <= 0:
422 | material_manager = MaterialManager(
423 | guidata=self.guidata,
424 | func_data=func_data,
425 | bobj=bobj,
426 | obj_label=obj_label,
427 | sharemats=self.config["sharemats"],
428 | report=self.config["report"],
429 | report_preline=func_data["pre_line"] + "| ",
430 | )
431 | material_manager.create_new()
432 | else:
433 | print(
434 | func_data["pre_line"]
435 | + " ignore material import. mesh already has material."
436 | )
437 | func_data["bobj"] = bobj
438 | return bobj
439 |
440 | def create_or_get_bmesh(self, pre_line, func_data, mesh_label):
441 | """Create or get bmesh."""
442 | pre_line_orig = func_data["pre_line"]
443 | print(pre_line_orig + "create_or_get_bmesh")
444 | pre_line = pre_line_orig + " "
445 | func_data["pre_line"] = pre_line
446 |
447 | bmesh = None
448 | # bmesh_old_name = None
449 | bmesh_import = True
450 |
451 | print(pre_line + "mesh_label:", mesh_label)
452 | # print(pre_line + "bpy.data.meshes ({})".format(len(bpy.data.meshes)))
453 | # for mesh in bpy.data.meshes:
454 | # print(pre_line + " - ", mesh)
455 | if mesh_label in bpy.data.meshes:
456 | bmesh = bpy.data.meshes[mesh_label]
457 | print(pre_line + "use found bmesh.")
458 | bmesh_import = False
459 | # print(
460 | # pre_line
461 | # + "bmesh.freecad_mesh_hash ",
462 | # bmesh.get("freecad_mesh_hash", None)
463 | # )
464 | # print(
465 | # pre_line
466 | # + "func_data[freecad_mesh_hash] ",
467 | # func_data["freecad_mesh_hash"]
468 | # )
469 | # print(pre_line + "mesh_label", mesh_label)
470 | # print(pre_line + "self.imported_obj_names")
471 | # for obj_name in self.imported_obj_names:
472 | # print(pre_line + " - ", obj_name)
473 | if mesh_label not in self.imported_obj_names and self.config["update"]:
474 | if self.config["update_only_modified_meshes"]:
475 | print(pre_line + "update_only_modified_meshes: TODO")
476 | # bmesh.get("freecad_mesh_hash", None)
477 | # func_data["freecad_mesh_hash"]
478 | # rename old mesh -
479 | # this way the new mesh can get the original name.
480 | helper.rename_old_data(bpy.data.meshes, mesh_label)
481 | # bmesh_old_name = helper.rename_old_data(bpy.data.meshes, mesh_label)
482 | bmesh_import = True
483 | # create bmesh
484 | if bmesh_import:
485 | print(pre_line + "import bmesh.")
486 | bmesh = self.create_bmesh_from_func_data(
487 | func_data, mesh_label, enable_import_scale=True
488 | )
489 | # print(pre_line + "create_bmesh_from_func_data: ", bmesh)
490 | print(
491 | pre_line + "set auto_smooth: ({}) '{}°'"
492 | "".format(
493 | self.config["auto_smooth_use"],
494 | math.degrees(self.config["auto_smooth_angle"]),
495 | )
496 | )
497 | bmesh.use_auto_smooth = self.config["auto_smooth_use"]
498 | bmesh.auto_smooth_angle = self.config["auto_smooth_angle"]
499 | if self.config["auto_smooth_use"]:
500 | for f in bmesh.polygons:
501 | f.use_smooth = True
502 | if mesh_label not in self.imported_obj_names:
503 | self.imported_obj_names.append(mesh_label)
504 | # return (bmesh, bmesh_old_name)
505 | func_data["pre_line"] = pre_line_orig
506 | return bmesh
507 |
508 | def create_or_update_bobj(self, pre_line, func_data, obj_label, bmesh):
509 | """Create or update bobj."""
510 | pre_line_orig = func_data["pre_line"]
511 | print(pre_line_orig + "create_or_update_bobj")
512 | pre_line = pre_line_orig + " "
513 | func_data["pre_line"] = pre_line
514 | bobj = None
515 | is_new = False
516 | bobj_import = True
517 | # locate existing object (object with same name)
518 | if obj_label in bpy.data.objects:
519 | bobj = bpy.data.objects[obj_label]
520 | print(pre_line + "found bobj!")
521 | bobj_import = False
522 | if obj_label not in self.imported_obj_names and self.config["update"]:
523 | print(
524 | pre_line + "Replacing existing object mesh: {}" "".format(obj_label)
525 | )
526 | # update only the mesh of existing object.
527 | # print(self.imported_obj_names)
528 | if len(bmesh.materials) <= 0:
529 | # TODO: fix this!!
530 | # correctly handle multimaterials
531 | # copy old materials to new mesh:
532 | for mat in bobj.data.materials:
533 | bmesh.materials.append(mat)
534 | bobj.data = bmesh
535 | # self.handle_material_update(func_data, bobj)
536 | bobj_import = False
537 | # create bobj
538 | if bobj_import:
539 | # print(
540 | # pre_line +
541 | # "create_bobj_from_bmesh: '{}'"
542 | # "".format(obj_label)
543 | # )
544 | bobj = self.create_bobj_from_bmesh(func_data, obj_label, bmesh)
545 | is_new = True
546 | # print(
547 | # pre_line +
548 | # "created new bobj: {}"
549 | # "".format(bobj)
550 | # )
551 | func_data["pre_line"] = pre_line_orig
552 | return (is_new, bobj)
553 |
554 | def add_or_update_blender_obj(self, func_data):
555 | """Create or update object with mesh and material data."""
556 | """
557 | What should happen?
558 | check if we have the mesh already
559 | if not create
560 | check if we have the object already
561 | if not create it
562 | """
563 | pre_line_orig = func_data["pre_line"]
564 | print(pre_line_orig + "add_or_update_blender_obj")
565 | pre_line = pre_line_orig + " "
566 | func_data["pre_line"] = pre_line
567 |
568 | obj_label = self.get_obj_label(func_data["obj"])
569 | mesh_label = obj_label
570 | if func_data["is_link"] and func_data["obj_label"]:
571 | obj_label = func_data["obj_label"]
572 |
573 | # print(pre_line + "obj_label", obj_label)
574 | # print(pre_line + "mesh_label", mesh_label)
575 | # print(pre_line + "obj", self.format_obj(func_data["obj"]))
576 |
577 | bmesh = self.create_or_get_bmesh(pre_line, func_data, mesh_label)
578 |
579 | is_new, bobj = self.create_or_update_bobj(pre_line, func_data, obj_label, bmesh)
580 |
581 | if self.config["update"] or is_new:
582 | # if func_data["obj"].isDerivedFrom("Part::Feature"):
583 | # print(pre_line + " obj isDerivedFrom Part::Feature")
584 | # if func_data["obj"].isDerivedFrom("App::Part"):
585 | # print(pre_line + " obj isDerivedFrom App::Part")
586 | # if func_data["parent_obj"].isDerivedFrom("Part::Feature"):
587 | # print(pre_line + "parent_obj isDerivedFrom Part::Feature")
588 | # if func_data["parent_obj"].isDerivedFrom("App::Part"):
589 | # print(pre_line + "parent_obj isDerivedFrom App::Part")
590 |
591 | if func_data["is_link"]:
592 | # print(pre_line + "is link")
593 | if func_data["obj"].isDerivedFrom("Part::Feature") and func_data[
594 | "parent_obj"
595 | ].isDerivedFrom("App::Part"):
596 | # print(
597 | # pre_line +
598 | # "is_link "
599 | # "&& obj is Part::Feature "
600 | # "&& parent_obj is App::Part "
601 | # )
602 | self.handle_placement(pre_line, func_data["obj"], bobj)
603 | else:
604 | # print(pre_line + "is not link")
605 | self.handle_placement(pre_line, func_data["obj"], bobj)
606 |
607 | if bobj.name not in self.imported_obj_names:
608 | self.imported_obj_names.append(bobj.name)
609 | func_data["bobj"] = bobj
610 | func_data["pre_line"] = pre_line_orig
611 |
612 | def sub_collection_add_or_update(self, func_data, collection_label):
613 | """Part-Collection handle add or update."""
614 | print(
615 | func_data["pre_line"]
616 | + "sub_collection_add_or_update: '{}'".format(collection_label)
617 | )
618 | temp_collection = None
619 | if self.config["update"]:
620 | if collection_label in bpy.data.collections:
621 | temp_collection = bpy.data.collections[collection_label]
622 | else:
623 | helper.rename_old_data(bpy.data.collections, collection_label)
624 |
625 | if not temp_collection:
626 | # create new
627 | temp_collection = bpy.data.collections.new(collection_label)
628 | func_data["collection"].children.link(temp_collection)
629 | print(
630 | func_data["pre_line"] + "'{}' add to '{}' "
631 | "".format(func_data["bobj"], func_data["collection"],)
632 | )
633 | else:
634 | # bpy.context.scene.collection.children.link(self.fcstd_collection)
635 | pass
636 |
637 | # update func_data links
638 | func_data["collection_parent"] = func_data["collection"]
639 | func_data["collection"] = temp_collection
640 |
641 | def set_obj_parent_and_collection(self, pre_line, func_data, bobj):
642 | """Set Object parent and collection."""
643 | bobj.parent = func_data["parent_bobj"]
644 | print(
645 | pre_line + "'{}' set parent to '{}' "
646 | "".format(bobj, func_data["parent_bobj"])
647 | )
648 |
649 | # add object to current collection
650 | collection = func_data["collection"]
651 | if not collection:
652 | collection = self.fcstd_collection
653 | if bobj.name not in collection.objects:
654 | collection.objects.link(bobj)
655 | # print(
656 | # pre_line +
657 | # "'{}' add to '{}' "
658 | # "".format(bobj, collection)
659 | # )
660 |
661 | def parent_empty_add_or_update(self, func_data, empty_label):
662 | """Parent Empty handle add or update."""
663 | print(
664 | func_data["pre_line"]
665 | + "parent_empty_add_or_update: '{}'".format(empty_label)
666 | )
667 | pre_line = func_data["pre_line"] + " → "
668 | empty_bobj = None
669 |
670 | obj = func_data["obj"]
671 |
672 | print(
673 | pre_line + "current parent_obj ", self.format_obj(func_data["parent_obj"])
674 | )
675 |
676 | if empty_label in bpy.data.objects:
677 | # print(
678 | # pre_line +
679 | # "'{}' already in objects list.".format(empty_label)
680 | # )
681 | if self.config["update"]:
682 | empty_bobj = bpy.data.objects[empty_label]
683 | # print(
684 | # pre_line +
685 | # "update: '{}'".format(empty_bobj)
686 | # )
687 | else:
688 | renamed_to = helper.rename_old_data(bpy.data.objects, empty_label)
689 | print(pre_line + "overwrite - renamed to " "'{}'".format(renamed_to))
690 |
691 | flag_new = False
692 | if empty_bobj is None:
693 | print(pre_line + "create new empty_bobj '{}'".format(empty_label))
694 | empty_bobj = bpy.data.objects.new(name=empty_label, object_data=None)
695 | empty_bobj.empty_display_size = self.config["scale"] * 10
696 | self.set_obj_parent_and_collection(pre_line, func_data, empty_bobj)
697 |
698 | if self.config["update"] or flag_new:
699 | # set position of empty
700 | if obj:
701 | self.handle_placement(
702 | pre_line, obj, empty_bobj,
703 | )
704 | # NOT HERE.
705 | # if not func_data["is_link"]:
706 | # self.handle_placement(
707 | # pre_line,
708 | # obj,
709 | # empty_bobj,
710 | # )
711 | #
712 | # TODO: handle origin things corrrectly...
713 | # origin_bobj
714 | # getLinkedObject()
715 | # >>> doc.Link006.getLinkedObject().Label
716 | # 'Seagull_Double'
717 | # >>> doc.Link003.getLinkedObject().Label
718 | # 'Seagull_A1'
719 | #
720 | # if (
721 | # not func_data["is_link"]
722 | # ):
723 | # self.handle_placement(
724 | # obj,
725 | # empty_bobj,
726 | # )
727 | # print(
728 | # pre_line +
729 | # "'{}' set position"
730 | # "".format(empty_bobj)
731 | # # "'{}' set position to '{}'"
732 | # # "".format(empty_bobj, position)
733 | # )
734 |
735 | # update func_data links
736 | func_data["parent_obj"] = obj
737 | func_data["parent_bobj"] = empty_bobj
738 | return empty_bobj
739 |
740 | def create_collection_instance(
741 | self, func_data, pre_line, obj_label, base_collection
742 | ):
743 | """Create instance of given collection."""
744 | result_bobj = bpy.data.objects.new(name=obj_label, object_data=None)
745 | result_bobj.instance_collection = base_collection
746 | result_bobj.instance_type = "COLLECTION"
747 | result_bobj.empty_display_size = self.config["scale"] * 10
748 |
749 | # TODO: CHECK where to add this!
750 | if func_data["collection"]:
751 | func_data["collection"].objects.link(result_bobj)
752 | print(
753 | pre_line + "'{}' add to '{}' "
754 | "".format(result_bobj, func_data["collection"])
755 | )
756 | # result_bobj.parent = func_data["parent_bobj"]
757 | # result_bobj.parent = parent_obj
758 | if result_bobj.name in bpy.context.scene.collection.objects:
759 | bpy.context.scene.collection.objects.unlink(result_bobj)
760 |
761 | return result_bobj
762 |
763 | def create_link_instance(
764 | self, func_data, pre_line, obj_label, link_target_bobj, link_target_obj
765 | ):
766 | """Create instance of given link_target_bobj."""
767 | result_bobj = None
768 | if link_target_obj.isDerivedFrom("Part::Feature"):
769 | object_data = None
770 | if link_target_bobj:
771 | object_data = link_target_bobj.data
772 | else:
773 | object_data = bpy.data.meshes.new(name=obj_label + ".temp")
774 | result_bobj = bpy.data.objects.new(name=obj_label, object_data=object_data)
775 | result_bobj.empty_display_size = self.config["scale"] * 10
776 | else:
777 | self.config["report"](
778 | {"WARNING"},
779 | (" TODO: create_link_instance " "part handling not implemented yet!!"),
780 | pre_line,
781 | )
782 |
783 | if link_target_bobj:
784 | result_bobj.scale = link_target_bobj.scale
785 | # check if we need to create link children...
786 | if link_target_obj.children:
787 | self.config["report"](
788 | {"WARNING"},
789 | (
790 | " Warning: create_link_instance "
791 | "children handling not implemented yet!!"
792 | ),
793 | pre_line,
794 | )
795 | return result_bobj
796 |
797 | def handle__sub_object_import(
798 | self,
799 | *,
800 | func_data,
801 | obj,
802 | pre_line,
803 | parent_obj,
804 | parent_bobj,
805 | is_link_source=False,
806 | ):
807 | """Handle sub object."""
808 | pre_line_orig = func_data["pre_line"]
809 | print(pre_line_orig + "handle__sub_object_import")
810 | pre_line = pre_line_orig + " "
811 | func_data["pre_line"] = pre_line
812 | link_source = None
813 | obj_label = self.get_obj_label(obj)
814 | # print(pre_line + "obj_label: " + obj_label)
815 | if func_data["is_link"]:
816 | obj_label = self.get_sub_obj_label(
817 | pre_line,
818 | func_data,
819 | # parent_obj,
820 | obj,
821 | )
822 | print(
823 | pre_line
824 | + "set special link obj_label: "
825 | + b_helper.colors.fg.green
826 | + "'{}'".format(obj_label)
827 | + b_helper.colors.reset
828 | )
829 | link_source = func_data["link_source"]
830 | if is_link_source:
831 | link_source = obj
832 | # debug output
833 | print(pre_line + ("*" * 42))
834 | print(pre_line + "obj: " + self.format_obj(obj))
835 | print(pre_line + "parent_obj: " + self.format_obj(parent_obj))
836 | print(pre_line + "parent_bobj: {}".format(parent_bobj))
837 | # # print(pre_line + "func_data[is_link]: {}".format(func_data["is_link"]))
838 | # is_link_color = b_helper.colors.fg.red
839 | # if func_data["is_link"]:
840 | # is_link_color = b_helper.colors.fg.green
841 | # print(
842 | # pre_line
843 | # + "func_data[is_link]: "
844 | # + is_link_color
845 | # + "{}".format(func_data["is_link"])
846 | # + b_helper.colors.reset
847 | # )
848 | # is_link_source_color = b_helper.colors.fg.red
849 | # if is_link_source:
850 | # is_link_source_color = b_helper.colors.fg.green
851 | # print(
852 | # pre_line
853 | # + "is_link_source: "
854 | # + is_link_source_color
855 | # + "{}".format(is_link_source)
856 | # + b_helper.colors.reset
857 | # )
858 | # print(
859 | # pre_line
860 | # + "func_data[link_source]: "
861 | # + self.format_obj(func_data["link_source"])
862 | # )
863 | # print(
864 | # pre_line
865 | # + "link_source: "
866 | # + self.format_obj(link_source)
867 | # )
868 | # print(pre_line + ("*"*42))
869 | # prepare import
870 | func_data_new = self.create_func_data()
871 | func_data_new["obj"] = obj
872 | func_data_new["obj_label"] = obj_label
873 | func_data_new["collection"] = func_data["collection"]
874 | func_data_new["collection_parent"] = func_data["collection_parent"]
875 | func_data_new["parent_obj"] = parent_obj
876 | func_data_new["parent_bobj"] = parent_bobj
877 | func_data_new["is_link"] = func_data["is_link"]
878 | func_data_new["link_source"] = link_source
879 | print(pre_line + "import_obj ...")
880 | self.import_obj(
881 | func_data=func_data_new, pre_line=pre_line,
882 | )
883 | func_data["pre_line"] = pre_line_orig
884 |
885 | def handle__sub_objects(
886 | self,
887 | func_data,
888 | sub_objects,
889 | pre_line,
890 | parent_obj,
891 | parent_bobj,
892 | include_only_visible=True,
893 | is_link_source=False,
894 | ):
895 | """Handle sub object."""
896 | # │─ ┌─ └─ ├─ ╞═ ╘═╒═
897 | # ║═ ╔═ ╚═ ╠═ ╟─
898 | # ┃━ ┏━ ┗━ ┣━ ┠─
899 | pre_line_start = pre_line + "╔════ "
900 | pre_line_sub_special = pre_line + "╠════ "
901 | pre_line_sub = pre_line + "╠═ "
902 | pre_line_follow = pre_line + "║ "
903 | pre_line_end = pre_line + "╚════ "
904 |
905 | pre_line_orig = func_data["pre_line"]
906 | pre_line = pre_line_follow
907 | func_data["pre_line"] = pre_line
908 | print(
909 | pre_line_start
910 | + "handle__sub_objects"
911 | # + " - sub_objects '{}'"
912 | # + "".format(sub_objects)
913 | )
914 | sub_filter_visible = False
915 | if not isinstance(include_only_visible, list):
916 | # convert True or False to list
917 | include_only_visible = [None] * len(sub_objects)
918 | sub_filter_visible = True
919 | # print(
920 | # pre_line +
921 | # "include_only_visible '{}'"
922 | # "".format(include_only_visible)
923 | # )
924 | # print(
925 | # pre_line +
926 | # "sub_objects '{}'"
927 | # "".format(sub_objects)
928 | # )
929 | # print(
930 | # pre_line +
931 | # "is_link_source '{}'"
932 | # "".format(is_link_source)
933 | # )
934 | print(pre_line + "parent_obj: " + self.format_obj(parent_obj))
935 | print(pre_line + "parent_bobj: {}".format(parent_bobj))
936 | sub_objects = fc_helper.filtered_objects(
937 | sub_objects, include_only_visible=sub_filter_visible
938 | )
939 | self.config["report"](
940 | {"INFO"},
941 | (
942 | b_helper.colors.bold
943 | + b_helper.colors.fg.purple
944 | + "Import {} Recusive:".format(len(sub_objects))
945 | + b_helper.colors.reset
946 | ),
947 | pre_line=pre_line_sub_special,
948 | )
949 | # is_link_source = False
950 | # # if func_data["is_link"] and len(sub_objects) > 1:
951 | # if len(sub_objects) > 1:
952 | # is_link_source = True
953 | for index, obj in enumerate(sub_objects):
954 | if self.check_obj_visibility_with_skiphidden(
955 | obj, include_only_visible[index]
956 | ):
957 | self.print_obj(obj, pre_line_sub)
958 | self.handle__sub_object_import(
959 | func_data=func_data,
960 | obj=obj,
961 | pre_line=pre_line_follow,
962 | parent_obj=parent_obj,
963 | parent_bobj=parent_bobj,
964 | is_link_source=is_link_source,
965 | )
966 | else:
967 | self.print_obj(
968 | obj=obj,
969 | pre_line=pre_line_sub,
970 | post_line=(
971 | b_helper.colors.fg.darkgrey
972 | + " (skipping - hidden)"
973 | + b_helper.colors.reset
974 | ),
975 | )
976 | func_data["pre_line"] = pre_line_orig
977 |
978 | if func_data["bobj"] is None:
979 | func_data["bobj"] = parent_bobj
980 |
981 | self.config["report"](
982 | {"INFO"},
983 | (
984 | b_helper.colors.bold
985 | + b_helper.colors.fg.purple
986 | + "done."
987 | + b_helper.colors.reset
988 | ),
989 | pre_line=pre_line_end,
990 | )
991 |
992 | def handle__object_with_sub_objects(
993 | self, func_data, sub_objects, include_only_visible=True, is_link_source=False,
994 | ):
995 | """Handle sub objects."""
996 | pre_line = func_data["pre_line"]
997 | parent_obj = func_data["obj"]
998 | parent_label = self.get_obj_label(parent_obj)
999 | if func_data["is_link"] and func_data["obj_label"]:
1000 | parent_label = func_data["obj_label"]
1001 | print(pre_line + "handle__object_with_sub_objects '{}'".format(parent_label))
1002 | # print(pre_line + "is_link_source '{}'".format(is_link_source))
1003 | # pre_line += "→ "
1004 |
1005 | # print(pre_line + "force update parent_bobj to match parent_obj")
1006 | # p_label = self.get_obj_label(func_data["parent_obj"])
1007 | # if (
1008 | # func_data["parent_obj"]
1009 | # and p_label in bpy.data.objects
1010 | # ):
1011 | # func_data["parent_bobj"] = bpy.data.objects[p_label]
1012 |
1013 | self.print_obj(
1014 | func_data["parent_obj"], pre_line=pre_line + "# func_data[parent_obj]",
1015 | )
1016 | print(pre_line + "# func_data[parent_bobj]", func_data["parent_bobj"])
1017 |
1018 | self.parent_empty_add_or_update(func_data, parent_label)
1019 | parent_bobj = func_data["parent_bobj"]
1020 | print(pre_line + "fresh created parent_bobj ", parent_bobj)
1021 |
1022 | if len(sub_objects) > 0:
1023 | self.handle__sub_objects(
1024 | func_data,
1025 | sub_objects,
1026 | pre_line,
1027 | parent_obj,
1028 | parent_bobj,
1029 | include_only_visible=include_only_visible,
1030 | is_link_source=is_link_source,
1031 | )
1032 | else:
1033 | self.config["report"](
1034 | {"INFO"},
1035 | (b_helper.colors.fg.darkgrey + "→ no childs." + b_helper.colors.reset),
1036 | pre_line=pre_line,
1037 | )
1038 |
1039 | # ##########################################
1040 | # Arrays and similar
1041 | def handle__ObjectWithElementList(self, func_data, is_link_source=False):
1042 | """Handle Part::Feature objects."""
1043 | pre_line_orig = func_data["pre_line"]
1044 | print(pre_line_orig + "handle__ObjectWithElementList")
1045 | pre_line = pre_line_orig + " "
1046 | func_data["pre_line"] = pre_line
1047 | # fc_helper.print_objects(
1048 | # func_data["obj"].ElementList,
1049 | # pre_line=pre_line
1050 | # )
1051 | include_only_visible = [*func_data["obj"].VisibilityList]
1052 | self.handle__object_with_sub_objects(
1053 | func_data,
1054 | func_data["obj"].ElementList,
1055 | include_only_visible=include_only_visible,
1056 | is_link_source=is_link_source,
1057 | )
1058 | func_data["pre_line"] = pre_line_orig
1059 |
1060 | # Part::FeaturePhython
1061 | def handle__PartFeaturePython_Array(self, func_data):
1062 | """Handle Part::Feature objects."""
1063 | pre_line_orig = func_data["pre_line"]
1064 | print(
1065 | pre_line_orig + "handle__PartFeaturePython_Array",
1066 | self.format_obj(func_data["obj"]),
1067 | )
1068 | pre_line = pre_line_orig + " "
1069 | func_data["pre_line"] = pre_line
1070 | pre_line = func_data["pre_line"]
1071 | # print(
1072 | # pre_line + "ElementList:",
1073 | # func_data["obj"].ElementList
1074 | # )
1075 | # print(pre_line + "Count:", func_data["obj"].Count)
1076 | # print(pre_line + "ExpandArray:", func_data["obj"].ExpandArray)
1077 | # print(pre_line + "expand Array")
1078 | # TODO: this currently has only any effect in the GUI
1079 | func_data["obj"].ExpandArray = True
1080 | self.doc.recompute()
1081 | # print(pre_line + "ExpandArray:", func_data["obj"].ExpandArray)
1082 | print(pre_line + "ElementList:", func_data["obj"].ElementList)
1083 | # print(
1084 | # pre_line
1085 | # + "call handle__ObjectWithElementList with "
1086 | # + b_helper.colors.fg.orange
1087 | # + "is_link_source=True"
1088 | # + b_helper.colors.reset
1089 | # + ".."
1090 | # )
1091 | self.handle__ObjectWithElementList(func_data, is_link_source=True)
1092 | func_data["pre_line"] = pre_line_orig
1093 |
1094 | def handle__PartFeaturePython_ArchWithHostChilds(self, func_data):
1095 | """Handle Part::Feature Arch objects with HostsChilds."""
1096 | pre_line_orig = func_data["pre_line"]
1097 | print(
1098 | pre_line_orig + "handle__PartFeaturePython_ArchWithHostChilds",
1099 | self.format_obj(func_data["obj"]),
1100 | )
1101 | pre_line = pre_line_orig + " "
1102 | func_data["pre_line"] = pre_line
1103 | pre_line = func_data["pre_line"]
1104 | obj = func_data["obj"]
1105 | # import the part itself
1106 | self.handle__PartFeature(func_data)
1107 | # handle childs
1108 | original_parent = func_data["parent_bobj"]
1109 | obj_childs = fc_helper.object_get_HostChilds(obj)
1110 | # print(pre_line + "obj_childs:", obj_childs)
1111 | # print(pre_line + "len(obj_childs):", len(obj_childs))
1112 | self.handle__object_with_sub_objects(func_data, obj_childs)
1113 | # restor
1114 | func_data["parent_bobj"] = original_parent
1115 | func_data["pre_line"] = pre_line_orig
1116 |
1117 | def handle__PartFeaturePython(self, func_data, pre_line=""):
1118 | """Handle Part::FeaturePython objects."""
1119 | obj = func_data["obj"]
1120 | if hasattr(obj, "ExpandArray") and hasattr(obj, "ElementList"):
1121 | self.handle__PartFeaturePython_Array(func_data)
1122 | elif hasattr(obj, "ArrayType"):
1123 | self.config["report"](
1124 | {"WARNING"},
1125 | (
1126 | "Unable to load '{}' ('{}') of type '{}'. "
1127 | "(Type Not implemented yet)."
1128 | "".format(obj.Label, obj.Name, obj.TypeId)
1129 | ),
1130 | pre_line,
1131 | )
1132 | elif len(fc_helper.object_get_HostChilds(obj)) > 0:
1133 | # Arch Workbench - ArchComponent
1134 | self.handle__PartFeaturePython_ArchWithHostChilds(func_data)
1135 | elif hasattr(obj, "Hosts"):
1136 | # Arch Workbench - Childs
1137 | self.handle__PartFeature(func_data)
1138 |
1139 | # self.handle__object_hosts(func_data)
1140 | # not needed anymore - as the main import now handles the parent-child relationship
1141 | else:
1142 | self.config["report"](
1143 | {"WARNING"},
1144 | (
1145 | "try to load '{}' ('{}') of type '{}' as normal Part::Feature. "
1146 | "no special implementation for Sub-Type of 'Part::FeaturePython' found."
1147 | "".format(obj.Label, obj.Name, obj.TypeId)
1148 | ),
1149 | pre_line,
1150 | )
1151 | self.handle__PartFeature(func_data)
1152 |
1153 | # App::Part
1154 | def handle__AppPart(self, func_data):
1155 | """Handle App:Part type."""
1156 | # pre_line = func_data["pre_line"]
1157 | self.handle__object_with_sub_objects(func_data, func_data["obj"].Group)
1158 |
1159 | # App::Link*
1160 | def add_or_update_collection_instance(
1161 | self, *, func_data, obj, obj_label, instance_target_label,
1162 | ):
1163 | """Add or update collection instance object."""
1164 | pre_line_orig = func_data["pre_line"]
1165 | pre_line = pre_line_orig
1166 | # │─ ┌─ └─ ├─ ╞═ ╘═╒═
1167 | # ║═ ╔═ ╚═ ╠═ ╟─
1168 | # ┃━ ┏━ ┗━ ┣━ ┠─
1169 | pre_line_start = pre_line_orig + "┌ "
1170 | # pre_line_sub = pre_line_orig + "├─ "
1171 | pre_line_follow = pre_line_orig + "│ "
1172 | pre_line_end = pre_line_orig + "└────────── "
1173 | print(
1174 | pre_line_start + "add_or_update_collection_instance '{}'"
1175 | "".format(obj_label)
1176 | )
1177 | func_data["pre_line"] = pre_line_follow
1178 | # pre_line = pre_line_sub
1179 | pre_line = pre_line_follow
1180 |
1181 | print(pre_line + "obj_label '{}'".format(obj_label))
1182 | print(pre_line + "instance_target_label '{}'".format(instance_target_label))
1183 | print(
1184 | pre_line + "func_data[collection] '{}'" "".format(func_data["collection"])
1185 | )
1186 |
1187 | base_collection = None
1188 | bobj = None
1189 | if instance_target_label in bpy.data.collections:
1190 | base_collection = bpy.data.collections[instance_target_label]
1191 | flag_new = False
1192 | if obj_label in bpy.data.objects:
1193 | bobj = bpy.data.objects[obj_label]
1194 | else:
1195 | bobj = self.create_collection_instance(
1196 | func_data, pre_line_follow, obj_label, base_collection
1197 | )
1198 | flag_new = True
1199 | # print(
1200 | # pre_line +
1201 | # "bobj '{}'; new:{}"
1202 | # "".format(bobj, flag_new)
1203 | # )
1204 | if self.config["update"] or flag_new:
1205 | self.set_obj_parent_and_collection(pre_line_follow, func_data, bobj)
1206 | self.handle_placement(pre_line_follow, obj, bobj, enable_scale=True)
1207 | # print(
1208 | # pre_line + " "
1209 | # "bobj '{}' ".format(bobj.location)
1210 | # )
1211 | else:
1212 | self.config["report"](
1213 | {"WARNING"},
1214 | (
1215 | "Warning: can't add or update instance. "
1216 | "'{}' collection not found."
1217 | "".format(instance_target_label)
1218 | ),
1219 | pre_line,
1220 | )
1221 | # return False
1222 | print(pre_line_end + "")
1223 | func_data["pre_line"] = pre_line_orig
1224 |
1225 | def add_or_update_link_instance(
1226 | self, *, func_data, obj, obj_label, link_target_obj, link_target_label,
1227 | ):
1228 | """Add or update link instance object."""
1229 | pre_line_orig = func_data["pre_line"]
1230 | pre_line = pre_line_orig
1231 | # │─ ┌─ └─ ├─ ╞═ ╘═╒═
1232 | # ║═ ╔═ ╚═ ╠═ ╟─
1233 | # ┃━ ┏━ ┗━ ┣━ ┠─
1234 | pre_line_start = pre_line_orig + "┌ "
1235 | # pre_line_sub = pre_line_orig + "├─ "
1236 | pre_line_follow = pre_line_orig + "│ "
1237 | pre_line_end = pre_line_orig + "└────────── "
1238 | print(pre_line_start + "add_or_update_link_instance '{}'" "".format(obj_label))
1239 | func_data["pre_line"] = pre_line_follow
1240 | # pre_line = pre_line_sub
1241 | pre_line = pre_line_follow
1242 |
1243 | print(pre_line + "obj_label '{}'".format(obj_label))
1244 | print(pre_line + "link_target_label '{}'".format(link_target_label))
1245 |
1246 | link_target_bobj = None
1247 | bobj = None
1248 | if link_target_label in bpy.data.objects:
1249 | link_target_bobj = bpy.data.objects[link_target_label]
1250 | print(pre_line + "# link_target_bobj ", link_target_bobj)
1251 | else:
1252 | self.config["report"](
1253 | {"WARNING"},
1254 | (
1255 | "Warning: can't add or update linnk instance. "
1256 | "'{}' link_target not found."
1257 | "".format(link_target_label)
1258 | ),
1259 | pre_line,
1260 | )
1261 | # return False
1262 | flag_new = False
1263 | if obj_label in bpy.data.objects:
1264 | bobj = bpy.data.objects[obj_label]
1265 | print(pre_line + "# bobj already here: ", bobj)
1266 | else:
1267 | bobj = self.create_link_instance(
1268 | func_data, pre_line_follow, obj_label, link_target_bobj, link_target_obj
1269 | )
1270 | print(pre_line + "# created new bobj: ", bobj)
1271 | flag_new = True
1272 | # print(
1273 | # pre_line +
1274 | # "bobj '{}'; new:{}"
1275 | # "".format(bobj, flag_new)
1276 | # )
1277 | if self.config["update"] or flag_new:
1278 | # if func_data["parent_bobj"] is None:
1279 | # func_data["parent_bobj"] = link_target_bobj
1280 | # print(
1281 | # pre_line +
1282 | # "'{}' try to set parent to '{}' "
1283 | # "".format(bobj, func_data["parent_bobj"])
1284 | # )
1285 | # self.set_obj_parent_and_collection(
1286 | # pre_line_follow,
1287 | # func_data,
1288 | # bobj
1289 | # )
1290 | self.handle_placement(pre_line_follow, obj, bobj, enable_scale=True)
1291 | # print(
1292 | # pre_line + " "
1293 | # "bobj '{}' ".format(bobj.location)
1294 | # )
1295 | print(pre_line + "# bobj.data: ", bobj.data)
1296 | if bobj.data:
1297 | if bobj.data.name != link_target_label:
1298 | if link_target_label in bpy.data.meshes:
1299 | print(
1300 | pre_line
1301 | + "update / relink '{}' to original link target '{}'"
1302 | "".format(obj_label, link_target_label)
1303 | )
1304 | old_mesh = bobj.data
1305 | bobj.data = bpy.data.meshes[link_target_label]
1306 | # clean up temporary mesh
1307 | if old_mesh.users == 0:
1308 | bpy.data.meshes.remove(old_mesh)
1309 | else:
1310 | print(
1311 | pre_line + "→ link_target_label not in bpy.data.meshes "
1312 | "Something wired going on.... "
1313 | "it seems to working..."
1314 | "TODO: maybe CHECK"
1315 | )
1316 | # else:
1317 | # print(
1318 | # pre_line +
1319 | # "→ bobj.data.name '{}' == link_target_label '{}' "
1320 | # "".format(bobj.data.name, link_target_label)
1321 | # )
1322 | else:
1323 | print(pre_line + "→ bobj.data == None " "→ maybe this is a Empty.")
1324 |
1325 | func_data["bobj"] = bobj
1326 | func_data["update_tree"] = True
1327 |
1328 | print(pre_line_end + "")
1329 | func_data["pre_line"] = pre_line_orig
1330 |
1331 | def add_or_update_link_target(
1332 | self, *, func_data, obj, obj_label, obj_linkedobj, obj_linkedobj_label,
1333 | ):
1334 | """Add or update link target object."""
1335 | pre_line = func_data["pre_line"]
1336 | # print(
1337 | # pre_line +
1338 | # "$ add_or_update_link_target: '{}'"
1339 | # "".format(
1340 | # obj_linkedobj_label,
1341 | # )
1342 | # )
1343 | # print(
1344 | # pre_line +
1345 | # "$ obj: '{}' '{}' parent: '{}'"
1346 | # "".format(
1347 | # obj,
1348 | # obj.Label,
1349 | # obj.getParentGeoFeatureGroup()
1350 | # )
1351 | # )
1352 |
1353 | # print(
1354 | # pre_line +
1355 | # "self.imported_obj_names ",
1356 | # self.imported_obj_names
1357 | # )
1358 |
1359 | if obj_linkedobj_label in bpy.data.objects or (
1360 | obj_linkedobj_label in self.imported_obj_names
1361 | ):
1362 | print(
1363 | pre_line + "→ already imported/updated '{}'."
1364 | "".format(obj_linkedobj_label)
1365 | )
1366 | else:
1367 | self.print_obj(
1368 | obj,
1369 | pre_line=pre_line + "# ",
1370 | post_line=" → import Link Target {}.".format(
1371 | self.format_obj(obj_linkedobj)
1372 | ),
1373 | end="",
1374 | )
1375 |
1376 | # self.print_obj(
1377 | # obj_linkedobj,
1378 | # pre_line=pre_line + "# ",
1379 | # post_line=""
1380 | # )
1381 |
1382 | # self.print_obj(
1383 | # func_data["parent_obj"],
1384 | # pre_line=pre_line + "# func_data[parent_obj]",
1385 | # )
1386 | # print(
1387 | # pre_line + "# func_data[parent_bobj]",
1388 | # func_data["parent_bobj"]
1389 | # )
1390 |
1391 | # if obj_linkedobj_label in bpy.data.objects:
1392 | # self.config["report"]({'INFO'}, (
1393 | # "skipping import. '{}' already in objects list."
1394 | # "".format(obj_linkedobj_label)
1395 | # ), pre_line)
1396 | # else:
1397 | # set collection to link_target
1398 | # this way the imports get definitly added to the scene.
1399 | # func_data["collection"] = self.link_targets
1400 | print(pre_line + "§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§")
1401 | func_data_obj_linked = self.create_func_data()
1402 | func_data_obj_linked["obj"] = obj_linkedobj
1403 | func_data_obj_linked["collection"] = self.link_targets
1404 | func_data_obj_linked["collection_parent"] = None
1405 | func_data_obj_linked["parent_obj"] = obj
1406 | func_data_obj_linked["parent_bobj"] = None
1407 | func_data_obj_linked = self.import_obj(
1408 | func_data=func_data_obj_linked, pre_line=pre_line + " ",
1409 | )
1410 | print(pre_line + "§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§")
1411 | bobj = func_data_obj_linked["bobj"]
1412 | # print(
1413 | # pre_line +
1414 | # "func_data_obj_linked '{}' "
1415 | # "".format(func_data_obj_linked)
1416 | # )
1417 | # pprint.pprint(func_data_obj_linked)
1418 |
1419 | # fix parent linking
1420 | # parent_obj = obj_linked.getParentGeoFeatureGroup()
1421 | # parent_label = self.get_obj_label(parent_obj)
1422 | # if parent_label:
1423 | # parent_bobj = bpy.data.objects[parent_label]
1424 | # if parent_bobj:
1425 | # bobj.parent = parent_bobj
1426 | # print(
1427 | # pre_line +
1428 | # "'{}' set parent to '{}' "
1429 | # "".format(bobj, parent_bobj)
1430 | # )
1431 | # this has no parent as we use only the raw obj.
1432 | self.print_obj(func_data_obj_linked["obj"], pre_line + "$ used obj: ")
1433 | print(pre_line + "$ created bobj: ", bobj)
1434 | print(pre_line + "$ bobj.parent: ", bobj.parent)
1435 | print(pre_line + "$ func_data[parent_bobj]: ", func_data["parent_bobj"])
1436 | # bobj.parent = None
1437 | self.reset_placement_position(bobj)
1438 |
1439 | # print(
1440 | # pre_line + "$ parent_bobj: ",
1441 | # func_data_obj_linked["parent_bobj"]
1442 | # )
1443 | # print(
1444 | # pre_line + "$ collection: ",
1445 | # func_data_obj_linked["collection"]
1446 | # )
1447 | # print(
1448 | # pre_line + "$ collection_parent: ",
1449 | # func_data_obj_linked["collection_parent"]
1450 | # )
1451 |
1452 | # created collection for new link target
1453 | func_data_obj_linked["collection"] = self.link_targets
1454 | self.sub_collection_add_or_update(func_data_obj_linked, obj_linkedobj_label)
1455 | # self.parent_empty_add_or_update(
1456 | # func_data_obj_linked, obj_linkedobj_label)
1457 | # add new object to collection.
1458 | func_data_obj_linked["collection"].objects.link(bobj)
1459 | print(
1460 | pre_line + "'{}' add to '{}' "
1461 | "".format(bobj, func_data_obj_linked["collection"])
1462 | )
1463 |
1464 | def handle__AppLink(self, func_data):
1465 | """Handle App::Link objects."""
1466 | pre_line_orig = func_data["pre_line"]
1467 | pre_line = pre_line_orig
1468 | # │─ ┌─ └─ ├─ ╞═ ╘═╒═
1469 | # ║═ ╔═ ╚═ ╠═ ╟─
1470 | # ┃━ ┏━ ┗━ ┣━ ┠─
1471 | pre_line_start = pre_line_orig + "┌ "
1472 | # pre_line_sub = pre_line_orig + "├─ "
1473 | pre_line_follow = pre_line_orig + "│ "
1474 | pre_line_end = pre_line_orig + "└────────── "
1475 | print(pre_line_start + "handle__AppLink")
1476 | func_data["pre_line"] = pre_line_follow
1477 | # pre_line = pre_line_sub
1478 | pre_line = pre_line_follow
1479 |
1480 | obj = func_data["obj"]
1481 | obj_linkedobj = func_data["obj"].LinkedObject
1482 | if isinstance(obj_linkedobj, tuple):
1483 | obj_linkedobj = obj_linkedobj[0]
1484 | # print(pre_line + "obj_linkedobj :", obj_linkedobj)
1485 | # self.config["report"]({'WARNING'}, (
1486 | # "'{}' ('{s}') of type '{}': "
1487 | # "".format(obj.Label, obj.Name, obj.TypeId)
1488 | # ), pre_line)
1489 | # self.config["report"]({'WARNING'}, (
1490 | # " Warning: App::Link handling is highly experimental!!"
1491 | # ), pre_line)
1492 | obj_label = self.get_obj_label(obj)
1493 | if func_data["is_link"] and func_data["obj_label"]:
1494 | obj_label = func_data["obj_label"]
1495 | # obj_linkedobj_label = self.get_obj_linkedobj_label(obj)
1496 | # obj_linked_label = self.get_obj_label(obj_linkedobj)
1497 |
1498 | # print(pre_line + "obj_label:", obj_label)
1499 | # print(pre_line + "obj_linkedobj_label:", obj_linkedobj_label)
1500 | # print(pre_line + "obj_linked_label:", obj_linked_label)
1501 | # fc_helper.print_obj(
1502 | # obj,
1503 | # pre_line=pre_line + "obj : ")
1504 | # fc_helper.print_obj(
1505 | # obj_linkedobj,
1506 | # pre_line=pre_line + "obj_linkedobj: ")
1507 | # if hasattr(obj_linkedobj, "LinkedObject"):
1508 | # fc_helper.print_obj(obj_linkedobj.LinkedObject, pre_line=pre_line)
1509 |
1510 | if obj_linkedobj:
1511 | orig_is_link = func_data["is_link"]
1512 | func_data["is_link"] = True
1513 |
1514 | if hasattr(obj, "ElementList") and len(obj.ElementList) > 0:
1515 | print(pre_line + "ElementList > 0")
1516 | self.handle__ObjectWithElementList(func_data)
1517 | else:
1518 | print(pre_line + "Single Element → fake list")
1519 | # if target is of Body type get real link target
1520 | # this excludes the link → link → link chain...
1521 | # try:
1522 | # obj_linkedobj.getLinkedObject().isDerivedFrom("Part::Feature")
1523 | # except Exception as e:
1524 | # print(pre_line + "obj_linkedobj error:", e)
1525 | # else:
1526 | # print(pre_line + "use recusive inner target")
1527 | # obj_linkedobj = obj_linkedobj.getLinkedObject()
1528 | if obj_linkedobj.getLinkedObject().isDerivedFrom("Part::Feature"):
1529 | print(pre_line + "use recusive inner target")
1530 | obj_linkedobj = obj_linkedobj.getLinkedObject()
1531 | self.handle__object_with_sub_objects(
1532 | func_data, [obj_linkedobj], include_only_visible=[True]
1533 | )
1534 | # set back to original
1535 | func_data["is_link"] = orig_is_link
1536 | else:
1537 | self.config["report"](
1538 | {"WARNING"},
1539 | ("Warning: '{}' LinkedObject is NONE → skipping." "".format(obj_label)),
1540 | pre_line,
1541 | )
1542 | print(pre_line_end + "")
1543 | func_data["pre_line"] = pre_line_orig
1544 |
1545 | def handle__AppLinkElement(self, func_data, obj_linkedobj=None):
1546 | """Handle App::LinkElement objects."""
1547 | pre_line_orig = func_data["pre_line"]
1548 |
1549 | pre_line = pre_line_orig
1550 | # │─ ┌─ └─ ├─ ╞═ ╘═╒═
1551 | # ║═ ╔═ ╚═ ╠═ ╟─
1552 | # ┃━ ┏━ ┗━ ┣━ ┠─
1553 | pre_line_start = pre_line_orig + "┌ "
1554 | # pre_line_sub = pre_line_orig + "├─ "
1555 | pre_line_follow = pre_line_orig + "│ "
1556 | pre_line_end = pre_line_orig + "└────────── "
1557 | print(pre_line_start + "handle__AppLinkElement")
1558 | func_data["pre_line"] = pre_line_follow
1559 | # pre_line = pre_line_sub
1560 | pre_line = pre_line_follow
1561 |
1562 | obj = func_data["obj"]
1563 | if obj_linkedobj is None:
1564 | obj_linkedobj = func_data["obj"].LinkedObject
1565 |
1566 | # if hasattr(obj_linkedobj, "LinkedObject"):
1567 | # # if we have Arrays they have a intermediet link object..
1568 | # # we skip this..
1569 | # obj_linkedobj = obj_linkedobj.LinkedObject
1570 |
1571 | # parent_obj = obj.InList[0]
1572 | parent_obj = func_data["parent_obj"]
1573 | # parent_obj_label = self.get_obj_label(parent_obj)
1574 | # if (
1575 | # parent_obj and
1576 | # parent_obj_label in bpy.data.objects
1577 | # ):
1578 | # func_data["parent_bobj"] = bpy.data.objects[parent_obj_label]
1579 | print(pre_line + "func_data[parent_bobj]:", func_data["parent_bobj"])
1580 |
1581 | # obj_label = self.get_obj_combined_label(parent_obj, obj)
1582 | obj_label = self.get_obj_label(obj)
1583 | if func_data["is_link"] and func_data["obj_label"]:
1584 | obj_label = func_data["obj_label"]
1585 | # obj_linkedobj_label = self.get_obj_linkedobj_label(obj)
1586 | obj_linkedobj_label = self.get_obj_label(obj_linkedobj)
1587 |
1588 | # print(pre_line + "collection:", func_data["collection"])
1589 | # print(pre_line + "parent_obj_label:", parent_obj_label)
1590 | print(pre_line + "obj_label:", obj_label)
1591 | print(pre_line + "obj_linkedobj_label:", obj_linkedobj_label)
1592 | fc_helper.print_obj(parent_obj, pre_line=pre_line + "parent_obj : ")
1593 | fc_helper.print_obj(obj, pre_line=pre_line + "obj : ")
1594 | fc_helper.print_obj(obj_linkedobj, pre_line=pre_line + "obj_linkedobj: ")
1595 | # fc_helper.print_obj(obj_linked.LinkedObject, pre_line=pre_line)
1596 |
1597 | if not self.config["links_as_collectioninstance"]:
1598 | self.add_or_update_link_instance(
1599 | func_data=func_data,
1600 | obj=obj,
1601 | obj_label=obj_label,
1602 | link_target_obj=obj_linkedobj,
1603 | link_target_label=obj_linkedobj_label,
1604 | )
1605 |
1606 | self.add_or_update_link_target(
1607 | func_data=func_data,
1608 | obj=obj,
1609 | obj_label=obj_label,
1610 | obj_linkedobj=obj_linkedobj,
1611 | obj_linkedobj_label=obj_linkedobj_label,
1612 | )
1613 |
1614 | if self.config["links_as_collectioninstance"]:
1615 | self.add_or_update_collection_instance(
1616 | func_data=func_data,
1617 | obj=obj,
1618 | obj_label=obj_label,
1619 | instance_target_label=obj_linkedobj_label,
1620 | )
1621 | else:
1622 | self.add_or_update_link_instance(
1623 | func_data=func_data,
1624 | obj=obj,
1625 | obj_label=obj_label,
1626 | link_target_obj=obj_linkedobj,
1627 | link_target_label=obj_linkedobj_label,
1628 | )
1629 | print(pre_line_end + "")
1630 | func_data["pre_line"] = pre_line_orig
1631 |
1632 | # ##########################################
1633 | # 'Arch' object types
1634 | def handle__object_hosts(self, func_data):
1635 | """Handle object with hosts attribute (Arch Workbench)."""
1636 | pre_line = func_data["pre_line"]
1637 | obj = func_data["obj"]
1638 | obj_host = obj.Hosts[0]
1639 | obj_label = self.get_obj_label(obj)
1640 | obj_host_label = self.get_obj_label(obj_host)
1641 | print(pre_line + "handle__object_hosts '{}'".format(obj_label))
1642 | print(pre_line + "obj_host_label '{}'".format(obj_host_label))
1643 | bobj = func_data["bobj"]
1644 | bobj_host = bpy.data.objects[obj_host_label]
1645 | if bobj_host:
1646 | print(pre_line + "bobj_host '{}'".format(bobj_host))
1647 | print(pre_line + "bobj_host.parent '{}'".format(bobj_host.parent))
1648 | # Arch Wall Objects are no collection things - so we need to use the parent of it...
1649 | # in the hope that this works...
1650 | if bobj_host.parent:
1651 | bobj.parent = bobj_host.parent
1652 | else:
1653 | self.config["report"](
1654 | {"WARNING"},
1655 | (
1656 | "Warning: host '{}' has no parrent. can not set parent for '{}' "
1657 | "".format(obj_host_label, obj_label)
1658 | ),
1659 | pre_line,
1660 | )
1661 |
1662 | # ##########################################
1663 | # 'real' object types
1664 |
1665 | # Part::Feature
1666 | def handle_shape_edge(self, func_data, edge):
1667 | """Handle edges that are not part of a face."""
1668 | if self.hascurves(edge):
1669 | # TODO use tessellation value
1670 | dv = edge.discretize(9)
1671 | for i in range(len(dv) - 1):
1672 | dv1 = [dv[i].x, dv[i].y, dv[i].z]
1673 | dv2 = [dv[i + 1].x, dv[i + 1].y, dv[i + 1].z]
1674 | if dv1 not in func_data["verts"]:
1675 | func_data["verts"].append(dv1)
1676 | if dv2 not in func_data["verts"]:
1677 | func_data["verts"].append(dv2)
1678 | func_data["edges"].append(
1679 | [func_data["verts"].index(dv1), func_data["verts"].index(dv2)]
1680 | )
1681 | else:
1682 | e = []
1683 | for vert in edge.Vertexes:
1684 | # TODO discretize non-linear edges
1685 | v = [vert.X, vert.Y, vert.Z]
1686 | if v not in func_data["verts"]:
1687 | func_data["verts"].append(v)
1688 | e.append(func_data["verts"].index(v))
1689 | func_data["edges"].append(e)
1690 |
1691 | def convert_face_to_polygon(self, func_data, face, faceedges):
1692 | """Convert face to polygons."""
1693 | import Part
1694 |
1695 | if (
1696 | (len(face.Wires) > 1)
1697 | or (not isinstance(face.Surface, Part.Plane))
1698 | or self.hascurves(face)
1699 | ):
1700 | # face has holes or is curved, so we need to triangulate it
1701 | rawdata = face.tessellate(self.config["tessellation"])
1702 | for v in rawdata[0]:
1703 | vl = [v.x, v.y, v.z]
1704 | if vl not in func_data["verts"]:
1705 | func_data["verts"].append(vl)
1706 | for f in rawdata[1]:
1707 | nf = []
1708 | for vi in f:
1709 | nv = rawdata[0][vi]
1710 | nf.append(func_data["verts"].index([nv.x, nv.y, nv.z]))
1711 | func_data["faces"].append(nf)
1712 | func_data["matindex"].append(len(rawdata[1]))
1713 | else:
1714 | f = []
1715 | ov = face.OuterWire.OrderedVertexes
1716 | for v in ov:
1717 | vl = [v.X, v.Y, v.Z]
1718 | if vl not in func_data["verts"]:
1719 | func_data["verts"].append(vl)
1720 | f.append(func_data["verts"].index(vl))
1721 | # FreeCAD doesn't care about func_data["verts"] order.
1722 | # Make sure our loop goes clockwise
1723 | c = face.CenterOfMass
1724 | v1 = ov[0].Point.sub(c)
1725 | v2 = ov[1].Point.sub(c)
1726 | n = face.normalAt(0, 0)
1727 | if (v1.cross(v2)).getAngle(n) > 1.57:
1728 | # inverting func_data["verts"] order
1729 | # if the direction is counterclockwise
1730 | f.reverse()
1731 | func_data["faces"].append(f)
1732 | func_data["matindex"].append(1)
1733 | for e in face.Edges:
1734 | faceedges.append(e.hashCode())
1735 |
1736 | def handle_shape_faces(self, func_data, shape, faceedges):
1737 | """Convert faces to polygons."""
1738 | if TRIANGULATE:
1739 | # triangulate and make faces
1740 | rawdata = shape.tessellate(self.config["tessellation"])
1741 | for v in rawdata[0]:
1742 | func_data["verts"].append([v.x, v.y, v.z])
1743 | for f in rawdata[1]:
1744 | func_data["faces"].append(f)
1745 | for face in shape.Faces:
1746 | for e in face.Edges:
1747 | faceedges.append(e.hashCode())
1748 | else:
1749 | # write FreeCAD faces as polygons when possible
1750 | for face in shape.Faces:
1751 | self.convert_face_to_polygon(func_data, face, faceedges)
1752 |
1753 | def create_mesh_from_shape(self, func_data):
1754 | """Create mesh from shape."""
1755 | # print(func_data["pre_line"] + "create_mesh_from_shape")
1756 | # a placeholder to store edges that belong to a face
1757 | faceedges = []
1758 | shape = func_data["obj"].Shape
1759 | # func_data["freecad_mesh_hash"] = shape.hashCode()
1760 | # hashCode changes on every file opening :-(
1761 | if self.config["placement"]:
1762 | shape = func_data["obj"].Shape.copy()
1763 | shape.Placement = (
1764 | func_data["obj"].Placement.inverse().multiply(shape.Placement)
1765 | )
1766 | if shape.Faces:
1767 | self.handle_shape_faces(func_data, shape, faceedges)
1768 | # Treat remaining edges (that are not in faces)
1769 | for edge in shape.Edges:
1770 | if not (edge.hashCode() in faceedges):
1771 | self.handle_shape_edge(func_data, edge)
1772 | return shape
1773 |
1774 | def handle__PartFeature(self, func_data):
1775 | """Handle Part::Feature objects."""
1776 | pre_line_orig = func_data["pre_line"]
1777 | pre_line = func_data["pre_line"]
1778 | print(func_data["pre_line"] + "handle__PartFeature")
1779 | pre_line += "> "
1780 | func_data["pre_line"] = pre_line
1781 |
1782 | obj = func_data["obj"]
1783 | obj_label = self.get_obj_label(obj)
1784 | if func_data["is_link"] and func_data["obj_label"]:
1785 | obj_label = func_data["obj_label"]
1786 |
1787 | # import_it = False
1788 | update_placement = False
1789 | # check if this Part::Feature object is already imported.
1790 | if self.config["links_as_collectioninstance"]:
1791 | if (
1792 | obj_label in self.link_targets.children
1793 | and obj_label in bpy.data.objects
1794 | ):
1795 | # print(
1796 | # pre_line + "found link target object '{}'"
1797 | # "".format(obj_label)
1798 | # )
1799 | bobj_link_target = bpy.data.objects[obj_label]
1800 | # bobj_link_target_label = self.fix_link_target_name(
1801 | self.fix_link_target_name(bobj_link_target)
1802 | # print(
1803 | # pre_line + "fixed name. '{}'"
1804 | # "".format(bobj_link_target)
1805 | # )
1806 | # self.add_or_update_link_target(
1807 | # func_data=func_data,
1808 | # obj=obj,
1809 | # obj_linked=obj_linkedobj,
1810 | # obj_linkedobj_label=bobj_link_target_label,
1811 | # )
1812 | self.add_or_update_collection_instance(
1813 | func_data=func_data,
1814 | obj=obj,
1815 | obj_label=obj_label,
1816 | # instance_target_label=bobj_link_target_label,
1817 | instance_target_label=obj_label,
1818 | )
1819 | else:
1820 | # import_it = True
1821 | pass
1822 | else:
1823 | # handle creation of linked copies
1824 | print(pre_line + "handle creation of linked copies..")
1825 | # print(pre_line + "imported_obj_names:", self.imported_obj_names)
1826 | if (
1827 | obj_label
1828 | in bpy.data.objects
1829 | # and obj_label in self.imported_obj_names
1830 | ):
1831 | print(pre_line + "→ update bobj")
1832 | bobj = bpy.data.objects[obj_label]
1833 | func_data["bobj"] = bobj
1834 | if not func_data["is_link"]:
1835 | update_placement = True
1836 | func_data["update_tree"] = True
1837 | else:
1838 | print(pre_line + "→ just import it")
1839 | # import_it = True
1840 |
1841 | # if import_it:
1842 | self.create_mesh_from_shape(func_data)
1843 | if func_data["verts"] and (func_data["faces"] or func_data["edges"]):
1844 | self.add_or_update_blender_obj(func_data)
1845 | func_data["update_tree"] = True
1846 |
1847 | if update_placement:
1848 | # print(pre_line + "update_placement..")
1849 | self.handle_placement(
1850 | pre_line, obj, func_data["bobj"],
1851 | )
1852 |
1853 | # restore
1854 | func_data["pre_line"] = pre_line_orig
1855 |
1856 | # Mesh::Feature
1857 | def handle__MeshFeature(self, func_data):
1858 | """Convert freecad mesh to blender mesh."""
1859 | mesh = func_data["obj"].Mesh
1860 | if self.config["placement"]:
1861 | # in meshes, this zeroes the placement
1862 | mesh = func_data["obj"].Mesh.copy()
1863 | t = mesh.Topology
1864 | func_data["verts"] = [[v.x, v.y, v.z] for v in t[0]]
1865 | func_data["faces"] = t[1]
1866 |
1867 | # ##########################################
1868 | # main object import
1869 | def create_func_data(self):
1870 | "Create a blank func_data structure."
1871 | func_data = {
1872 | "obj": None,
1873 | "bobj": None,
1874 | "obj_label": None,
1875 | "verts": [],
1876 | "edges": [],
1877 | "faces": [],
1878 | "freecad_mesh_hash": None,
1879 | # face to material relationship
1880 | "matindex": [],
1881 | # to store reusable materials
1882 | "matdatabase": {},
1883 | # name: "Unnamed",
1884 | "link_targets": [],
1885 | "collection": None,
1886 | "collection_parent": None,
1887 | "parent_obj": None,
1888 | "parent_bobj": None,
1889 | "pre_line": "",
1890 | "update_tree": False,
1891 | "is_link": False,
1892 | "link_source": None,
1893 | }
1894 | return func_data
1895 |
1896 | def _import_obj__handle_type(self, func_data, pre_line=""):
1897 | """Choose Import Type."""
1898 | obj = func_data["obj"]
1899 | if obj.isDerivedFrom("Part::FeaturePython"):
1900 | self.handle__PartFeaturePython(func_data, pre_line)
1901 | elif obj.isDerivedFrom("Part::Feature"):
1902 | self.handle__PartFeature(func_data)
1903 | elif obj.isDerivedFrom("Mesh::Feature"):
1904 | self.handle__MeshFeature(func_data)
1905 | # elif obj.isDerivedFrom("PartDesign::Body"):
1906 | # self.create_mesh_from_Body(func_data)
1907 | # elif obj.isDerivedFrom("XXXXXX"):
1908 | # self.handle__XXXXXX(func_data)
1909 | elif obj.isDerivedFrom("App::Part"):
1910 | self.handle__AppPart(func_data)
1911 | elif obj.isDerivedFrom("App::LinkElement"):
1912 | # self.handle__AppLinkElement(func_data)
1913 | self.handle__AppLink(func_data)
1914 | elif obj.isDerivedFrom("App::Link"):
1915 | self.handle__AppLink(func_data)
1916 | else:
1917 | self.config["report"](
1918 | {"WARNING"},
1919 | (
1920 | "Unable to load '{}' ('{}') of type '{}'. "
1921 | "(Type Not implemented yet)."
1922 | "".format(obj.Label, obj.Name, obj.TypeId)
1923 | ),
1924 | pre_line,
1925 | )
1926 | return func_data
1927 |
1928 | def import_obj(
1929 | self,
1930 | func_data=None,
1931 | # obj=None,
1932 | # collection=None,
1933 | # collection_parent=None,
1934 | # parent_obj=None,
1935 | # parent_bobj=None,
1936 | pre_line="",
1937 | ):
1938 | """Import Object."""
1939 | # import some FreeCAD modules needed below.
1940 | # After "import FreeCAD" these modules become available
1941 | # import Part
1942 | # import PartDesign
1943 | # print("import_obj: obj", obj)
1944 | # dict for storing all data
1945 | if not func_data:
1946 | func_data = self.create_func_data()
1947 | func_data["pre_line"] = pre_line
1948 | obj = func_data["obj"]
1949 | if obj:
1950 | self._import_obj__handle_type(func_data, pre_line)
1951 |
1952 | if func_data["update_tree"]:
1953 | self.update_tree_collections(func_data)
1954 | self.update_tree_parents(func_data)
1955 | return func_data
1956 |
1957 | def import_doc_content(self, doc):
1958 | """Import document content = filterd objects."""
1959 | pre_line = ""
1960 | obj_list, obj_list_withHost = fc_helper.get_root_objects(
1961 | doc, filter_list=self.typeid_filter_list
1962 | )
1963 | print("-" * 21)
1964 |
1965 | self.config["report"](
1966 | {"INFO"},
1967 | (
1968 | "found {} root objects in '{}'"
1969 | "".format(len(obj_list), self.doc_filename)
1970 | ),
1971 | pre_line=pre_line,
1972 | )
1973 | fc_helper.print_objects(obj_list, show_lists=True, show_list_details=True)
1974 | print("-" * 21)
1975 |
1976 | self.config["report"](
1977 | {"INFO"},
1978 | (
1979 | "found {} objects with Hosts attribute set in '{}' - will be handled as childs.."
1980 | "".format(len(obj_list_withHost), self.doc_filename)
1981 | ),
1982 | pre_line=pre_line,
1983 | )
1984 | fc_helper.print_objects(
1985 | obj_list_withHost, show_lists=True, show_list_details=True
1986 | )
1987 | print("-" * 21)
1988 | # self.config["report"](
1989 | # {"INFO"},
1990 | # ("the Hosts ARCH way is not implemented yet. so we just import them."),
1991 | # pre_line=pre_line,
1992 | # )
1993 | # obj_list.extend(obj_list_withHost)
1994 | # fc_helper.print_objects(obj_list, show_lists=True)
1995 | # print("-" * 21)
1996 |
1997 | # │─ ┌─ └─ ├─ ╞═ ╘═╒═
1998 | # ║═ ╔═ ╚═ ╠═ ╟─
1999 | # ┃━ ┏━ ┗━ ┣━ ┠─
2000 | pre_line_start = pre_line + "┏━━━━ "
2001 | pre_line_sub = pre_line + "┣━ "
2002 | pre_line_follow = pre_line + "┃ "
2003 | pre_line_end = pre_line + "┗━━━━ "
2004 | self.config["report"]({"INFO"}, "Import", pre_line=pre_line_start)
2005 | for obj in obj_list:
2006 | if self.check_obj_visibility_with_skiphidden(obj):
2007 | self.print_obj(obj, pre_line=pre_line_sub)
2008 | func_data_new = self.create_func_data()
2009 | func_data_new["obj"] = obj
2010 | func_data_new["collection"] = self.fcstd_collection
2011 | func_data_new["parent_bobj"] = self.fcstd_empty
2012 | self.import_obj(
2013 | func_data=func_data_new, pre_line=pre_line_follow,
2014 | )
2015 | if obj in obj_list_withHost:
2016 | self.config["report"](
2017 | {"INFO"},
2018 | ("TODO: handle Hosts [{}] of obj '{}'").format(obj.Hosts, obj),
2019 | pre_line=pre_line_follow,
2020 | )
2021 | else:
2022 | self.print_obj(
2023 | obj=obj,
2024 | pre_line=pre_line_sub,
2025 | post_line=(
2026 | b_helper.colors.fg.darkgrey
2027 | + " (skipping - hidden)"
2028 | + b_helper.colors.reset
2029 | ),
2030 | )
2031 | self.config["report"]({"INFO"}, "finished.", pre_line=pre_line_end)
2032 |
2033 | def prepare_collection(self):
2034 | """Prepare main import collection."""
2035 | link_targets_label = self.doc.Name + "__link_targets"
2036 | if self.config["update"]:
2037 | if self.doc_filename in bpy.data.collections:
2038 | self.fcstd_collection = bpy.data.collections[self.doc_filename]
2039 | if link_targets_label in bpy.data.collections:
2040 | self.link_targets = bpy.data.collections[link_targets_label]
2041 |
2042 | if not self.fcstd_collection:
2043 | self.fcstd_collection = bpy.data.collections.new(self.doc_filename)
2044 | bpy.context.scene.collection.children.link(self.fcstd_collection)
2045 |
2046 | if not self.link_targets:
2047 | self.link_targets = bpy.data.collections.new(link_targets_label)
2048 | self.fcstd_collection.children.link(self.link_targets)
2049 | # hide this internal object.
2050 | # we use only the instances..
2051 | self.link_targets.hide_render = False
2052 | self.link_targets.hide_select = True
2053 | self.link_targets.hide_viewport = False
2054 | # exclude from all view layers
2055 | for lc in helper.find_layer_collection_in_scene(
2056 | collection_name=link_targets_label
2057 | ):
2058 | lc.exclude = True
2059 |
2060 | def prepare_root_empty(self):
2061 | """Prepare import file root empty."""
2062 | func_data = {
2063 | "obj": None,
2064 | "parent_obj": None,
2065 | "parent_bobj": None,
2066 | "collection": self.fcstd_collection,
2067 | "pre_line": "",
2068 | }
2069 | self.fcstd_empty = self.parent_empty_add_or_update(func_data, self.doc_filename)
2070 |
2071 | def append_path(self, path, sub=""):
2072 | if path and sub:
2073 | path = os.path.join(path, sub)
2074 | print("full path:", path)
2075 | if path and os.path.exists(path):
2076 | if os.path.isfile(path):
2077 | path = os.path.dirname(path)
2078 | print("configured path:", path)
2079 | if path not in sys.path:
2080 | sys.path.append(path)
2081 | else:
2082 | self.config["report"](
2083 | {"WARNING"}, ("Path does not exist. Please check! " "'{}'".format(path))
2084 | )
2085 |
2086 | def prepare_freecad_path(self):
2087 | """Find FreeCAD libraries."""
2088 | # https://github.com/s-light/io_import_fcstd/issues/11
2089 |
2090 | # check user specified location specified in addon preferences
2091 |
2092 | # try snap
2093 | # "/snap/freecad/current/usr/lib/"
2094 |
2095 | # try appimage
2096 | self.appimage_mounted = False
2097 | # my.AppImage --appimage-mount
2098 | # use mountingpoitn.
2099 |
2100 | # try flatpack
2101 |
2102 | # set path to new location
2103 | # self.path_to_freecad
2104 | # self.path_to_system_packages
2105 |
2106 | def prepare_freecad_import(self):
2107 | """Prepare FreeCAD import."""
2108 | self.append_path(self.path_to_freecad)
2109 | self.append_path(self.path_to_system_packages)
2110 |
2111 | def cleanup_freecad_import(self):
2112 | """Cleanup if nessesary."""
2113 | if self.appimage_mounted:
2114 | pass
2115 |
2116 |
2117 | def handle_additonal_paths(self):
2118 | """Prepare more paths for import."""
2119 | import FreeCAD
2120 |
2121 | path_base = FreeCAD.getResourceDir() # noqa
2122 | # https://wiki.freecadweb.org/PySide
2123 | # /usr/share/freecad-daily/Ext/PySide
2124 | self.append_path(path_base, "Ext")
2125 | self.append_path(path_base, "Mod")
2126 |
2127 | def import_extras(self):
2128 | """Import additional things."""
2129 | self.handle_additonal_paths()
2130 | try:
2131 | import Part # noqa
2132 |
2133 | # import PartDesign # noqa
2134 | import Draft # noqa
2135 | import Arch # noqa
2136 | except ModuleNotFoundError as e:
2137 | self.config["report"](
2138 | {"ERROR"},
2139 | "Unable to import one of the additional modules. \n"
2140 | "\n"
2141 | "Make sure it can be found by Python, \n"
2142 | "you might need to set its path in this Addon preferences.. "
2143 | "(User preferences->Addons->expand this addon).\n"
2144 | "\n" + str(e),
2145 | )
2146 | return {"CANCELLED"}
2147 | except Exception as e:
2148 | self.config["report"]({"ERROR"}, "Import Failed.\n" "\n" + str(e))
2149 | return {"CANCELLED"}
2150 |
2151 | def import_fcstd(self, filename=None):
2152 | """Read a FreeCAD .FCStd file and creates Blender objects."""
2153 | if filename:
2154 | self.config["filename"] = filename
2155 |
2156 | try:
2157 | self.prepare_freecad_path()
2158 | self.prepare_freecad_import()
2159 | import FreeCAD
2160 | except ModuleNotFoundError as e:
2161 | self.config["report"](
2162 | {"ERROR"},
2163 | "Unable to import the FreeCAD Python module. \n"
2164 | "\n"
2165 | "Make sure FreeCAD is installed on your system! \n"
2166 | "and compiled with Python3 (same version as Blender).\n"
2167 | "We tried to search for it - \n"
2168 | "but maybee its easier to set its path in this Addon preferences "
2169 | "(User preferences->Addons->expand this addon).\n"
2170 | "\n" + str(e),
2171 | )
2172 | return {"CANCELLED"}
2173 | except Exception as e:
2174 | self.config["report"]({"ERROR"}, "Import Failed.\n" "\n" + str(e))
2175 | return {"CANCELLED"}
2176 | finally:
2177 | self.cleanup_freecad_import()
2178 |
2179 | self.import_extras()
2180 |
2181 | self.guidata = guidata.load_guidata(
2182 | self.config["filename"], self.config["report"],
2183 | )
2184 |
2185 | # Context Managers not implemented..
2186 | # see https://docs.python.org/3.8/reference/compound_stmts.html#with
2187 | # with FreeCAD.open(self.config["filename"]) as doc:
2188 | # so we use the classic try finally block:
2189 | try:
2190 | # doc = FreeCAD.open(
2191 | # "/home/stefan/mydata/freecad/tests/linking_test/Linking.FCStd")
2192 | self.config["report"](
2193 | {"INFO"}, "open FreeCAD file. '{}'" "".format(self.config["filename"])
2194 | )
2195 | try:
2196 | doc = FreeCAD.open(self.config["filename"])
2197 | except Exception as e:
2198 | print(e)
2199 | docname = doc.Name
2200 | if doc:
2201 | self.doc_filename = doc.Name + ".FCStd"
2202 | self.config["report"](
2203 | {"INFO"},
2204 | "File '{}' successfully opened." "".format(self.doc_filename),
2205 | )
2206 | self.doc = doc
2207 | # self.print_debug_report()
2208 | self.config["report"]({"INFO"}, "recompute..")
2209 | self.doc.recompute()
2210 | # self.config["report"]({'INFO'}, "importLinks..")
2211 | # self.doc.importLinks()
2212 | # importLinks is currently not reliable..
2213 | # self.config["report"]({'INFO'}, "recompute..")
2214 | # self.doc.recompute()
2215 | self.prepare_collection()
2216 | self.prepare_root_empty()
2217 | self.import_doc_content(doc)
2218 | else:
2219 | self.config["report"](
2220 | {"ERROR"},
2221 | "Unable to open the given FreeCAD file '{}'"
2222 | "".format(self.config["filename"]),
2223 | )
2224 | return {"CANCELLED"}
2225 | except Exception as e:
2226 | self.config["report"]({"ERROR"}, str(e))
2227 | raise e
2228 | finally:
2229 | FreeCAD.closeDocument(docname)
2230 | print("Import finished.")
2231 | return {"FINISHED"}
2232 |
2233 |
2234 | def main_test():
2235 | """Tests."""
2236 | pass
2237 |
2238 |
2239 | if __name__ == "__main__":
2240 | main_test()
2241 |
--------------------------------------------------------------------------------
/import_fcstd/guidata.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 | """XML handler."""
5 |
6 | import xml.sax
7 | import zipfile
8 |
9 |
10 | class FreeCAD_xml_handler(xml.sax.ContentHandler):
11 | """
12 | A XML handler to process the FreeCAD GUI xml data.
13 |
14 | this creates a dictionary where each key is a FC object name,
15 | and each value is a dictionary of property:value pairs
16 | """
17 |
18 | def __init__(self):
19 | """Init."""
20 | self.guidata = {}
21 | self.current = None
22 | self.properties = {}
23 | self.currentprop = None
24 | self.currentval = None
25 |
26 | def startElement(self, tag, attributes):
27 | """Call when an element starts."""
28 | if tag == "ViewProvider":
29 | self.current = attributes["name"]
30 | elif tag == "Property":
31 | name = attributes["name"]
32 | element_names = [
33 | "Visibility",
34 | "ShapeColor",
35 | "Transparency",
36 | "DiffuseColor"
37 | ]
38 | if name in element_names:
39 | self.currentprop = name
40 | elif tag == "Bool":
41 | if attributes["value"] == "true":
42 | self.currentval = True
43 | else:
44 | self.currentval = False
45 | elif tag == "PropertyColor":
46 | c = int(attributes["value"])
47 | r = float((c >> 24) & 0xFF)/255.0
48 | g = float((c >> 16) & 0xFF)/255.0
49 | b = float((c >> 8) & 0xFF)/255.0
50 | self.currentval = (r, g, b)
51 | elif tag == "Integer":
52 | self.currentval = int(attributes["value"])
53 | elif tag == "Float":
54 | self.currentval = float(attributes["value"])
55 | elif tag == "ColorList":
56 | self.currentval = attributes["file"]
57 |
58 | def endElement(self, tag):
59 | """Call when an elements ends."""
60 | if tag == "ViewProvider":
61 | if self.current and self.properties:
62 | self.guidata[self.current] = self.properties
63 | self.current = None
64 | self.properties = {}
65 | elif tag == "Property":
66 | if self.currentprop and (self.currentval is not None):
67 | self.properties[self.currentprop] = self.currentval
68 | self.currentprop = None
69 | self.currentval = None
70 |
71 |
72 | def load_guidata(filename, report):
73 | """Check if we have a GUI document."""
74 | report({'INFO'}, "load guidata..")
75 | guidata = None
76 | zdoc = zipfile.ZipFile(filename)
77 | if zdoc:
78 | if "GuiDocument.xml" in zdoc.namelist():
79 | gf = zdoc.open("GuiDocument.xml")
80 | guidata = gf.read()
81 | gf.close()
82 | Handler = FreeCAD_xml_handler()
83 | xml.sax.parseString(guidata, Handler)
84 | guidata = Handler.guidata
85 | for key, properties in guidata.items():
86 | # open each diffusecolor files and retrieve values
87 | # first 4 bytes are the array length,
88 | # then each group of 4 bytes is abgr
89 | if "DiffuseColor" in properties:
90 | # print ("opening:",guidata[key]["DiffuseColor"])
91 | df = zdoc.open(guidata[key]["DiffuseColor"])
92 | buf = df.read()
93 | # print (buf," length ",len(buf))
94 | df.close()
95 | cols = []
96 | for i in range(1, int(len(buf)/4)):
97 | cols.append(
98 | (buf[i*4+3], buf[i*4+2], buf[i*4+1], buf[i*4]))
99 | guidata[key]["DiffuseColor"] = cols
100 | zdoc.close()
101 | report({'INFO'}, "load guidata done.")
102 | # print("guidata:", guidata)
103 | return guidata
104 |
--------------------------------------------------------------------------------
/import_fcstd/helper.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 | """Helper."""
5 |
6 | import bpy
7 |
8 |
9 | def rename_old_data(data, data_label):
10 | """Recusive add '_old' to data object."""
11 | name_old = None
12 | if data_label in data:
13 | name_old = data[data_label].name + "_old"
14 | if name_old in data:
15 | # rename recusive..
16 | rename_old_data(data, name_old)
17 | data[data_label].name = name_old
18 | return name_old
19 |
20 |
21 | def find_layer_collection_recusive(*, collection_name, layer_collection):
22 | """Recursivly transverse layer_collection for a particular name."""
23 | result_layer_collection = None
24 | if layer_collection.name == collection_name:
25 | result_layer_collection = layer_collection
26 | else:
27 | for lc in layer_collection.children:
28 | temp = find_layer_collection_recusive(
29 | collection_name=collection_name,
30 | layer_collection=lc
31 | )
32 | if temp:
33 | result_layer_collection = temp
34 | return result_layer_collection
35 |
36 |
37 | # def find_layer_collection_in_view_layer(collection_name, view_layer):
38 | # """Find layer_collection in view_layer."""
39 | # result_layer_collection = find_layer_collection_recusive(
40 | # collection_name=collection_name,
41 | # layer_collection=view_layer.layer_collection
42 | # )
43 | # return result_layer_collection
44 |
45 |
46 | def find_layer_collection_in_scene(*, collection_name, scene=None):
47 | """Find layer_collection in scene."""
48 | result_layer_collections = []
49 | if scene is None:
50 | # use active scene
51 | scene = bpy.context.scene
52 | for view_layer in scene.view_layers:
53 | result_layer_collections.append(
54 | find_layer_collection_recusive(
55 | collection_name=collection_name,
56 | layer_collection=view_layer.layer_collection
57 | )
58 | )
59 | return result_layer_collections
60 |
--------------------------------------------------------------------------------
/import_fcstd/material.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 | """Material Things."""
5 |
6 | import bpy
7 | from bpy_extras.node_shader_utils import PrincipledBSDFWrapper
8 |
9 | from .. import blender_helper as b_helper
10 |
11 | # from . import helper
12 |
13 |
14 | class MaterialManager(object):
15 | """
16 | Handle all Material related things.
17 | """
18 |
19 | def __init__(
20 | self,
21 | guidata,
22 | func_data,
23 | bobj,
24 | obj_label,
25 | sharemats,
26 | report=None,
27 | report_preline="",
28 | ):
29 | """Init."""
30 | self.report_fnc = report
31 | self.report_preline = report_preline
32 | self.guidata = guidata
33 | self.func_data = func_data
34 | self.bobj = bobj
35 | self.obj_label = obj_label
36 | self.sharemats = sharemats
37 |
38 | def report(self, data, mode=None, pre_line=None):
39 | if not mode:
40 | mode = {"INFO"}
41 | if not pre_line:
42 | pre_line = self.report_preline
43 | else:
44 | pre_line = self.report_preline + pre_line
45 | if self.report_fnc:
46 | return self.report_fnc(mode, data, pre_line=pre_line)
47 | else:
48 | print(pre_line + data)
49 |
50 | # material
51 | def get_obj_Transparency(self, obj_Name):
52 | """Get object Transparency and convert to blender units."""
53 | alpha = 1.0
54 | if "Transparency" in self.guidata[obj_Name]:
55 | if self.guidata[obj_Name]["Transparency"] > 0:
56 | alpha = (100 - self.guidata[obj_Name]["Transparency"]) / 100.0
57 | return alpha
58 |
59 | def get_obj_ShapeColor(self, obj_Name):
60 | """Get object ShapeColor and convert to blender units."""
61 | rgb = (0.5, 0.5, 0.5)
62 | if "ShapeColor" in self.guidata[obj_Name]:
63 | rgb = self.guidata[obj_Name]["ShapeColor"]
64 | return rgb
65 |
66 | def get_obj_DiffuseColor(self, obj_Name, i):
67 | """Get object DiffuseColor and convert to blender units."""
68 | # DiffuseColor stores int values, Blender use floats
69 | rgba = tuple(
70 | [float(x) / 255.0 for x in self.guidata[obj_Name]["DiffuseColor"][i]]
71 | )
72 | return rgba
73 |
74 | def get_obj_rgba(self, obj_Name, mat_index=None):
75 | """Get object rgba value in blender usable format."""
76 | if mat_index:
77 | rgba = self.get_obj_DiffuseColor(obj_Name, mat_index)
78 | # FreeCAD stores transparency, not alpha
79 | alpha = 1.0
80 | if rgba[3] > 0:
81 | alpha = 1.0 - rgba[3]
82 | rgba = rgba[:3] + (alpha,)
83 | else:
84 | alpha = self.get_obj_Transparency(obj_Name)
85 | rgb = self.get_obj_ShapeColor(obj_Name)
86 | rgba = rgb + (alpha,)
87 | return rgba
88 |
89 | def create_new_bmat(self, bmat_name, rgba):
90 | """Create new blender material."""
91 | bmat = bpy.data.materials.new(name=bmat_name)
92 | bmat.use_nodes = True
93 | # link bmat to PrincipledBSDFWrapper
94 | principled = PrincipledBSDFWrapper(bmat, is_readonly=False)
95 | principled.base_color = rgba[:3]
96 | # check for alpha
97 | if rgba[3] < 1.0:
98 | bmat.diffuse_color = rgba
99 | principled.alpha = rgba[3]
100 | bmat.blend_method = "BLEND"
101 | if self.sharemats:
102 | self.func_data["matdatabase"][rgba] = bmat
103 | return bmat
104 |
105 | def handle_material_per_face(self, face_index, objmats, material_index):
106 | """Handle material for face."""
107 | # Create new mats and attribute faces to them
108 | # DiffuseColor stores int values, Blender use floats
109 | # self.report(
110 | # b_helper.colors.fg.lightblue
111 | # + "handle_material_per_face"
112 | # + b_helper.colors.reset,
113 | # pre_line="| ",
114 | # )
115 | rgba = self.get_obj_rgba(self.func_data["obj"].Name, material_index)
116 | # get or create blender material
117 | bmat = None
118 | if self.sharemats:
119 | if rgba in self.func_data["matdatabase"]:
120 | bmat = self.func_data["matdatabase"][rgba]
121 | if rgba not in objmats:
122 | objmats.append(rgba)
123 | self.bobj.data.materials.append(bmat)
124 | if not bmat:
125 | if rgba in objmats:
126 | bmat = self.bobj.data.materials[objmats.index(rgba)]
127 | if not bmat:
128 | bmat_name = self.obj_label + "_" + str(len(objmats))
129 | bmat = self.create_new_bmat(bmat_name, rgba)
130 | objmats.append(rgba)
131 | self.bobj.data.materials.append(bmat)
132 |
133 | # at this point we should have a valid blender material
134 |
135 | # self.report(
136 | # b_helper.colors.fg.lightblue
137 | # + "objmats "
138 | # + b_helper.colors.reset
139 | # + "{}".format(objmats),
140 | # pre_line="| ",
141 | # )
142 | # self.report(
143 | # b_helper.colors.fg.lightblue
144 | # + "bmat "
145 | # + b_helper.colors.reset
146 | # + "{}".format(bmat),
147 | # pre_line="| ",
148 | # )
149 | # self.report(
150 | # b_helper.colors.fg.lightblue
151 | # + "face_index "
152 | # + b_helper.colors.reset
153 | # + "{}".format(face_index),
154 | # pre_line="| ",
155 | # )
156 |
157 | # assigne materials to polygons
158 | objmats_index = objmats.index(rgba)
159 | # self.report(
160 | # b_helper.colors.fg.lightblue
161 | # + "objmats_index "
162 | # + b_helper.colors.reset
163 | # + "{}".format(objmats_index),
164 | # pre_line="| ",
165 | # )
166 | # self.report(
167 | # b_helper.colors.fg.lightblue
168 | # + 'self.func_data["matindex"][material_index] '
169 | # + b_helper.colors.reset
170 | # + "{}".format(self.func_data["matindex"][material_index]),
171 | # pre_line="| ",
172 | # )
173 |
174 | for fj in range(self.func_data["matindex"][material_index]):
175 | # self.report(
176 | # b_helper.colors.fg.lightblue
177 | # + "fj "
178 | # + b_helper.colors.reset
179 | # + "{}".format(fj),
180 | # pre_line="| * ",
181 | # )
182 | # self.report(
183 | # b_helper.colors.fg.lightblue
184 | # + "face_index + fj "
185 | # + b_helper.colors.reset
186 | # + "{}".format(face_index + fj),
187 | # pre_line="| * ",
188 | # )
189 | self.bobj.data.polygons[face_index + fj].material_index = objmats_index
190 | face_index += self.func_data["matindex"][material_index]
191 | return face_index
192 |
193 | def handle_material_multi(self):
194 | """Handle multi material."""
195 | # we have per-face materials.
196 | # self.report(
197 | # b_helper.colors.fg.lightgreen
198 | # + "handle_material_multi"
199 | # + b_helper.colors.reset,
200 | # pre_line="| ",
201 | # )
202 | face_index = 0
203 | objmats = []
204 | for material_index in range(len(self.func_data["matindex"])):
205 | face_index = self.handle_material_per_face(
206 | face_index, objmats, material_index
207 | )
208 |
209 | def handle_material_single(self):
210 | """Handle single material."""
211 | # one material for the whole object
212 | self.report(
213 | b_helper.colors.fg.lightgreen
214 | + "handle_material_single"
215 | + b_helper.colors.reset
216 | )
217 | rgba = self.get_obj_rgba(self.func_data["obj"].Name)
218 | bmat = None
219 | if self.sharemats:
220 | if rgba in self.func_data["matdatabase"]:
221 | bmat = self.func_data["matdatabase"][rgba]
222 | else:
223 | # print("not found in db:",rgba,"in",matdatabase)
224 | pass
225 | if not bmat:
226 | bmat_name = self.obj_label
227 | bmat = self.create_new_bmat(bmat_name, rgba)
228 | self.bobj.data.materials.append(bmat)
229 |
230 | def create_new(self):
231 | """Handle material creation."""
232 | # check if we have a material at all...
233 | # self.report(
234 | # b_helper.colors.fg.lightgreen
235 | # + "create_new material"
236 | # + b_helper.colors.reset
237 | # )
238 | if self.func_data["obj"].Name in self.guidata:
239 | # check if we have 'per face' or 'object' coloring.
240 | # self.report(
241 | # b_helper.colors.bold
242 | # + b_helper.colors.fg.lightblue
243 | # + 'self.func_data["matindex"]'
244 | # + " ({}): ".format(len(self.func_data["matindex"]))
245 | # + b_helper.colors.reset
246 | # + "{}".format(self.func_data["matindex"])
247 | # )
248 | # # ############
249 | # # list colors:
250 | # self.report(
251 | # b_helper.colors.bold
252 | # + b_helper.colors.fg.lightblue
253 | # + 'self.guidata[self.func_data["obj"].Name]["DiffuseColor"]'
254 | # + " ({}):".format(
255 | # len(self.guidata[self.func_data["obj"].Name]["DiffuseColor"])
256 | # )
257 | # + b_helper.colors.reset
258 | # )
259 | # for index, color in enumerate(
260 | # self.guidata[self.func_data["obj"].Name]["DiffuseColor"]
261 | # ):
262 | # self.report(" {:>3} {}".format(index, color))
263 | # # ############
264 | #
265 | # self.report(
266 | # b_helper.colors.fg.lightblue
267 | # + "self.bobj.data.polygons "
268 | # + b_helper.colors.reset
269 | # + "{}".format(self.bobj.data.polygons)
270 | # )
271 |
272 | # # create a list with all faces
273 | # face_list = [face for face in self.bobj.data.polygons]
274 | # self.report(
275 | # b_helper.colors.fg.lightblue + "face_list " + b_helper.colors.reset
276 | # )
277 | # for index, face in enumerate(face_list):
278 | # self.report(" {:>3} {}".format(index, face))
279 | # # ############
280 |
281 | # check for multi material
282 | if (
283 | self.func_data["matindex"]
284 | and ("DiffuseColor" in self.guidata[self.func_data["obj"].Name])
285 | and (
286 | len(self.func_data["matindex"])
287 | == len(self.guidata[self.func_data["obj"].Name]["DiffuseColor"])
288 | )
289 | ):
290 | self.handle_material_multi()
291 | else:
292 | self.handle_material_single()
293 |
--------------------------------------------------------------------------------
/pylama.ini:
--------------------------------------------------------------------------------
1 | [pylama:pycodestyle]
2 | max_line_length = 100
3 |
4 | [pylama:pylint]
5 | max_line_length = 100
6 |
7 | [pylama:pep8]
8 | max_line_length = 100
9 |
--------------------------------------------------------------------------------