Compare commits

...

378 Commits

Author SHA1 Message Date
Andrej730 bd093303c4 UI info message when patch execution was finished 2024-02-13 12:33:36 +05:00
Andrej730 c5c7363a93 ifctester Attribute.filter not to check subtypes attributes
if attribute is already found in the super type
2024-02-13 12:25:32 +05:00
Andrej730 5014fef129 Fix #4314
It was looking for IFC entities with the attribute from Attribute.name but wasn't considering ifc subclasses that inherit from that entity.
2024-02-13 12:24:58 +05:00
Andrej730 d710a01df5 ifctester - some typing 2024-02-13 12:24:58 +05:00
Andrej730 23c65742e1 CMake - disable optimization for RelWithDebInfo with ENABLE_BUILD_OPTIMIZATIONS=Off 2024-02-12 14:23:21 +05:00
Andrej730 d8f52afa6c Fixed #4310 2024-02-12 12:15:27 +05:00
Andrej730 29ce8d5d80 Fix bug removing relating object of IfcRelNests
it was unlinking current collection of the related object and related objects were missing after
2024-02-12 12:01:04 +05:00
Andrej730 5e5de0bbd8 root.remove_product - fix some cases producing invalid ifc #4313
always remove IfcRelNests
1) if product is RelatingObject
2) if product is the only RelatedObject
2024-02-12 11:40:25 +05:00
Ryan Schultz b9b58f5726 proposed fix for #4311 2024-02-11 13:36:23 -06:00
Massimo Fabbro 5c7271ddc8 See #4007. Now clone opening works also when a opening hosts a window or a door 2024-02-11 14:43:12 +01:00
Massimo Fabbro 2db35556e9 Clone opening now deletes also old opening placement 2024-02-11 14:43:12 +01:00
Massimo Fabbro 2449656311 Small refactor clone opening operator in order to better understand and reuse it rebase 2024-02-11 14:43:12 +01:00
Gorgious56 c66cf0a22b Assigning an ifc class to an object now displays a message if the object is not in OBJECT mode 2024-02-09 17:26:48 +01:00
Andrej730 e1a1ba62c5 add fallback to the previous schema versions for docs and mappings
before it was breaking part of BlenderBIM UI if we would open IFC4X1 file
2024-02-09 12:12:02 +05:00
Andrej730 6489f4dc1d bump py version in run-cmake.bat after c0d84713b 2024-02-08 16:39:27 +05:00
Andrej730 beab99bb0d Fix issue building IfcOpenHouse for IFC4 2024-02-07 13:34:19 +01:00
Andrej730 8b50f1bcd6 Fix bug in IfcHierarchyHelper.addRelatedObject
Noticed that `file.addRelatedObject<IfcSchema::IfcRelDefinesByType>(door_style, door);` was resulting in list with `door` assigned as RelatingObject and `door_style` assigned as RelatedObjects.
2024-02-07 17:10:16 +05:00
Andrej730 4617834578 MergeDuplicateTypes - fix error occuring if ifcopenshell.api is not imported
error was:
File "\site-packages\ifcpatch\__init__.py", line 78, in execute
    patcher.patch()
  File "\site-packages\ifcpatch\recipes\MergeDuplicateTypes.py", line 73, in patch
    self.assign_type(element, original_type)
  File "\site-packages\ifcpatch\recipes\MergeDuplicateTypes.py", line 114, in assign_type
    ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": types[0]})
    ^^^^^^^^^^^^^^^^
AttributeError: module 'ifcopenshell' has no attribute 'api'
2024-02-07 12:20:11 +05:00
Andrej730 426dcbee43 ifcopenshell installation notion about compiling only specific schemas 2024-02-07 12:20:11 +05:00
Andrej730 2a12151c07 cmake - clean up trailing spaces 2024-02-07 12:16:55 +05:00
Andrej730 c08958c1bd fix cmake warning with CMP0144 policy
The warning was:
```
CMake Warning (dev) at CMakeLists.txt:299 (find_package):
  Policy CMP0144 is not set: find_package uses upper-case <PACKAGENAME>_ROOT
  variables.  Run "cmake --help-policy CMP0144" for policy details.  Use the
  cmake_policy command to set the policy and suppress this warning.

  CMake variable BOOST_ROOT is set to:

    \IfcOpenShell\_deps\boost_1_74_0

  For compatibility, find_package is ignoring the variable, but code in a
  .cmake module might still use it.
```
2024-02-07 12:16:55 +05:00
Andrej730 e355a33d48 bump required cmake version to remove deprecation warning
deprecation warning:
CMake Deprecation Warning at CMakeLists.txt:20 (cmake_minimum_required):
  Compatibility with CMake < 3.5 will be removed from a future version of
  CMake.

  Update the VERSION argument <min> value or use a ...<max> suffix to tell
  CMake that the project does not need compatibility with older versions.
2024-02-07 12:16:55 +05:00
Andrej730 ece9e2fdd1 build-deps - bump boost version to avoid "missing .b2 issue"
issue was mentioned in #2524 and resulted in error below trying to build boost dependency
```
'.\b2' is not recognized as an internal or external command,
operable program or batch file.
```
2024-02-07 12:16:54 +05:00
Andrej730 c0d84713bc bump python version in windows build dependencies
they seem to switch to .exe from .msi starting from 3.5+, changed amd prefix. Also fixed version parsing line for two digits minor versions
2024-02-07 12:16:54 +05:00
Andrej730 ca9f165b6a ifcpatch.execute - make input attribute optional
e.g. MergeDuplicateTypes  doesn't use it in the code either way
2024-02-07 12:16:54 +05:00
dependabot[bot] 8f9c97a663 Bump fastapi from 0.101.0 to 0.109.1 in /src/opencdeserver/api/app
Bumps [fastapi](https://github.com/tiangolo/fastapi) from 0.101.0 to 0.109.1.
- [Release notes](https://github.com/tiangolo/fastapi/releases)
- [Commits](https://github.com/tiangolo/fastapi/compare/0.101.0...0.109.1)

---
updated-dependencies:
- dependency-name: fastapi
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-07 09:31:38 +11:00
marwiss 4ed8310b93 Correct protocol to encrypted HTTPS 2024-02-06 09:55:11 +11:00
marwiss 31be899dc9 Replace existing code in src/opencdeserver with code from the "Kontroll OpenCDE API:s server" POC project on https://github.com/marwiss/Kontroll 2024-02-06 09:55:11 +11:00
Andrej730 6d7a6eeda7 fixed bug removing representation item
if you'd click on "remove item" button for some item it wouldn't remove the corresponding item but would go for removing active item instead
2024-02-02 14:53:10 +05:00
Andrej730 c520e0eeb5 some refactor for bbim drawings svg serialization
Made some changes to svg serialization code by moving chunks of code so `.generate_linework()` will be easier to read
- moved gathering linework contexts to a separate method
- moved common code for elements serialization to a function
- replaced self.yield_from_iterator(it) with it.__iter__

Functionally code is exactly the same.
2024-02-02 12:56:06 +05:00
Andrej730 73817acdff add a note on get_info_2() #4270 2024-02-01 17:19:35 +05:00
Andrej730 3d7e8d7c6c Fixed #4259
Result of DeepDiff contain ordered sets. Also added fallback for usual sets to lists just in case, should be safe.
2024-01-30 15:41:08 +05:00
Andrej730 a701840826 Fixed error adding a boolean
It occured if active object was IFC object and second selected object was just Blender object.
2024-01-30 14:40:07 +05:00
Andrej730 5de2ac9042 black format 2024-01-30 14:36:35 +05:00
Andrej730 573e2626ad Fix test Model.get_manual_booleans after we added pset to store them 2024-01-30 14:36:35 +05:00
Andrej730 9de59afd49 Fix #4267 and add tests
Crash was caused by unmark_manual_booleans accessing boolean element after they were deleted from IFC
2024-01-30 14:36:35 +05:00
Andrej730 74a9671075 Update ui.py 2024-01-30 14:36:35 +05:00
Andrej730 799658ff41 display active representation presentation layer in UI
example - https://i.imgur.com/mpnoqPH.png
2024-01-30 14:36:35 +05:00
Andrej730 bd64f42a54 remove_representation to consider representation item's layers too
and purge layers if they are out of items as they become invalid
2024-01-30 14:36:35 +05:00
Andrej730 8eab7f155e Small presentation layers UI fixes 2024-01-30 14:36:35 +05:00
Andrej730 5351966430 access layer assignments from inverse attribute instead of going through representation inverses 2024-01-30 14:36:35 +05:00
Andrej730 2663c079f4 test for removing representation items 2024-01-29 16:47:17 +05:00
Andrej730 6747bd9c8f UI to remove representation items
example - https://i.imgur.com/jJDpIQP.png
2024-01-29 16:47:16 +05:00
Andrej730 6ece295be4 tests for creating shape aspects and adding items 2024-01-29 15:52:40 +05:00
Andrej730 64a9f85f25 create new shape aspects from UI
example - https://i.imgur.com/BJJZWrC.png
2024-01-29 15:52:39 +05:00
Andrej730 d8fa853308 edit shape aspect attributes from UI
example - https://i.imgur.com/SzUEn6r.png
2024-01-29 15:52:39 +05:00
Andrej730 ce37c49624 Fix #4262 after 0da3270c7 2024-01-29 11:13:22 +05:00
Andrej730 0da3270c7f UI for changing representation item's shape aspect
Example - https://imgur.com/a/tN7hCYz
2024-01-26 17:32:46 +05:00
Andrej730 fda6fc6c48 geometry.remove_representation documentation 2024-01-26 17:20:39 +05:00
Andrej730 3865f01df9 operator to remove representation item from shape aspect 2024-01-26 17:20:39 +05:00
Andrej730 6bf61d7163 Simple UI to display repr item shape aspect
Example - https://i.imgur.com/YEAE9zU.png
2024-01-26 15:27:05 +05:00
Andrej730 b8ad73668f BBIM to print console errors in case IFC file is failed to load 2024-01-26 14:54:51 +05:00
Andrej730 b7a3dd4164 black format 2024-01-26 14:54:50 +05:00
Bruno Postle 566fd443db IfcGit avoid leaving stray .git/config.lock files 2024-01-25 23:30:00 +00:00
Andrej730 51b5a507cf shape builder docs 2024-01-25 16:46:38 +05:00
Andrej730 a44357dad5 shape builder polyline example and tests 2024-01-25 16:46:34 +05:00
Andrej730 8e792ebb50 UI for changing styles for representation items
Example - https://imgur.com/a/SQh6rR2
2024-01-25 15:17:44 +05:00
Andrej730 352321364c fix couple building docs errors / warnings 2024-01-24 16:38:53 +05:00
Andrej730 2ab32e41b2 shape builder docs fixes 2024-01-24 16:38:53 +05:00
Andrej730 3fbd9d7cdf IFC4 Landscape Library
Created IFC library with simple trees, shrubs and hedges - with both 2d and 3d representations. Example - https://i.imgur.com/pOjh1si.png
Now it's available along with the furniture library when creating new project - https://i.imgur.com/Ho0B5Gn.png
2024-01-24 16:38:52 +05:00
Dion Moult 7a7fde0a2a See #4247. Loading models now skips excessively voided objects for performance. 2024-01-24 10:39:31 +11:00
Ryan Schultz 7992bd9902 Give user additional feedback when trying to duplicate an IFCproject and Drawing. Also deselects the object to prevent inadvertently moving it. 2024-01-23 08:04:12 -06:00
Dion Moult 3182fe9c15 Fix bug where merging by type when loading broke if a type didn't have a name 2024-01-23 19:45:31 +11:00
Dion Moult e8858aeeb3 Fix bug where loading filtered IFC2X3 models didn't work. 2024-01-23 17:39:13 +11:00
Dion Moult ec79d61095 Fix #2719. Indicator whether or not IFC is dirty to help users know when to save. 2024-01-23 16:29:42 +11:00
Dion Moult 266236ee49 See #4241. Only use shapely to repolygonise if IfcOpenShell returns unclosed paths. Hollow cuts typically are closed paths. 2024-01-23 16:06:54 +11:00
Dion Moult 192e4e43a4 Fix #4239. Stop users from duplicating IfcProject. 2024-01-23 12:30:25 +11:00
Dion Moult b22c92e951 Fix regression where you couldn't unlink an object that already had a broken element link, and bug where you couldn't unlink a spatial element or similar with its own collection. 2024-01-23 12:27:09 +11:00
Dion Moult 7634e71434 See #4239. Ensure that all copy pasted objects are unlinked. Use duplicate if you want to copy. 2024-01-23 12:25:46 +11:00
Dion Moult dda803df2e See 0028fd3. Fix regression when duplicating objects without connections. 2024-01-23 09:05:40 +11:00
Andrej730 c3ded31a6a static ifcopenshell.geom.serializers and some typing for IDE code hints
code itself should work exactly the same way
2024-01-22 14:24:24 +05:00
Thomas Krijnen f2d19ab2c3 #4216 todo note 2024-01-22 09:38:05 +01:00
Dion Moult ab80400e16 See #4240. Guard against possible user creating invalid userdefined attribute values. 2024-01-21 21:56:45 +11:00
Dion Moult c88496c6a6 See #4240. Fix validation issue with perspective drawing pyramid booleans. Boolean clipping results can only be used on swept solids. 2024-01-21 19:13:40 +11:00
Dion Moult 0ee07fb8cc See #4240. Fix bug where 2D arbitrary profiles were invalid IFC (used 3D point lists not 2D) 2024-01-21 16:07:00 +11:00
Dirk Olbrich 36cf458498 docs: include doxygen-awesome as git submodule 2024-01-20 14:05:50 +01:00
Dirk Olbrich e7c25bf99f docs: add README with instructions 2024-01-20 14:05:50 +01:00
Dirk Olbrich 50a3c8c2f3 docs: modify doxygen config to new folder 2024-01-20 14:05:50 +01:00
Dirk Olbrich f18298d1fd git: clean up gitignore 2024-01-20 14:05:50 +01:00
Dirk Olbrich d4770bdee3 docs: move C++ API docs to subfolder 2024-01-20 14:05:50 +01:00
Dirk Olbrich 1fb107c361 docs: include doxygen-awesome theme 2024-01-20 14:05:50 +01:00
Dirk Olbrich 0fa1e2d585 docs: remove duplicate cmake for Sphinx setup 2024-01-20 14:05:50 +01:00
Dirk Olbrich dc13f4c5cc docs: remove geom setting docs and images
- these are duplicated in /src/ifcopenshell-python/docs/ifcopenshell/
2024-01-20 14:05:50 +01:00
Dirk Olbrich d197c3f49a docs: update doxygen file to 1.9.8 2024-01-20 14:05:50 +01:00
Dirk Olbrich c05200a5c6 docs: set LOOKUP_CACHE_SIZE 2024-01-20 14:05:50 +01:00
Dirk Olbrich 235bb198da docs: update Doxyfile to v1.9.4
- specify ./output/doxygen as main path for output
- use relative paths for subfolder output
2024-01-20 14:05:50 +01:00
Dion Moult 40f332d674 Fix #3947. Do slight 2mm offset to account for Z-fighting on 2D plan views and RCPs. 2024-01-20 21:53:34 +11:00
Dion Moult e8a272dc97 Fix #4213. Allow having separate windows to trigger drawing operators. 2024-01-20 15:06:47 +11:00
Dion Moult 0b308770a6 See #4216. Document using OpenCASCADE geometry serialisation to create geometry. 2024-01-20 14:35:52 +11:00
Dion Moult 96c8323a06 Fix #4221. Only load leaf rows with cost values. Createing a monetary measure with None results in a segfault on Windows. 2024-01-20 14:17:19 +11:00
Dion Moult 25573cd0e0 Fix #4236. Consolidate revert, reload IFC, and reload selected IFC operations.
Reload IFC incrementally (via diff) is moved to the debug panel since it is a dangerous advanced user command.
2024-01-20 13:15:59 +11:00
Dion Moult ff3c7786f6 Fix #4109. Write docs about geometry serialiser. 2024-01-19 23:18:38 +11:00
Dion Moult 5f4dfcf215 Fix #4231. Bug where you couldn't add a door to an element assembly. 2024-01-19 22:47:53 +11:00
Andrej730 9a89555a53 bbim_translations.py - remove operator context from operator tooltips
noticed editing translations from ui that operator tooltip doesn't appear in ui if tooltips has `msgctxt "Operator"`
2024-01-19 16:31:34 +05:00
Dion Moult fc69a2acbe Fix #4127. Bug where IfcTester couldn't save ODS report. 2024-01-19 22:25:03 +11:00
Andrej730 85a2a5ad12 fix build error 2024-01-19 15:11:59 +05:00
Andrej730 49c3d28a0b Merge branch 'localization' into v0.7.0 2024-01-19 15:02:01 +05:00
Andrej730 a8f20b2c6b drop bpy related translations.py<->.po stuff as we won't be using it 2024-01-19 15:00:41 +05:00
Andrej730 66fa1cedd9 run bbim_translations.py without bpy 2024-01-19 15:00:41 +05:00
Andrej730 eb971d3d73 save just a final dictionary to translations.py instead of tuples
We can do that since In our workflow we won't be converting translations.py to .po files, that way it will be much shorter as it's containing only translated strings and doesn't have strings sources.
2024-01-19 13:58:49 +05:00
Andrej730 a30a796c07 generate translations.py without bpy 2024-01-19 13:58:49 +05:00
Andrej730 9e46bacc12 gettext bug fix 2024-01-19 12:06:11 +05:00
Ioannis P. Christovasilis b33378e135 ifc2ca major update - todo: update readme file 2024-01-18 12:53:31 +01:00
Vukas Pajic d42e19f491 Fix #4226 2024-01-18 08:54:24 +01:00
Eduardo Schilling 2597827ab3 Roof slope as percentage property 2024-01-18 09:40:33 +11:00
Dion Moult 3f71efea6d Fix #4227. Add shapely as an optional dependency for IOS-Python 2024-01-18 09:22:35 +11:00
Andrej730 2d279b7a7f update blenderbim build to include translation data 2024-01-17 17:18:04 +05:00
Andrej730 e5fe039fd0 expose current blender locale in bbim translations ui 2024-01-17 17:18:04 +05:00
Andrej730 9e14829cc1 bbim messages parser to also parse gettext 2024-01-17 17:10:26 +05:00
Andrej730 e867602d9d keep order of strings in .pot as they were parsed and store source filepaths 2024-01-17 17:10:26 +05:00
Andrej730 aecc3d56b8 switch to our own string parser instead of blender's 2024-01-17 17:10:26 +05:00
Andrej730 e3f5d3d2b7 rename BlenderBIM translation script/addon 2024-01-17 17:10:26 +05:00
Thomas Krijnen 65f8b1a532 #4222 template kw 2024-01-17 11:10:06 +01:00
Thomas Krijnen 96b4df10ba #4222 typename keywords 2024-01-17 11:02:02 +01:00
Andrej730 98b56aea42 allow missing translations.py as not everyone will need translations for BBIM 2024-01-17 12:07:02 +05:00
Andrej730 ba6ba29da8 auto enable "Manage UI Translations" addon if it's not enabled 2024-01-16 17:34:24 +05:00
Andrej730 1112b102f7 add without-blender-way to generate translations module from po files
though it does require `bpy` module which is available only for python 3.10.
Actually, `bpy` module by itself is not necessary - we just need `bl_i18n_utils` that becomes available after `import `bpy`. So alternatively we can just download that module from Blender https://projects.blender.org/blender/blender/src/branch/main/scripts/modules/bl_i18n_utils
2024-01-16 17:06:43 +05:00
Andrej730 04b8172ea9 black format 2024-01-16 15:28:55 +05:00
Andrej730 f5f3edbc79 check if translation ui is loaded by current Blender session instead of current Blender file
as it's more reliable and thing could change after Blender restart
2024-01-16 15:27:43 +05:00
Andrej730 30029e3dc7 expose i18n path in translator UI
removed bim.open_po_directory operator as it's now available just from alt-clicking on the path browsing button
2024-01-16 15:24:03 +05:00
Andrej730 ed836f5b4a Update bbim_setup_translations.py 2024-01-16 15:18:05 +05:00
Andrej730 a88b708442 fix issues with empty paths accepted as valid paths 2024-01-16 15:15:11 +05:00
Andrej730 764c3bbb85 separate developer/translator ui 2024-01-16 15:08:55 +05:00
Andrej730 382d3f1f9f generate pot file too converting translations.py 2024-01-16 15:04:02 +05:00
Thomas Krijnen c92f607a15 #4222 sprinkle with template keywords 2024-01-16 09:56:50 +01:00
Thomas Krijnen b57b7080fd #4220 weakref to file 2024-01-16 09:44:04 +01:00
Thomas Krijnen 860ff4e513 #4220 Update file.py 2024-01-16 09:31:35 +01:00
Andrej730 3546f13593 change test addon name from localization_test 2024-01-16 11:42:34 +05:00
Dion Moult 663966e08d Fix #4089. Document support for Linux AArch64 on Conda. 2024-01-16 11:19:17 +11:00
Dion Moult f077ebdc81 Fix #4211. Minor documentation fix that clarifies upstream bug about whether qto can be assigned to types. 2024-01-16 10:56:35 +11:00
Dion Moult eb888fd116 Fix #4223. Basic lint error. 2024-01-16 10:50:43 +11:00
Dion Moult 40b6b3bf17 Fix #4224. Fix sequencing lint bugs. 2024-01-16 10:47:50 +11:00
Dion Moult 2ef2f923d0 Fix #4219. Update documentation for creating a new model. 2024-01-16 10:45:13 +11:00
Bruno Postle 75fab58831 IfcGit fixes suggested by ruff 2024-01-15 22:16:19 +00:00
Andrej730 ce26c6e19b BBIM - upgrade to IFC4X3 on loading projects
Added "Upgrate to IFC4X3" button on loading new projects to migrate them to IFC4x3 as Ifc4x3 got approved now will be used more widely.

https://i.imgur.com/mHSIV7T.png
2024-01-15 17:39:49 +05:00
Andrej730 8728fa38b9 Migrator - basic support for migrating attributes ifc4 -> ifc4x3
And added one of the IfcProperty renamed attribute as an example
2024-01-15 17:39:49 +05:00
Dion Moult 3037795199 Fix #4139. Bulk purge orphaned classes and move advanced operators to debug panel. 2024-01-15 23:16:42 +11:00
Andrej730 39ee9cd8b1 Migrator - display some error message for unhandled schemas
Noticed it was resulting in error below migrating to IFC4X3 in some cases as we don't have a mapping for IFC4X3 yet.
```
    if value is None and not attribute.optional():
UnboundLocalError: local variable 'value' referenced before assignment
```
2024-01-15 16:16:29 +05:00
Andrej730 b375bbf558 black format 2024-01-15 16:16:29 +05:00
Thomas Krijnen f2cae79dea #4220 Update entity_instance.py 2024-01-15 12:01:18 +01:00
Andrej730 aadc77f6b6 minor fix for bim.create_all_shapes printed messages 2024-01-15 14:44:40 +05:00
Andrej730 35223d968c semi-automatic setup for necessary directories 2024-01-15 14:43:23 +05:00
Andrej730 e106dc4502 register translations dict only if addon is already loaded 2024-01-15 13:39:27 +05:00
Andrej730 2995b73c2d open .po directory operator 2024-01-15 13:39:27 +05:00
Andrej730 770bc6ea61 Operator to disable BBIM and restart Blender
as it's needed to reload the translations

Update bbim_setup_translations.py

Update bbim_setup_translations.py
2024-01-15 13:38:34 +05:00
Andrej730 f5736a7144 convert setup_translations to blender addon for UI 2024-01-15 12:18:53 +05:00
Dion Moult b4120b1cab Fix #4173. Don't recreate boundaries of building elements that already exist.
Inline opening processing. Minor improvement to detecting attributes for int/ext and physical/virtual.
2024-01-15 15:28:52 +11:00
Dirk Olbrich f44ca67ab9 readme: fix listed ifc version, add link to IFC4x3 docs 2024-01-15 09:20:15 +11:00
Dion Moult 77eae1c8e8 Fix #4195. Forgot to add UI for contains selector. 2024-01-14 23:20:06 +11:00
Dion Moult 02d79f0687 See #4173. Prototype new approach to generating 1st level space boundaries. 2024-01-14 23:12:14 +11:00
mohamadalbaaj 83d364574a Fixing Dimension Mismatch in Matrix Multiplication 2024-01-14 15:08:08 +11:00
Dion Moult 688d2b7bd1 Minor test fix 2024-01-13 16:49:27 +11:00
Dion Moult bf35239979 Fix bug where UI didn't show applicable psets to specific predefined types 2024-01-13 16:49:02 +11:00
Dion Moult ca8e014647 Fix #4195. Fix regressions from f840da3 and add support for "contains string" comparisons. 2024-01-13 15:58:57 +11:00
Dion Moult b5f63ec4f3 Fix #4196. Settings are now saved when you save Spreadsheet export JSON files. Also a new setting to generate an SVG alongside the spreadsheet (intended to be used as a schedule placed on a sheet). 2024-01-13 15:03:51 +11:00
Dion Moult aec2141128 Minor fix 2024-01-13 00:00:58 +11:00
Dion Moult 778344c804 Fix #4197. URLs in SVG schedules are now clickable. 2024-01-12 23:44:45 +11:00
Dion Moult 3ede9b254f Fix #4199. Bug where external references on sheets weren't properly referenced leading to no names. 2024-01-12 19:48:32 +11:00
Dion Moult 8df40dc4a6 Fix #4203. Fix bug where removing nested cost items left orphaned relationships. 2024-01-12 18:14:49 +11:00
Dion Moult 75dd4f3132 Fix #4204. Add support for XLSX schedules on sheets. 2024-01-12 17:26:04 +11:00
Christoph Mellueh be11df3dc1 sort code alphabetically 2024-01-12 08:43:50 +11:00
Christoph Mellueh 09ac212aa9 improve core file for better zen 2024-01-12 08:43:50 +11:00
Christoph Mellueh e159151b44 update BsDD to match new API
+ move Functions from operator to core/tool
2024-01-11 12:00:44 +11:00
Dion Moult 27f6ac0575 Fix #4198. Minor regression in updating text literals. 2024-01-11 09:53:48 +11:00
c4rlosdias f840da3ffd Added more comparisons in search 2024-01-10 23:21:18 +11:00
sennhvi 034df852b2 Update geometry_processing.rst
Add an usage example of the parameter include  for the Geometry Iterator
2024-01-10 23:19:46 +11:00
Christoph Mellueh f242071ebb add pset view to groups 2024-01-10 23:14:33 +11:00
Sigma Dimensions (Yass) b6663e9189 fix counting elements when importing cost schedules ( and run black) 2024-01-10 09:30:03 +01:00
Dion Moult 39d143668c Fix #4186. Support different RL insertion modes when adding occurrences of a type. 2024-01-10 18:53:05 +11:00
Dion Moult 6ea1e378c5 Fix bug where updating type text literals didn't propagate and formatting rounding null values wasn't handled. 2024-01-10 14:42:14 +11:00
Dion Moult a33f30e8ce Fix #4189. Hidden or non-isolated objects now don't show annotations. 2024-01-10 12:29:56 +11:00
Christoph Mellueh e79fb2ff0b add typing to BsDD 2024-01-10 09:10:38 +11:00
Christoph Mellueh 054b4d0ee2 add deprecation warnings 2024-01-10 09:10:38 +11:00
Christoph Mellueh 43bebf4eee update new api to BsDD 2024-01-10 09:10:38 +11:00
Thomas Krijnen 532869a48a #4182 Properly initialize filename in stream_or_filename class 2024-01-09 13:41:38 +01:00
Andrej730 6569d0031f monkey patch blender's dump_addon_messages
The way blender gather messages to translate is:
1) disable the addon
2) parse all available strings
3) enable the addon
4) parse all strings available strings again and subtract the ones gathered at step 2.

But due Blender bug (https://projects.blender.org/blender/blender/issues/116579) not all addon parts unregistered at step 1, so some addon's strings left out and gathered at step 2 and then subtracted at step 4. At the end, we loose them and they will be missing from resulting translations files.

I've monkey patched dump_addon_messages so it now works a bit more safe:
0) It expects addon to be disabled and Blender restarted after
1) Gather all Blender strings
2) Enable addon
3) Gather all Blender strings again and subtract strings from step 1.
2024-01-09 16:54:58 +05:00
Andrej730 bfaa61e592 setup_translations - original dump_addon_messages
same as with dump_py_messages - this commit is needed to track monkey patch changes later on
2024-01-09 16:46:22 +05:00
Andrej730 1a09ce40e9 fix error converting translations to .po if .po files already exist 2024-01-09 16:00:21 +05:00
Dion Moult 6f1b40fc86 Fix #4162. Reduce minimum area requirement for auto space generation down to 0.1sqm. 2024-01-09 18:20:51 +11:00
Dion Moult 734eec7715 See #4162. Fix bug with generating space from cursor due to numerical inaccuracies and colinear lines for shapely. 2024-01-09 18:13:29 +11:00
Dion Moult bfabc0db0f Add support for groups in selection queries 2024-01-09 16:44:40 +11:00
Dion Moult e71aac84dc Fix #4175. Add new UI for managing IFC object and edit modes. 2024-01-09 12:57:31 +11:00
Andrej730 c156100be2 Fix breaking spatial containers UI when spatial container is deleted
E.g. if you had some container selected in `active_container_id` and then it got deleted UI would break and won't allow to reload the spatial manger until you reload .ifc completely
2024-01-08 15:53:10 +05:00
Andrej 48c2b43a58 Merge pull request #4171 from c-mellueh/fix-SurfaceStyle-loader-bug
Fix Surface Style Loader Bug
2024-01-08 12:31:14 +05:00
Dion Moult 2f8cfb5a95 Fix #4161. Deprecate unmaintained barebones LCA module. 2024-01-08 16:59:31 +11:00
Eduardo Schilling 1c4cc34c1b Add degrees format display 2024-01-08 16:54:58 +11:00
Eduardo Schilling b81e03d9c2 Add stair calculated parameter Pitch 2024-01-08 16:54:58 +11:00
Dion Moult eb96858460 Fix #4167. Bug where pset template module didn't allow creating qto template types. 2024-01-08 16:52:59 +11:00
Dion Moult 5c7e70e987 Minor fix to loading nested ports 2024-01-08 16:03:57 +11:00
Dion Moult 9093c9d419 Fix #4176. Bug where tabbed interface didn't work on new or temporary screens. 2024-01-08 16:03:34 +11:00
Dion Moult 02a5b8d997 Fix #4178. Bug when removing the wrong classification reference. 2024-01-08 13:48:35 +11:00
Sigma Dimensions (Yass) f1b5f7d558 add conversion based unit symbols 2024-01-05 18:45:54 +01:00
Thomas Krijnen 742a9ecd81 IfcParseExamples requires C++17 now 2024-01-05 09:04:34 +01:00
Thomas Krijnen 77a497cdf7 Generalize shortened file.schema attribute 2024-01-04 15:05:40 +01:00
CyrilWaechter 00576ef574 IfcRelSpaceBoundary: Add error handling and better error message for user 2024-01-04 10:22:51 +01:00
Sigma Dimensions (Yass) c26caaa58d fix resource cost calculation 2024-01-04 09:33:55 +01:00
Sigma Dimensions (Yass) b260bd3877 Record formula and resource name for cost values when calculating resource cost 2024-01-04 09:32:31 +01:00
Sigma Dimensions (Yass) cd6246c382 fix cost value calculation when using parent cost 2024-01-04 09:30:27 +01:00
Sigma Dimensions (Yass) a96d6dacc7 Display cost value names, and add import operator for cost schedule panel 2024-01-04 09:29:50 +01:00
CyrilWaechter 64c711a02a IfcRelSpaceBoundary: Create almost all boundaries for a selected space
* Refactor boundary creation to allow usage in a loop.
* Loop through all building element in building storey instead of stopping to the first one
* Enforce checking to prevent creation of invalid boundary geometries
2024-01-03 16:37:10 +01:00
Christoph Mellueh 68a2480a66 turn list into tuple 2024-01-03 13:42:14 +01:00
CyrilWaechter d5e2fd5d3c Fix some url and add missing ones 2024-01-03 01:11:05 +01:00
CyrilWaechter bb445663f8 Bump IOS and use more update friendly url 2024-01-03 00:22:50 +01:00
CyrilWaechter 1f7db75572 Add python 3.11 to blenderbim build matrix 2024-01-02 20:59:04 +01:00
CyrilWaechter f478ec8d6b black boundary operator.py 2024-01-02 18:49:50 +01:00
CyrilWaechter cf3ba334b7 IfcRelSpaceBoundary: make sure parent boundary is whole 2024-01-02 18:49:05 +01:00
CyrilWaechter c36af110c4 IfcRelSpaceBoundary: Improve inner boundary generation workflow and boundary names
# Inner boundaries
Some software or workflow generates many opening for the same element (eg. window) so it is not relevant to create inner boundaries for opening with no filling.

# Boundary name
Apply convention for 1st and 2nd Level boundaries
2024-01-02 17:52:23 +01:00
Kristoffer Andersen 91eac7085b Update meta.yaml
pin swig version for conda
2024-01-02 15:46:48 +01:00
Thomas Krijnen 239d1d6fd2 C++ get_psets() with extensive preprocessor trickery 2023-12-30 16:14:10 +01:00
Thomas Krijnen 831709d514 Update src/ifcopenshell-python/docs/ifcopenshell-python/geometry_processing.rst 2023-12-30 10:03:10 +01:00
wangsenhui 5925476ed9 add geometry processing with multiple representations example 2023-12-30 10:03:10 +01:00
wangsenhui 3edb3c8df3 add geometry processing with multiple representations example 2023-12-30 10:03:10 +01:00
wangsenhui 72badd8427 add geometry processing with multiple representations example 2023-12-30 10:03:10 +01:00
Andrej730 ff251b7a6a monkey patch dump_py_messages to ignore blenderbim dependencies 2023-12-29 17:36:12 +05:00
Andrej730 dbce57ffc5 setup_translations - original dump_py_messages
as we're going to monkey patch it and this commit is needed to keep the history of changes
2023-12-29 17:36:12 +05:00
Andrej730 8ff9bcef32 update translations in current blender session 2023-12-29 17:13:43 +05:00
Andrej730 82b2006686 setup_translations.py - use just 1 directory to store .po files
and use temp directory to store .po files for ui_translate.export/import
2023-12-29 17:13:43 +05:00
Andrej730 ca4181c714 fixed typo 2023-12-29 16:58:31 +05:00
Andrej730 3928dd7c9c BBIM - register translation strings #889 2023-12-29 15:33:38 +05:00
Andrej730 925964ee2a BBIM script to reload/export/import translation strings #889 2023-12-29 15:33:30 +05:00
Thomas Krijnen 7201df8d22 p21 binary should be uppercase 2023-12-26 14:28:45 +01:00
Andrej730 7b65108a76 ifctester - added some typing for IDE type hints 2023-12-26 16:18:08 +05:00
Andrej730 0fbe4d80c7 Clean up IfcIndexedColourMap removing representation #4138
Because we wasn't removing colour map, remove_representation was producing orphaned IfcPolygonalFaceSet that were referred by colour map.
2023-12-26 11:33:20 +05:00
Ryan Schultz 54c689d615 Applying multiple opening objects in bulk
Merry Christmas.
2023-12-25 10:33:49 -06:00
Andrej730 ab828b1a8b ifcpixeltexture tests to reflect actual order of data 2023-12-25 16:22:08 +05:00
Andrej730 8ddd349f8b Fixed issue with flipped ifcblobtexture loaded to blender 2023-12-25 16:21:05 +05:00
Andrej730 3221219f4a save ifcblobtexture and ifcpixeltexture to .blend file 2023-12-25 14:36:58 +05:00
Andrej730 36387f348e Fixed issue changing type of object creating opening in an aggregate
More: https://community.osarch.org/discussion/1888/error-after-trying-to-change-a-window-type-whose-opening-is-applied-to-an-aggregate#latest

A bit related to #3973 in case if it will be resolved
2023-12-25 11:49:06 +05:00
Andrej730 c39c6d81e3 Note on not allowing aggregates with representation #3973 2023-12-25 11:47:58 +05:00
Andrej730 e8042de1ed expose assign/unassign aggregation hotkey to bim tool ui 2023-12-25 11:03:10 +05:00
Andrej730 78fd999c53 Fix a few uses of assigning/unassigning agregation after 165262933 2023-12-25 10:38:51 +05:00
dushyant basson 91d1cb79c2 Using transformation-matrix data; Turned off world-coords; Re-using geometry of repeating IDs 2023-12-24 11:57:14 +01:00
dushyant basson 832618b84a initial camera-orientation; multiple materials in a single element; default material used from IfcGeom; improved triangle node data generation for OSG 2023-12-24 11:57:14 +01:00
dushyant basson e4ce53d548 update 2023-12-24 11:57:14 +01:00
dushyant basson 7b92b5c7e2 IFC file successfully parsed and loaded in Qt OpenGL widget 2023-12-24 11:57:14 +01:00
dushyant basson c8c08439b2 added header-guard 2023-12-24 11:57:14 +01:00
dushyant basson 3476ee8fd5 debug messages 2023-12-24 11:57:14 +01:00
dushyant basson d2f331c813 update 2023-12-24 11:57:14 +01:00
dushyant basson 9e9b294f44 Implemented: Application icons; OpenSceneGraph to handle OpenGL with Qt; devicePixelRatio() for handing render on highDPI displays; Sample 3D with handler; MouseHandler not yet working as expected (WIP) 2023-12-24 11:57:14 +01:00
Dushyant Basson f824ee8ca4 updated .ico file 2023-12-24 11:57:14 +01:00
dushyant basson 919f673108 add application file icon for macos 2023-12-24 11:57:14 +01:00
Dushyant Basson b034b40b09 application file-icon and window-icon set on ms-windows 2023-12-24 11:57:14 +01:00
dushyant basson 34f038530f Removed .clangd file from tracking and added it to .gitignore 2023-12-24 11:57:14 +01:00
dushyant basson 1c70990b06 Removed .clangd file from tracking and added it to .gitignore 2023-12-24 11:57:14 +01:00
dushyant basson 07eecad433 adding qt resources 2023-12-24 11:57:14 +01:00
Sigma Dimensions (Yass) ef87f0fe74 New Clash management panel to load IFCClash collision results 2023-12-23 09:14:08 +01:00
Sigma Dimensions (Yass) 23c2d73e8e fix smart clash group selection 2023-12-23 09:11:20 +01:00
Andrej730 0b47fb5987 Fix typo in 92cf0d9 2023-12-22 21:53:31 +05:00
Andrej730 8a1dee532b Support loading styles with IfcPixelTexture 2023-12-22 19:22:14 +05:00
Andrej730 f0c6c4ca59 Allow copying node graphs for external shaders on Blender 4.1+ #3876 2023-12-22 16:40:48 +05:00
Andrej730 92cf0d9265 remove_representation not to leave brokens refs with batch_remove #4138 2023-12-22 16:04:52 +05:00
Andrej730 7c438b6b9b Purge IfcWindow/DoorStyle/IfcTypeProduct #4137 2023-12-21 14:26:43 +05:00
Sigma Dimensions (Yass) 46454c5c94 consider unit basis for calculating resource costs #4122 2023-12-20 14:19:16 +01:00
Andrej730 73aeafb063 Fix issue renaming objects first time project was loaded after 962b8fa
E.g. if you load a project and try to rename some element to "test" it will renamed to "test" instead of "IfcWindow/test" e.g.

Since we relid on `obj.name` handler to set `.is_renaming` back to `False`, it didn't set it back to False when project was just loaded and renamed object was not yet linked to ifc element and name handler is not yet setup.

Another option was to check whether object is linked before changing `is_renaming` but just setting it to False should be more more explicit.
2023-12-20 16:51:02 +05:00
Kristoffer Andersen e06297940f Update ci.yml
Hopefully this fixes ccache
2023-12-20 12:28:24 +01:00
Andrej730 1652629332 prefix bim.assign/unassign_object to avoid operator override #4099
Co-Authored-By: Bruno Perdigão <57102715+brunoperdigao@users.noreply.github.com>
2023-12-20 13:51:26 +05:00
Andrej730 3d8de40407 Fix for 308457300
Co-Authored-By: Bruno Postle <bruno@postle.net>
2023-12-20 11:00:24 +05:00
ay-ex 308457300e fix cmake regex for used blender python version 2023-12-19 15:46:32 +05:00
Andrej730 6ef96841ac Temp disable setting paint_active_slot from external materials #4129 2023-12-19 14:38:23 +05:00
Andrej730 289b2d1e9d Less console errors if failed to find external styles library
Now:
```
Error: Error loading external style - cannot find file: 'D:\Dropbox\GitHub\OD_Textures\Closed (Encrypted)\Materials.blend'
```

Before:
```
File "\blenderbim\bim\module\style\prop.py", line 250, in update_shading_style
Error: Error loading external style - cannot find file: 'D:\Dropbox\GitHub\OD_Textures\Closed (Encrypted)\Materials.blend'
Traceback (most recent call last):
  File "\blenderbim\bim\module\style\prop.py", line 255, in update_shading_style
    bpy.ops.bim.activate_external_style(material_name=blender_material.name)
  File "L:\Games\SteamLibrary\steamapps\common\Blender\4.0\scripts\modules\bpy\ops.py", line 109, in __call__
    ret = _op_call(self.idname_py(), kw)
RuntimeError: Error: Error loading external style - cannot find file: 'D:\Dropbox\GitHub\OD_Textures\Closed (Encrypted)\Materials.blend'
```
2023-12-19 14:38:23 +05:00
Kristoffer Andersen 96db699ad2 Merge pull request #4125 from Krande/fix/convert-units
Proposal for updating IfcPatch ConvertLengthUnit.py for compatibility with boost>=1.82
2023-12-19 06:43:01 +01:00
Andrej730 6b7eec0db6 Fix #4124 2023-12-18 12:28:19 +05:00
Sigma Dimensions (Yass) 80f35d0bda Fix CSV workschedule import - handle date-related attributes more gracefully 2023-12-17 20:25:23 +01:00
Sigma Dimensions (Yass) df390a655d fix displaying derived task durations 2023-12-17 20:20:52 +01:00
krande 593fca7c3d remove ci-py-only.yml as ci.yml already does this 2023-12-17 18:42:15 +01:00
krande 9335c8a0c8 add test and update ci 2023-12-17 18:09:28 +01:00
krande eccc21277d update ci 2023-12-17 15:42:38 +01:00
krande 2ce7e16537 add fix for ci tests 2023-12-17 15:33:35 +01:00
Thomas Krijnen 49835ee303 nix build-all: don't strip non-release wrapper builds 2023-12-17 13:18:34 +01:00
krande f5500da0af remove ifc2x3 conditional in conversion utility 2023-12-16 14:18:13 +01:00
krande c9552fac29 update name of function to make its purpose more clear 2023-12-16 14:01:36 +01:00
krande b11b3b7fa8 update unit_assignment 2023-12-16 13:47:18 +01:00
krande 8aecee5836 initial proposal for updating units conversion 2023-12-16 13:37:38 +01:00
Bruno Postle 2f3b3011ec BlenderBIM IFC Git set push.autoSetupRemote
When pushing to a remote repository set tracking for the current branch
so that a locally created repository behaves the same (on the
command-line) as a cloned repository
2023-12-15 23:26:52 +00:00
Andrej730 491c9203b0 test for loading ifc with blob textures 2023-12-15 18:19:59 +05:00
Andrej730 7f2b996d23 test for loading ifc style to blender material 2023-12-15 18:19:59 +05:00
Andrej730 d2ea6f6656 BBIM to support loading IfcBlobTexture 2023-12-15 18:19:59 +05:00
Andrej730 25128e8aa8 support to add all surface style types creating new IfcSurfaceStyle
Previously it was working only for shading styles, for other styles it worked but then produced errors as IfcSurfaceStyle.Styles end up being None.
2023-12-15 18:19:59 +05:00
Dion Moult 30fa917e69 Patch recipes can now be performed in-place and are sorted in the UI. 2023-12-15 23:09:34 +11:00
Dion Moult 40e494996d Add support for manual classifications and references 2023-12-15 23:09:34 +11:00
Andrej730 cfdc54064e disable rel paths for adding ref images and textures with unsaved .ifc 2023-12-14 18:53:54 +05:00
Andrej730 6daead6b61 Fixed issues using relative paths for external styles
- removed redundant notion about saving .blend file
- relative paths were saved incorrectly
- fix issue using "bim.browse_external_style" if IfcSurfaceStyle already has external style and it's either .blend file or material in it is missing (it was failing opening file browser)
2023-12-14 18:47:55 +05:00
Andrej730 241d52f0ad small error message fix
as it appears as one of the messages during .ifc load, that way it will be more clear that it's related to external styles
2023-12-14 18:43:17 +05:00
Andrej730 68dd93da1d Fix issue BrowseExternalStyle not selecting current external style in file manager 2023-12-14 17:43:12 +05:00
Andrej730 82703670f0 Pull all shading related material attributes from external styles #4053 2023-12-14 17:42:17 +05:00
Dion Moult cb8462a0d0 New SetWorldCoordinateSystem patch. Autodesk software is starting to pay attention to this attribute which I find really confusing. 2023-12-14 17:02:00 +11:00
Dion Moult 000245c26c Nesting collections only generate for element nesting, not port nesting 2023-12-14 16:50:56 +11:00
Andrej730 d97ee8eed4 Support editing IfcSurfaceStyleLighting in UI 2023-12-13 16:57:23 +05:00
Andrej730 f8af28f12a style.edit_surface_style to support editing IfcSurfaceStyleLighting
It was failing because api method wasn't supporting IfcColourRgb attributes edit in general but only for IfcSurfaceStyleShading.SurfaceColour
2023-12-13 16:53:58 +05:00
Andrej730 f6fab30d0a Support editing IfcSurfaceStyleRefraction in UI 2023-12-13 15:16:52 +05:00
Andrej730 f1a4ed702b Hide IfcStyledItem from bim.print_unused_elements_stats
as IfcStyledItem can be not referred by any other element and it doesn't mean it's not used in the project
2023-12-13 14:29:10 +05:00
Andrej730 c4080626df style.add_surface_texture to work without blender material provided 2023-12-13 14:07:48 +05:00
Bruno Postle a8078e38d2 Fix Clipping divide error (reported in #4057) 2023-12-12 22:15:59 +00:00
Andrej730 c52fe8abf9 data.py rename for consistency 2023-12-12 16:14:19 +05:00
Andrej730 b310b5aaba Remove old external styles UI from material in favor of general styles UI
Adding external styles from other styles now also moved to general styles UI - https://i.imgur.com/uEyjKVY.png
2023-12-12 16:12:55 +05:00
Andrej730 7246e75085 Fix after 2520a9305 2023-12-12 15:32:40 +05:00
Andrej730 48c0378bff Fixed #4105 2023-12-12 14:48:04 +05:00
Andrej 91c90e7b86 Merge pull request #4095 from c4rlosdias/v0.7.0 2023-12-12 14:09:14 +05:00
Thomas Krijnen 72abdb0569 Update build-all.py - py vers 2023-12-11 16:00:36 +01:00
Andrej730 7947ba4aaf Override some more blender scene panels #3842 2023-12-11 16:35:21 +05:00
Andrej730 2520a93055 Fix issues unregistering overridden panels
Previously overridden scene panels were removed but original panels were not restored.
Also added condition check for Blender version to generate list of panels to override, so we won't miss any errors using `try - except`.
2023-12-11 16:35:21 +05:00
Andrej730 fa480d99a2 Procedural override for default blender panels, hide keying panels #3842
also fixed the issue with default scene panels that were parented to other panels - because we didn't change `bl_parent_id` the panel structure for them was broken.
2023-12-11 16:35:21 +05:00
Bruno Postle 72c057e7a0 BlenderBIM user docs update after 6452872 2023-12-10 22:13:05 +00:00
Carlos Dias 61cfb361fa Update __init__.py
Fix bug
2023-12-08 11:09:28 -03:00
Andrej730 3786cfa666 Fixed issues with linked elements from linked blend files #4081
In #4081 reported the case when during redo (changing param in operator's popup) `Ifc.rebuild_element_maps` was relinking elements without discarding possible linked blender libraries. It end up accidentally linking some library's objects to the current Ifc file leading to errors.

Also fixed possible similar issue in couple other places.
2023-12-08 18:38:09 +05:00
Andrej730 cabc06821f test for tool.Blender.copy_node_graph #3876 2023-12-08 16:58:11 +05:00
Dion Moult f9fef4f5f6 Fix #4085. Classification reference URIs now have a button you can jump to that link. 2023-12-08 16:35:46 +11:00
Dion Moult 7c89d072e2 Fix #4086. Add support for non predefined bSDD pset and qto values. 2023-12-08 15:07:46 +11:00
Dion Moult dbb9e8c907 Fix #4092. Minor bug with bad detection of text literals when switching geometry. 2023-12-08 14:03:46 +11:00
Dion Moult 2df5b4c70e Fix #4090. Bug where profile set generator didn't accommodate optional axes. 2023-12-08 12:17:48 +11:00
Andrej730 a5aa608240 Fix reference images import after styles ui change 2023-12-07 18:09:17 +05:00
Andrej730 f7454366cc Representation Items UI - replace icon for active style
Now it's more consistent because we use the same icon is Styles UI
2023-12-07 18:09:17 +05:00
Andrej730 f773480917 style.edit_surface_style - fix crash setting attribute to None
Occured on setting IfcColourOrFactor attribute to None (e.g. DiffuseColour).
2023-12-07 18:09:17 +05:00
Andrej730 89f9cd0403 Styles UI - moved UI from shader graph N-panel to Styles UI #3912
Moved all shading style and textures parameters from shader-graph N-panel to general Styles UI. Shader graph now also update in real time on any prop changes in Styles UI.

Now there is practically no need to switch to shader graph to edit styles shaders - all parameters are available from the Styles UI. That also allows you to edit styles and see the results in viewport even if the edited style is not the one that's currently active.

small demo - https://imgur.com/a/i2suoZw
2023-12-07 18:09:16 +05:00
Martin15135215 b21159c6b3 add cff for ifcsverchok 2023-12-07 13:42:32 +11:00
Martin15135215 227f556669 add cff for serializers 2023-12-07 13:42:32 +11:00
Martin15135215 42bd69929c add cff for qtviewer 2023-12-07 13:42:32 +11:00
Martin15135215 a3532b24b3 add cff for ifcwrap 2023-12-07 13:42:32 +11:00
Martin15135215 5486c4c8e9 add cff for ifctester 2023-12-07 13:42:32 +11:00
Martin15135215 0259d4c0cd add cff for ifcpatch 2023-12-07 13:42:32 +11:00
Martin15135215 66c4c61dc2 add cff for ifcparse 2023-12-07 13:42:32 +11:00
Martin15135215 0f32ffe232 add cff for ifcopenshell-python 2023-12-07 13:42:32 +11:00
Martin15135215 72630de77a add cff for ifcmax 2023-12-07 13:42:32 +11:00
Martin15135215 9395bed72b add cff for ifcjni 2023-12-07 13:42:32 +11:00
Martin15135215 507a5cab83 add cff for ifcgeom_schema_agnostic 2023-12-07 13:42:32 +11:00
Martin15135215 a46a60996d add cff for ifcgeom 2023-12-07 13:42:32 +11:00
Martin15135215 3106168889 add cff for ifcfm 2023-12-07 13:42:32 +11:00
Martin15135215 7e0c0c5c83 add cff for ifcdiff 2023-12-07 13:42:32 +11:00
Martin15135215 19205c8774 add cff for ifccsv 2023-12-07 13:42:32 +11:00
Martin15135215 62b5f1d728 add cff for ifcconvert 2023-12-07 13:42:32 +11:00
Martin15135215 299df62758 add cff for ifcclash 2023-12-07 13:42:32 +11:00
Martin15135215 e58bfde500 Add cff for ifccityjson 2023-12-07 13:42:32 +11:00
Martin15135215 b60306cdbd add cff for ifcblender 2023-12-07 13:42:32 +11:00
Bruno Perdigão 61e0549e65 add_aggregate now add the empty using the 3d cursor location 2023-12-07 13:40:16 +11:00
Ryan Schultz 52ae759218 Added classification to selector 2023-12-07 13:39:42 +11:00
c4rlosdias 9b41614eff Update Load bSDD domains 2023-12-07 13:38:25 +11:00
Dirk Olbrich 58e01751c1 bbim-docs: add interface property editor section
- add stubs for each sub-tab
2023-12-07 13:19:46 +11:00
Dirk Olbrich 624134d033 bbim-docs: use figure with caption instead of plain image 2023-12-07 13:19:46 +11:00
Dirk Olbrich 22f83b68e4 bbim-docs: update workspace doc for sidebar panels 2023-12-07 13:19:46 +11:00
Dion Moult 1b35ea122e Implement support for managing nest hosts and components. 2023-12-07 13:19:29 +11:00
Ryan Schultz f1deefbcb3 Merge pull request #3969 from brunoperdigao/refactor-duplicate-aggregate
Refactor duplicate aggregate
2023-12-06 14:39:43 -06:00
Dion Moult d6c020b5ee Fix #4076. Fix bug where drawing activation priorities didn't match drawing generation priorities, and switching to a text literal representation failed. 2023-12-06 23:04:21 +11:00
Dion Moult ff4eddf38a Fix #4078. Bug in IfcTester that didn't allow certain facets to be used as applicabilities. Also canonicalise facet ordering for convenience. 2023-12-06 15:06:11 +11:00
Dion Moult 2b34dc4ce7 Fix bug in TessellateElements patch recipe where sometimes the patch would run forever. 2023-12-06 14:16:18 +11:00
Dion Moult 530260ebe7 Run black on cityjson2ifc 2023-12-05 09:54:38 +11:00
Dion Moult 594268c860 Fix two minor bugs in cityjson2ifc 2023-12-05 09:54:38 +11:00
Andrej730 e5fa8a01b4 fixed couple styles ui bugs
1) it was failing adding a new IfcSurfaceStyle if surface_style_class != Shading/Render as surface_style was not defined

2) It was never creating render material as IfcSurfaceStyleRendering class is a child of IfcSurfaceStyleShading and if statement never got to the is_a("IfcSurfaceStyleRendering") check
2023-12-04 17:38:22 +05:00
Andrej730 ad934cd9a6 Use paths relative to .ifc instead of .blend choosing texture paths
Texture path file dialog is also filtering image files now.

Removed subtype from `FILE_PATH` from all related props since `layout.prop()` for those props starts file dialog that cannot return path relative to .ifc. It can return path relative to .blend - though it can be processed in props update to convert to relative to .ifc, this doesn't allow setting relative paths if .blend is not saved. Therefore, I've created a separate operator.

add
2023-12-04 16:42:31 +05:00
Andrej730 92fe325816 Store reference images by relative paths #3447 2023-12-04 16:42:31 +05:00
Andrej730 818bb9c823 Fix normals for generic parametric stairs #4020
Also made concrete stair normals defined more explicitly.
2023-12-04 16:42:30 +05:00
Andrej d71fad988d Merge pull request #4071 from maxfb87/fix_clone_opening 2023-12-04 16:39:34 +05:00
Dion Moult ff0d6c810f Fix #4072. IDS reports now include filename. 2023-12-04 17:26:57 +11:00
Massimo Fabbro 9aad249586 fix #3834. Now clone opening produces valid IFC files 2023-12-02 17:38:28 +01:00
Bruno Perdigão d23e32c64a added report messages 2023-11-23 10:57:17 -03:00
Bruno Perdigão b815719477 added the option to refresh different groups of linked aggregate 2023-11-23 10:27:54 -03:00
Bruno Perdigão 8e64b55290 bugfix in recreate_aggregate 2023-11-20 22:05:41 -03:00
Bruno Perdigão b45001a95a fix conflicts 2023-11-20 18:40:17 -03:00
Bruno Perdigão fd65009f5a Refresh Linked Aggregate now handles multiple selection 2023-11-20 18:34:46 -03:00
Bruno Perdigão 52036e9bd4 merged the branch 'improvements-on-override-duplicat' 2023-11-20 16:38:10 -03:00
Bruno Perdigão 5399767898 Merge branch 'improvements-on-override-duplicate' into refactor-duplicate-aggregate 2023-11-20 16:24:16 -03:00
Bruno Perdigão f96d477d2b Groups are only added to the parent. Parts now have a pset with index 2023-11-20 16:23:34 -03:00
Ryan Schultz 67be2e8ce8 Refresh different types of aggregates at one time 2023-11-15 21:40:15 -06:00
Bruno Perdigão f0e5304b8e fix conflicts merging with v0.7.0 2023-11-14 11:58:35 -03:00
Bruno Perdigão 61bd98e7db moved 'recreate_assembly' to tool/root.py 2023-11-07 18:28:43 -03:00
Bruno Perdigão 4bf0d9ccff added more tests 2023-11-07 18:19:42 -03:00
Bruno Perdigão 1e36ac8740 removed the pset and added group to all elements 2023-11-06 21:38:06 -03:00
Bruno Perdigão af864e8556 added test scenario for duplicating an aggregate 2023-11-06 12:15:31 -03:00
Bruno Perdigão ef72cd3250 recreates assembly for array children objects 2023-10-23 15:46:14 -03:00
Bruno Perdigão 4f1e59a613 improvements on dealing with sub-aggregates 2023-10-20 21:26:56 -03:00
Bruno Perdigão 5960e4153c wip: RefreshLinkedAggregate is working with IfcGroup 2023-10-20 14:34:03 -03:00
Bruno Perdigão 66f735fde9 wip: refactor of RefreshLinkedAggregate. The operator triggers all the other linked aggregates deletion. 2023-10-18 21:36:04 -03:00
Bruno Perdigão 1a3aa7f031 IfcElementAssembly share a group instead of a pset 2023-10-18 20:43:41 -03:00
Bruno Perdigão 37ab9c80e3 Duplication now recreates connections path #3770 2023-10-17 18:45:14 -03:00
Bruno Perdigão 86a234d17e Changed operator name 2023-10-16 22:32:09 -03:00
Bruno Perdigão f68baee5c3 small deletions 2023-10-16 22:17:22 -03:00
Bruno Perdigão 0028fd3e9c ifc_duplicate_operator now remove the connections with old objects. Similar solutions presented in PR #3789 2023-10-16 15:39:38 -03:00
Bruno Perdigão f5f9862b78 Fix #2187. ifc_duplicate_operator now recreates the IfcElementAssembly structure into a new Assembly 2023-10-16 15:31:56 -03:00
Bruno Perdigão 4e833e26ca wip: DuplicateMoveAggregate now calls DuplicateMove function and removes code duplication 2023-10-13 14:41:21 -03:00
466 changed files with 23725 additions and 9167 deletions
+2 -2
View File
@@ -39,7 +39,7 @@ jobs:
strategy:
fail-fast: false
matrix:
pyver: [py39, py310]
pyver: [py39, py310, py311]
config:
- {
name: "Windows Build",
@@ -62,7 +62,7 @@ jobs:
- uses: actions/setup-python@v2 # https://github.com/actions/setup-python
with:
architecture: 'x64' # optional x64 or x86. Defaults to x64 if not specified
python-version: '3.10'
python-version: '3.11'
- run: echo ${{ env.DATE }}
- name: Get current date
id: date
-98
View File
@@ -1,98 +0,0 @@
name: ci_py_only
on:
push:
paths:
- 'src/**'
- 'test/**'
- 'conda/**'
- 'cmake/**'
- '.github/workflows/ci-py-only.yml'
pull_request:
jobs:
activate:
runs-on: ubuntu-latest
if: |
github.repository == 'IfcOpenShell/IfcOpenShell' &&
!contains(github.event.head_commit.message, 'skip ci')
steps:
- run: echo ok go
build:
runs-on: ubuntu-20.04
needs: activate
steps:
- uses: actions/checkout@v2
with:
submodules: recursive
- name: Install C++ dependencies
run: |
sudo apt update
sudo apt-get install --no-install-recommends \
git cmake gcc g++ \
libboost-date-time-dev \
libboost-filesystem-dev \
libboost-iostreams-dev \
libboost-program-options-dev \
libboost-regex-dev \
libboost-system-dev \
libboost-thread-dev \
python3-all-dev python3-pip \
swig libpcre3-dev libxml2-dev \
libtbb-dev nlohmann-json3-dev \
libocct-foundation-dev libocct-modeling-algorithms-dev libocct-modeling-data-dev libocct-ocaf-dev libocct-visualization-dev libocct-data-exchange-dev \
libhdf5-dev libcgal-dev
- name: ccache
uses: hendrikmuhs/ccache-action@v1
with:
key: ${GITHUB_WORKFLOW}
- name: Build ifcopenshell
run: |
sudo /usr/sbin/update-ccache-symlinks
mkdir build && cd build
PATH="/usr/lib/ccache:$PATH" cmake \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_PREFIX_PATH=/usr \
-DCMAKE_SYSTEM_PREFIX_PATH=/usr \
-DOCC_INCLUDE_DIR=/usr/include/opencascade \
-DOCC_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu \
-DPYTHON_EXECUTABLE:FILEPATH=/usr/bin/python3 \
-DPYTHON_INCLUDE_DIR:PATH=/usr/include/python3.8 \
-DPYTHON_LIBRARY:FILEPATH=/usr/lib/x86_64-linux-gnu/libpython3.8.so \
-DCOLLADA_SUPPORT=Off \
"-DSCHEMA_VERSIONS=2x3;4;4x3_add1" \
-DBUILD_CONVERT=Off \
-DGLTF_SUPPORT=On \
-DJSON_INCLUDE_DIR=/usr/include \
-DCGAL_INCLUDE_DIR=/usr/include \
-DGMP_INCLUDE_DIR=/usr/include \
-DMPFR_INCLUDE_DIR=/usr/include \
-DGMP_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu \
-DMPFR_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu \
-DHDF5_INCLUDE_DIR=/usr/include/hdf5/serial \
../cmake
sudo make -j $(nproc)
sudo make install
- name: Install Python dependencies
run: |
sudo /usr/bin/python -m pip install -U pip
sudo /usr/bin/python -m pip install xmlschema xsdata numpy lxml
sudo /usr/bin/python -m pip install src/bcf --no-deps
sudo /usr/bin/python -m pip install pytest
sudo /usr/bin/python -m pip install isodate
sudo /usr/bin/python -m pip install lark
sudo /usr/bin/python -m pip install networkx
sudo /usr/bin/python -m pip install tabulate
sudo /usr/bin/python -m pip install python-dateutil
- name: Test
run: |
cd test
sudo /usr/bin/python tests.py
cd ../src/ifcopenshell-python
mv ifcopenshell ifcopenshell-local # Force testing on installed module
make test-safe
+23 -20
View File
@@ -31,12 +31,25 @@ jobs:
- run: echo ok go
build:
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
needs: activate
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: 3.11
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install xmlschema xsdata numpy lxml pytest isodate lark networkx tabulate python-dateutil
pip install src/bcf --no-deps
pip install https://github.com/Andrej730/aud/archive/refs/heads/master-reduced-size.zip
- name: Install C++ dependencies
run: |
sudo apt update
@@ -49,7 +62,6 @@ jobs:
libboost-regex-dev \
libboost-system-dev \
libboost-thread-dev \
python3-all-dev python3-pip \
swig libpcre3-dev libxml2-dev \
libtbb-dev nlohmann-json3-dev \
libocct-foundation-dev libocct-modeling-algorithms-dev libocct-modeling-data-dev libocct-ocaf-dev libocct-visualization-dev libocct-data-exchange-dev \
@@ -62,6 +74,9 @@ jobs:
- name: Build ifcopenshell
run: |
echo $Python3_ROOT_DIR
echo ${{ env.pythonLocation }}
mkdir build && cd build
cmake \
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
@@ -71,9 +86,9 @@ jobs:
-DCMAKE_SYSTEM_PREFIX_PATH=/usr \
-DOCC_INCLUDE_DIR=/usr/include/opencascade \
-DOCC_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu \
-DPYTHON_EXECUTABLE:FILEPATH=/usr/bin/python3 \
-DPYTHON_INCLUDE_DIR:PATH=/usr/include/python3.8 \
-DPYTHON_LIBRARY:FILEPATH=/usr/lib/x86_64-linux-gnu/libpython3.8.so \
-DPYTHON_EXECUTABLE:FILEPATH=${{ env.pythonLocation }}/bin/python \
-DPYTHON_INCLUDE_DIR:PATH=${{ env.pythonLocation }}/include/python3.11 \
-DPYTHON_LIBRARY:FILEPATH=${{ env.pythonLocation }}/lib/libpython3.11.so \
-DCOLLADA_SUPPORT=Off \
"-DSCHEMA_VERSIONS=2x3;4;4x3_add1" \
-DGLTF_SUPPORT=On \
@@ -102,23 +117,11 @@ jobs:
grep 0$ statuses
grep 0$ statuses | wc -l
- name: Install Python dependencies
run: |
sudo /usr/bin/python -m pip install -U pip
sudo /usr/bin/python -m pip install xmlschema xsdata numpy lxml
sudo /usr/bin/python -m pip install src/bcf --no-deps
sudo /usr/bin/python -m pip install pytest
sudo /usr/bin/python -m pip install isodate
sudo /usr/bin/python -m pip install lark
sudo /usr/bin/python -m pip install networkx
sudo /usr/bin/python -m pip install https://github.com/Andrej730/aud/archive/refs/heads/master-reduced-size.zip
sudo /usr/bin/python -m pip install tabulate
sudo /usr/bin/python -m pip install python-dateutil
- name: Test
- name: Test ifcopenshell-python
run: |
cd test
sudo /usr/bin/python tests.py
python tests.py
cd ../src/ifcopenshell-python
mv ifcopenshell ifcopenshell-local # Force testing on installed module
make test-safe
+6 -12
View File
@@ -7,34 +7,26 @@
# output directories
/cmake/out/
/docs/out/
/src/examples/out/
/src/ifcmax/out/
/src/ifcwrap/out/
/src/qtviewer/out/
/win/BuildDepsCache*.txt
# IfcExpressParser residue
/src/ifcexpressparser/express_parser.py
# General Python residue
__pycache__
*.py.bak
# Visual Studio Code files
.vscode
.vs
# PyCharm files
.idea
#Virtual Env Files
Pipfile
Pipfile.lock
# Docs
/docs/output
/docs/rst_files
/docs/doxygen
/src/ifcblenderexport/docs/_build
/docs/cpp-api/output
# gettext binary translation files
*.mo
@@ -96,6 +88,8 @@ src/ifcopenshell-python/ifcopenshell/ifcopenshell_wrapper.py
# apple
.DS_Store
# clangd config
.clangd
# clangd cache
.cache
+4 -1
View File
@@ -10,4 +10,7 @@
url = https://github.com/IfcOpenShell/svgfill
[submodule "src/ifcopenshell-python/test/Sample-BIM-Files"]
path = src/ifcopenshell-python/test/Sample-BIM-Files
url = https://github.com/IfcOpenShell/ids-test-files
url = https://github.com/IfcOpenShell/ids-test-files
[submodule "docs/cpp-api/assets/doxygen-awesome-css"]
path = docs/cpp-api/assets/doxygen-awesome-css
url = https://github.com/jothepro/doxygen-awesome-css.git
+8 -7
View File
@@ -7,13 +7,13 @@ IfcOpenShell
</p>
IfcOpenShell is an open source ([LGPL]) software library for working with Industry Foundation Classes ([IFC]). Complete
parsing support is provided for [IFC2x3 TC1], [IFC4 Add2 TC1], IFC4x1, IFC4x2, and IFC4x3. Extensive geometric support
parsing support is provided for [IFC2x3 TC1], [IFC4 Add2 TC1], IFC4x1, IFC4x2, and [IFC4x3 Add2]. Extensive geometric support
is implemented for the IFC releases [IFC2x3 TC1] and [IFC4 Add2 TC1]. Extending with support for arbitrary IFC schemas
is possible at compile-time when using C++ and at run-time when using Python.
In addition to a C++ and Python API, IfcOpenShell comes with an ecosystem of tools, notably including IfcConvert (an application to convert IFC models to
other formats), the BlenderBIM Add-on (an add-on to Blender providing a graphical IFC authoring platform), and many
other libraries, CLI apps, and more. Support is also provided for auxiliary standards such as BCF and IDS.
In addition to a C++ and Python API, IfcOpenShell comes with an ecosystem of tools, notably including IfcConvert (an application
to convert IFC models to other formats), the BlenderBIM Add-on (an add-on to Blender providing a graphical IFC authoring platform),
and many other libraries, CLI apps, and more. Support is also provided for auxiliary standards such as BCF and IDS.
For more information, see:
@@ -32,8 +32,8 @@ For more information, see:
| Anaconda Daily Build | [![Anaconda-Server Badge](https://img.shields.io/conda/vn/ifcopenshell/ifcopenshell)](https://anaconda.org/ifcopenshell/ifcopenshell) |
| Anaconda v0.7.0 Stable | [![Anaconda-Server Badge](https://img.shields.io/conda/vn/conda-forge/ifcopenshell)](https://anaconda.org/conda-forge/ifcopenshell) |
| PyPi Daily Build | [![PyPi Badge](https://img.shields.io/pypi/v/ifcopenshell)](https://pypi.org/project/ifcopenshell/) |
| ArchLinux AUR Package Stable | [![AUR Badge](https://img.shields.io/aur/version/ifcopenshell)](https://aur.archlinux.org/packages/ifcopenshell) |
| ArchLinux AUR Package git | [![AUR Badge](https://img.shields.io/aur/version/ifcopenshell-git)](https://aur.archlinux.org/packages/ifcopenshell-git) |
| ArchLinux AUR Package Stable | [![AUR Badge](https://img.shields.io/aur/version/ifcopenshell)](https://aur.archlinux.org/packages/ifcopenshell) |
| ArchLinux AUR Package git | [![AUR Badge](https://img.shields.io/aur/version/ifcopenshell-git)](https://aur.archlinux.org/packages/ifcopenshell-git) |
| BlenderBIM Add-on Chocolatey (under moderation) | [![Chocolatey Badge](https://img.shields.io/chocolatey/v/blenderbim-nightly)](https://community.chocolatey.org/packages/blenderbim-nightly/) |
| Sponsor development on OpenCollective | [![Financial Contributors](https://opencollective.com/opensourcebim/tiers/badge.svg)](https://opencollective.com/opensourcebim/) |
| Docker hub | [![Docker Pulls](https://img.shields.io/docker/pulls/aecgeeks/ifcopenshell)](https://hub.docker.com/r/aecgeeks/ifcopenshell) |
@@ -44,7 +44,7 @@ Contents
Those marked with an asterisk are part of IfcOpenShell.
| Name | Description | License |
| ------------------------- | ----------------------------------------------------------------------| ------------------- |
| ------------------------- | --------------------------------------------------------------------- | ------------------- |
| bcf | Library to read and write BCF-XML and query OpenCDE BCF-API modules | LGPL-3.0-or-later |
| blenderbim | Add-on to Blender providing a graphical native IFC authoring platform | GPL-3.0-or-later |
| bsdd | Library to query the bSDD API | LGPL-3.0-or-later |
@@ -77,6 +77,7 @@ Those marked with an asterisk are part of IfcOpenShell.
[IFC]: https://technical.buildingsmart.org/standards/ifc/ "IFC"
[IFC2x3 TC1]: https://standards.buildingsmart.org/IFC/RELEASE/IFC2x3/TC1/HTML/ "IFC2x3 TC1"
[IFC4 Add2 TC1]: https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/ "IFC4 Add2 TC1"
[IFC4x3 Add2]: https://standards.buildingsmart.org/IFC/RELEASE/IFC4_3/ "IFC4x3 Add2"
[Visual Studio]: https://www.visualstudio.com/ "Visual Studio"
[Visual C++ Build Tools]: http://landinghub.visualstudio.com/visual-cpp-build-tools "Visual C++ Build Tools"
[MSYS2]: https://msys2.github.io/ "MSYS2"
+1 -1
View File
@@ -29,7 +29,7 @@ URL_CHOCO_PACKAGE = "https://community.chocolatey.org/packages/blender"
URL_BLENDER_CMAKE = "https://raw.githubusercontent.com/blender/blender/{}/build_files/cmake/Modules/FindPythonLibsUnix.cmake"
RE_BLENDER_VERSION_MIN_MAJ = r"Latest Version.+<span>Blender (\d+\.\d+)\..+</span>"
RE_BLENDER_VERSION_MIN_MAJ_PAT = r"Latest Version.+<span>Blender (\d+\.\d+\.\d+)</span>"
RE_BLENDER_PYTHON_VERSION_MAJ_MIN = r"SET\(_PYTHON_VERSION_SUPPORTED (\d+\.\d+)\)"
RE_BLENDER_PYTHON_VERSION_MAJ_MIN = r"(?:set|SET)\(_PYTHON_VERSION_SUPPORTED (\d+\.\d+)\)"
if sys.argv[1] == "--do_choco_release?":
+20 -13
View File
@@ -17,7 +17,7 @@
# #
################################################################################
cmake_minimum_required(VERSION 3.1.3)
cmake_minimum_required(VERSION 3.5)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON) # not necessary, but encouraged
@@ -27,6 +27,7 @@ cmake_policy(SET CMP0048 NEW)
cmake_policy(SET CMP0074 NEW)
cmake_policy(SET CMP0078 OLD)
cmake_policy(SET CMP0086 NEW)
cmake_policy(SET CMP0144 NEW) # find_package() uses upper-case <PACKAGENAME>_ROOT variables.
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release")
@@ -186,7 +187,7 @@ if(GLTF_SUPPORT AND BUILD_CONVERT)
set(SWIG_DEFINES ${SWIG_DEFINES} -DWITH_GLTF)
endif()
# Add USD support to serializers
# Add USD support to serializers
if(USD_SUPPORT)
UNIFY_ENVVARS_AND_CACHE(USD_INCLUDE_DIR)
UNIFY_ENVVARS_AND_CACHE(USD_LIBRARY_DIR)
@@ -209,13 +210,13 @@ if(USD_SUPPORT)
endif()
set(USD_LIBRARIES
usd_usd
usd_usd
usd_usdGeom
usd_usdShade
usd_usdLux
usd_vt
usd_sdf
usd_tf
usd_usdShade
usd_usdLux
usd_vt
usd_sdf
usd_tf
usd_gf
)
@@ -435,7 +436,7 @@ if(BUILD_IFCGEOM)
if(OCCT_STATIC)
find_package(Threads)
if(WASM_BUILD)
set(OPENCASCADE_LIBRARIES ${OPENCASCADE_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
else()
@@ -447,7 +448,7 @@ if(BUILD_IFCGEOM)
set(OPENCASCADE_LIBRARIES -Wl,--start-group ${OPENCASCADE_LIBRARIES} -Wl,--end-group ${CMAKE_THREAD_LIBS_INIT})
endif()
endif()
if (NOT APPLE AND NOT WIN32)
set(OPENCASCADE_LIBRARIES ${OPENCASCADE_LIBRARIES} "rt")
endif()
@@ -656,6 +657,10 @@ if(ENABLE_BUILD_OPTIMIZATIONS)
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELEASE} -O3")
endif()
else()
if(MSVC)
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} /Od")
endif()
endif()
if(MSVC)
@@ -755,6 +760,8 @@ if(NOT SCHEMA_VERSIONS)
endif()
endif()
message(STATUS "IFC SCHEMA_VERSIONS that will be used for the build: ${SCHEMA_VERSIONS}.")
foreach(schema ${SCHEMA_VERSIONS})
add_definitions(-DHAS_SCHEMA_${schema})
endforeach()
@@ -837,7 +844,7 @@ if (BUILD_IFCGEOM)
if (WASM_BUILD)
set(IFCOPENSHELL_LIBRARIES ${IFCOPENSHELL_LIBRARIES} IfcGeom ${IFCGEOM_SCHEMA_LIBRARIES})
else()
set(IFCOPENSHELL_LIBRARIES ${IFCOPENSHELL_LIBRARIES} IfcGeom ${IFCGEOM_SCHEMA_LIBRARIES} IfcGeom ${IFCGEOM_SCHEMA_LIBRARIES})
set(IFCOPENSHELL_LIBRARIES ${IFCOPENSHELL_LIBRARIES} IfcGeom ${IFCGEOM_SCHEMA_LIBRARIES} IfcGeom ${IFCGEOM_SCHEMA_LIBRARIES})
endif()
endif()
@@ -900,7 +907,7 @@ if(BUILD_IFCGEOM)
add_library(IfcGeom_ifc${schema} STATIC ${IFCGEOM_FILES})
set_target_properties(IfcGeom_ifc${schema} PROPERTIES COMPILE_FLAGS "-DIFC_GEOM_EXPORTS -DIfcSchema=Ifc${schema}")
if (NOT WASM_BUILD)
target_link_libraries(IfcGeom_ifc${schema} IfcParse ${OPENCASCADE_LIBRARIES})
target_link_libraries(IfcGeom_ifc${schema} IfcParse ${OPENCASCADE_LIBRARIES})
endif()
endforeach()
@@ -936,7 +943,7 @@ if(BUILD_CONVERT OR BUILD_IFCPYTHON)
foreach(schema ${SCHEMA_VERSIONS})
add_library(Serializers_ifc${schema} STATIC ${SERIALIZERS_S_FILES})
set_target_properties(Serializers_ifc${schema} PROPERTIES COMPILE_FLAGS "-DIFC_GEOM_EXPORTS -DIfcSchema=Ifc${schema}")
if (WASM_BUILD)
target_link_libraries(Serializers_ifc${schema} ${HDF5_LIBRARIES})
else()
+1 -1
View File
@@ -24,7 +24,7 @@ requirements:
- {{ compiler('cxx') }}
- ninja >=1.10.2
- cmake
- swig >=4.0.2
- swig 4.1.1
host:
- python
-11
View File
@@ -1,11 +0,0 @@
#Look for an executable called sphinx-build
find_program(SPHINX_EXECUTABLE
NAMES sphinx-build
DOC "Path to sphinx-build executable")
include(FindPackageHandleStandardArgs)
#Handle standard arguments to find_package like REQUIRED and QUIET
find_package_handle_standard_args(Sphinx
"Failed to find sphinx-build executable"
SPHINX_EXECUTABLE)
-11
View File
@@ -1,11 +0,0 @@
#Look for an executable called sphinx-build
find_program(SPHINX_EXECUTABLE
NAMES sphinx-build
DOC "Path to sphinx-build executable")
include(FindPackageHandleStandardArgs)
#Handle standard arguments to find_package like REQUIRED and QUIET
find_package_handle_standard_args(Sphinx
"Failed to find sphinx-build executable"
SPHINX_EXECUTABLE)
File diff suppressed because it is too large Load Diff
+33
View File
@@ -0,0 +1,33 @@
# IfcOpenShell C++ API documentation
This folder contains the setup to build the IfcOpenShell C++ API documentation from the source code.
## Generating the documentation
> Prerequisites:
>
> Make sure to have [Doxygen](https://www.doxygen.nl) and [Graphviz](https://graphviz.org) installed into your `$PATH` variable.
>
> The documentation also use the [doxygen-awesome](https://jothepro.github.io/doxygen-awesome-css) theme as a git submodule.
Build with the command (from within the `/docs/cpp-api` folder):
```shell
$ doxygen
```
To include the current git commit hash into the build documentation, use the following command:
```shell
$ PROJECT_NUMBER=$(git rev-parse --short HEAD) doxygen
```
This will extract the current commit hash in short version and sets the propper ENV variable used by doxygen.
The generation of the documentation might take a while depending on your systems hardware, as it is configured to generate the Class graphs using .
The resulting documentation is located unter `/cpp-api/output/html` and can be directly accessed with your browser:
```shell
$ open ./output/html/index.html
```
Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 KiB

-240
View File
@@ -1,240 +0,0 @@
APPLY_DEFAULT_MATERIALS
-----------------------
Given the command invocation:
Duplex_A_20110907_optimized.ifc d.dae -yv --include attribute GlobalId 3bXiCStxP6Fgxdej$yc50U
You will find log messages along the lines of
[Warning] {3bXiCStxP6Fgxdej$yc50U} No material and surface styles for:
#333=IfcCovering('3bXiCStxP6Fgxdej$yc50U',#1,'Compound Ceiling:Gypsum Board:187483',$,'Compound Ceiling:Gypsum Board',#17840,#17052,'187483',.CEILING.)
This means that there is no IfcStyledItem associated to the representation items and that the element does not have an IfcMaterial association with IfcMaterialRepresentation from which we can derive a style (colour) for the element.
The interactive session below shows how with this setting enabled you will get a default generated material from the IFC element entity type and material indices of 0 pointing to that. With this setting disabled the material index would be -1 to indicate a missing style. Note that there is one material index for every triangle in the list of `shp.geometry.faces`.
>>> import ifcopenshell, ifcopenshell.geom
>>> f = ifcopenshell.open("Duplex_A_20110907_optimized.ifc")
>>> s = ifcopenshell.geom.settings()
>>> c = f["3bXiCStxP6Fgxdej$yc50U"]
>>>
>>> shp = ifcopenshell.geom.create_shape(s, c)
>>> shp.geometry.material_ids
(-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)
>>> [(m.name, m.diffuse) for m in shp.geometry.materials]
[]
>>>
>>> s.set(s.APPLY_DEFAULT_MATERIALS, True)
>>> shp = ifcopenshell.geom.create_shape(s, c)
>>> shp.geometry.material_ids
(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
>>> [(m.name, m.diffuse) for m in shp.geometry.materials]
[('IfcCovering', (0.7, 0.7, 0.7))]
This is enabled by default for the IfcConvert serializers as they will not gracefully handle -1 material indices and allows users to quickly assign colours based on entity types in their modelling applications.
APPLY_LAYERSETS
---------------
This setting is available in IfcConvert as `--enable-layerset-slicing`.
For IfcWall and IfcSlab elements, takes the associated IfcMaterialLayerSet and builds a set of surfaces to segment the building element geometry.
Note that enabling this settings is computationally intensive as it involves 3D Boolean operations.
Duplex_A_20110907_optimized.ifc d1.dae -yv --include attribute GlobalId 2O2Fr$t4X7Zf8NOew3FNr2
![Image of d1.dae](images/settings-1.png)
Duplex_A_20110907_optimized.ifc d2.dae --enable-layerset-slicing -yv --include attribute GlobalId 2O2Fr$t4X7Zf8NOew3FNr2
![Image of d2.dae](images/settings-2.png)
BUILDING_LOCAL_PLACEMENT
------------------------
This setting is available in IfcConvert using `--building-local-placement`.
In the typical IfcSite > IfcBuilding > IfcBuildingStorey > ... hierarchy of elements, don't incorporate the ObjectPlacement of the IfcBuilding and above in the placement of elements in the output. This is useful when there is a large offset in this placement that reduces precision in further processing.
CONVERT_BACK_UNITS
------------------
This setting is available in IfcConvert using `--convert-back-units`.
Internally IfcOpenShell uses meters as the global length unit to do calculations. This setting restores the coordinate positions after conversion by multiplying the factor of the IfcUnit with UnitType=LENGTHUNIT into the output geometry coordinate values.
DISABLE_OPENING_SUBTRACTIONS
----------------------------
This setting is available in IfcConvert using `--disable-opening-subtraction`.
As in most viewer applications, IfcOpeningElement geometry is subtracted from their host elements. This setting disables this behavior.
Duplex_A_20110907_optimized.ifc d1.dae -yv --include attribute GlobalId 2O2Fr$t4X7Zf8NOew3FNr2
![Image of d1.dae](images/settings-1.png)
Duplex_A_20110907_optimized.ifc d3.dae --disable-opening-subtraction -yv --include attribute GlobalId 2O2Fr$t4X7Zf8NOew3FNr2
![Image of d3.dae](images/settings-3.png)
Note that disabling this settings will reduce processing time and improve robustness as it involves 3D Boolean operations.
DISABLE_TRIANGULATION
---------------------
To be used in conjunction with `USE_BREP_DATA`. Do not apply the triangulation and - when `USE_BREP_DATA` is set - return a OpenCASCADE serialized TopoDS_Shape from `create_shape()` and `iterator`.
>>> import ifcopenshell, ifcopenshell.geom
>>> s = ifcopenshell.geom.settings()
>>> s.set(s.DISABLE_TRIANGULATION, True)
>>> s.set(s.USE_BREP_DATA, True)
>>> f = ifcopenshell.open("Duplex_A_20110907_optimized.ifc")
>>> c = f["3bXiCStxP6Fgxdej$yc50U"]
>>> shp = ifcopenshell.geom.create_shape(s, c)
>>> print(shp.geometry.brep_data)
CASCADE Topology V1, (c) Matra-Datavision
Locations 0
Curve2ds 0
Curves 12
1 4.6750000000000034 -8.0749999999999904 2.657 -2.0455514041918775e-15 -1 0
1 4.6750000000000034 -8.0749999999999904 2.657 1 -3.435893306383461e-15 0
1 6.2260000000000044 -8.0749999999999957 2.657 -2.0455514041918724e-15 -1 0
1 6.226 -10.246000000000031 2.657 -1 6.8717866127669219e-15 0
...
EDGE_ARROWS
-----------
When `INCLUDE_CURVES` is true and geometric elements include curves (such as the wall axis), add arrow heads to the edges to indicate direction of the curve.
Duplex_A_20110907_optimized.ifc d4.dae --model --plan --edge-arrows -yv --include attribute GlobalId 2O2Fr$t4X7Zf8NOew3FNr2
![Image of d4.dae](images/settings-4.png)
EXCLUDE_SOLIDS_AND_SURFACES
---------------------------
Exclude faces, shells and solids from geometrical output. Implied when using `--plan` without `--model` in IfcConvert.
FASTER_BOOLEANS
---------------
NOTE: Only applicable when using OCCT 6.9 and earlier.
This setting is available in IfcConvert using `--merge-boolean-operands`.
Fuse the collection of all boolean operands into a single union before applying the boolean subtraction, as opposed to doing individual subtractions. This likely improves performance. From OCCT 7.0 onwards the boolean operations with multiple arguments is used.
GENERATE_UVS
------------
This setting is available in IfcConvert using `--generate-uvs`.
Applies a box projection on the generated geometry for the element to obtain UV coordinates. This is purely generated, it does not involve texture coordinates stored in the IFC model.
Duplex_A_20110907_optimized.ifc d5.dae --generate-uvs -yv --include attribute GlobalId 2O2Fr$t4X7Zf8NOew3FNr2
![Image of d5.dae with a UV grid applied in Blender](images/settings-5.png)
INCLUDE_CURVES
--------------
This setting is available in IfcConvert using `--plan`.
Include edge and wire geometries in the geometric output.
LAYERSET_FIRST
--------------
This setting is available in IfcConvert using `--layerset-first`.
When not using APPLY_LAYERSETS, take the first material layer from the set to use as the material for the overall element.
NO_NORMALS
----------
This setting is available in IfcConvert using `--no-normals`.
Do not emit normals on geometric output
SEARCH_FLOOR
------------
Note: Only applicable to Collada .DAE output when used from IfcConvert.
This setting is available in IfcConvert using `--use-element-hierarchy`.
Include the spatial hierarchy in the elements.
SEW_SHELLS
----------
This setting is available in IfcConvert using `--orient-shells`.
Re-orient or sew connected face sets to have a consistent outwards orientation.
SITE_LOCAL_PLACEMENT
--------------------
This setting is available in IfcConvert using `--site-local-placement`.
See `BUILDING_LOCAL_PLACEMENT`, but exclude also the ObjectPlacement of the IfcSite.
USE_BREP_DATA
-------------
See `DISABLE_TRIANGULATION`.
USE_PYTHON_OPENCASCADE
----------------------
Note: Only available when an import of `OCC.Core.BRepTools` or `OCC.BRepTools` succeeds.
This implies `USE_WORLD_COORDS` `USE_BREP_DATA` and `DISABLE_TRIANGULATION`. The serialized TopoDS_Shape of `USE_BREP_DATA` is deserialized by Python OpenCASCADE.
USE_WORLD_COORDS
----------------
Apply the ObjectPlacement of the building elements to the geometric output. This is implied when using the Wavefront .OBJ output in IfcConvert. Note that this also eliminates the possibility for geometric elements to point to the same interpreted geometry result.
VALIDATE_QUANTITIES
-------------------
This setting is available in IfcConvert using `--validate`.
Running IfcConvert with `--validate` will set a non-zero exit code when ever a log message with severity equal or greater than ERROR has been emitted.
Currently for internal use only. For every building element geometry converted, looks for an associated quantity set where the OwnerHistory's organization name is IfcOpenShell. And looks for the quantities "Total Surface Area", "Volume", "Shape Validation Properties.Surface Genus" and validates these according to the interpreted geometry definition. Emit Logger::Error when calculated values are outside of the tolerance range for the value stored in the model.
WELD_VERTICES
-------------
Note: In Python, this setting is *on* by default.
Note: This setting only affects triangulated output.
This setting is available in IfcConvert using `--weld-vertices`.
Discards normals and joins vertices solely based on position. This is useful when output is to be modified in a modeling application.
>>> import ifcopenshell, ifcopenshell.geom
>>> s = ifcopenshell.geom.settings()
>>> s.set(s.WELD_VERTICES, False)
>>> f = ifcopenshell.open("Duplex_A_20110907_optimized.ifc")
>>> c = f["3bXiCStxP6Fgxdej$yc50U"]
>>> shp = ifcopenshell.geom.create_shape(s, c)
>>> shp.geometry.verts
(4.675000000000003, -8.07499999999999, 2.657, 4.674999999999999, -10.24600000000002, 2.657, 6.226000000000004, -8.074999999999996, 2.657, 6.226, -10.24600000000003, 2.657, 4.675000000000003, -8.07499999999999, 2.6, 4.674999999999999, -10.24600000000002, 2.6, 6.226000000000004, -8.074999999999996, 2.6, 6.226, -10.24600000000003, 2.6, 4.674999999999999, -10.24600000000002, 2.657, 4.674999999999999, -10.24600000000002, 2.6, 4.675000000000003, -8.07499999999999, 2.657, 4.675000000000003, -8.07499999999999, 2.6, 6.226, -10.24600000000003, 2.657, 4.674999999999999, -10.24600000000002, 2.657, 6.226, -10.24600000000003, 2.6, 4.674999999999999, -10.24600000000002, 2.6, 6.226000000000004, -8.074999999999996, 2.657, 6.226, -10.24600000000003, 2.657, 6.226000000000004, -8.074999999999996, 2.6, 6.226, -10.24600000000003, 2.6, 4.675000000000003, -8.07499999999999, 2.657, 4.675000000000003, -8.07499999999999, 2.6, 6.226000000000004, -8.074999999999996, 2.657, 6.226000000000004, -8.074999999999996, 2.6)
>>> shp.geometry.normals
(3.059754518198021e-17, 0.0, -1.0, 3.059754518198021e-17, 0.0, -1.0, 3.059754518198021e-17, 0.0, -1.0, 3.059754518198021e-17, 0.0, -1.0, 2.110175529791737e-16, 0.0, -1.0, 2.110175529791737e-16, 0.0, -1.0, 2.110175529791737e-16, 0.0, -1.0, 2.110175529791737e-16, 0.0, -1.0, -1.0, 1.79434333701042e-15, 0.0, -1.0, 1.79434333701042e-15, 0.0, -1.0, 1.79434333701042e-15, 0.0, -1.0, 1.79434333701042e-15, 0.0, 6.8717866127669046e-15, 1.0, 0.0, 6.8717866127669046e-15, 1.0, 0.0, 6.8717866127669046e-15, 1.0, 0.0, 6.8717866127669046e-15, 1.0, 0.0, -1.0, 1.79434333701042e-15, 0.0, -1.0, 1.79434333701042e-15, 0.0, -1.0, 1.79434333701042e-15, 0.0, -1.0, 1.79434333701042e-15, 0.0, 3.4358933063834523e-15, 1.0, 0.0, 3.4358933063834523e-15, 1.0, 0.0, 3.4358933063834523e-15, 1.0, 0.0, 3.4358933063834523e-15, 1.0, 0.0)
>>>
>>> s.set(s.WELD_VERTICES, True)
>>> shp = ifcopenshell.geom.create_shape(s, c)
>>> shp.geometry.verts
(4.675000000000003, -8.07499999999999, 2.657, 4.674999999999999, -10.24600000000002, 2.657, 6.226000000000004, -8.074999999999996, 2.657, 6.226, -10.24600000000003, 2.657, 4.675000000000003, -8.07499999999999, 2.6, 4.674999999999999, -10.24600000000002, 2.6, 6.226000000000004, -8.074999999999996, 2.6, 6.226, -10.24600000000003, 2.6)
>>> shp.geometry.normals
()
+4 -3
View File
@@ -76,7 +76,7 @@ PROJECT_NAME = "IfcOpenShell"
USE_CURRENT_PYTHON_VERSION = os.getenv("USE_CURRENT_PYTHON_VERSION")
ADD_COMMIT_SHA = os.getenv("ADD_COMMIT_SHA")
PYTHON_VERSIONS = ["3.6.14", "3.7.13", "3.8.13", "3.9.11", "3.10.3", "3.11.0"]
PYTHON_VERSIONS = ["3.6.14", "3.7.13", "3.8.13", "3.9.11", "3.10.3", "3.11.7", "3.12.1"]
JSON_VERSION = "v3.6.1"
OCE_VERSION = "0.18.3"
OCCT_VERSION = "7.5.3"
@@ -892,8 +892,9 @@ if "IfcOpenShell-Python" in targets:
module_dir = os.path.dirname(run([python_executable, "-c", "import inspect, ifcopenshell; print(inspect.getfile(ifcopenshell))"]))
if platform.system() != "Darwin":
# TODO: This symbol name depends on the Python version?
run([strip, "-s", "-K", "PyInit__ifcopenshell_wrapper", "_ifcopenshell_wrapper.so"], cwd=module_dir)
if BUILD_CFG == "Release":
# TODO: This symbol name depends on the Python version?
run([strip, "-s", "-K", "PyInit__ifcopenshell_wrapper", "_ifcopenshell_wrapper.so"], cwd=module_dir)
return module_dir
+87 -47
View File
@@ -50,24 +50,31 @@ PYLIBDIR:=python3.10
PYNUMBER:=310
PYPI_VERSION:=3.10
endif
ifeq ($(PYVERSION), py311)
PYLIBDIR:=python3.11
PYNUMBER:=311
PYPI_VERSION:=3.11
endif
ifeq ($(PLATFORM), linux)
PYPI_PLATFORM:=--platform manylinux_2_17_x86_64
LXML_ARCH:=manylinux_2_28_x86_64
SHAPELY_ARCH:=manylinux_2_17_x86_64.manylinux2014_x86_64
PILLOW_ARCH:=manylinux_2_28_x86_64
ifeq ($(PYVERSION), py39)
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.3.4/download/linux-64/hpp-fcl-2.3.4-py39h40a70d0_0.conda
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.1.0/download/linux-64/eigenpy-3.1.0-py39hdfdd6bb_0.conda
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.4.0/download/linux-64/hpp-fcl-2.4.0-py39h359ec1e_0.conda
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.2.0/download/linux-64/eigenpy-3.2.0-py39hb1babde_0.conda
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.78.0/download/linux-64/boost-1.78.0-py39h7c9e3ff_4.tar.bz2
LXML_URL:=https://files.pythonhosted.org/packages/19/d9/a69c6aff5673554df48120565a14a50eaa41d29ae03b02faa0b023666318/lxml-4.6.3-cp39-cp39-manylinux2014_x86_64.whl
SHAPELY_URL:=https://files.pythonhosted.org/packages/2d/f2/8ec281d357e8bb7d08dc8d727f0e4c8ef3dae7d3fa75c69c8e452bb82d50/shapely-2.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
PILLOW_URL:=https://files.pythonhosted.org/packages/01/61/3ff85fb4bb596ce3d223c8fcf93c8df5c12bc8899dfb4fb3cb1c5b20dd5f/Pillow-9.2.0-cp39-cp39-manylinux_2_28_x86_64.whl
endif
ifeq ($(PYVERSION), py310)
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.3.4/download/linux-64/hpp-fcl-2.3.4-py310h995690b_0.conda
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.1.0/download/linux-64/eigenpy-3.1.0-py310hf02b7e0_0.conda
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.4.0/download/linux-64/hpp-fcl-2.4.0-py310hd002f03_0.conda
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.2.0/download/linux-64/eigenpy-3.2.0-py310hcfe0b05_0.conda
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.78.0/download/linux-64/boost-1.78.0-py310hc4a4660_4.tar.bz2
LXML_URL:=https://files.pythonhosted.org/packages/25/1e/19b46d8e8881fe0df2e20945d51919eeb1817836d62a90efa8506530e45c/lxml-4.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl
SHAPELY_URL:=https://files.pythonhosted.org/packages/a8/a5/403728b5614b28083f6424dfdefec5fcf58068495fb03bb08532671c642f/shapely-2.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
PILLOW_URL:=https://files.pythonhosted.org/packages/f6/51/320986ebd6d46a0e95c2240468ced73153b691ce07617078bcdf30c609ec/Pillow-9.2.0-cp310-cp310-manylinux_2_28_x86_64.whl
endif
ifeq ($(PYVERSION), py311)
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.4.0/download/linux-64/hpp-fcl-2.4.0-py311h04f086a_0.conda
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.2.0/download/linux-64/eigenpy-3.2.0-py311h995a085_0.conda
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.78.0/download/linux-64/boost-1.78.0-py311h59ea3da_3.tar.bz2
endif
ASSIMP_URL:=https://anaconda.org/conda-forge/assimp/5.0.1/download/linux-64/assimp-5.0.1-hedfc422_6.tar.bz2
QHULL_URL:=https://anaconda.org/conda-forge/qhull/2020.2/download/linux-64/qhull-2020.2-h4bd325d_2.tar.bz2
@@ -78,21 +85,25 @@ endif
ifeq ($(PLATFORM), macos)
PYPI_PLATFORM:=--platform macosx_10_9_x86_64
SHAPELY_ARCH:=macosx_10_9_x86_64
PILLOW_ARCH:=macosx_10_10_x86_64
ifeq ($(PYVERSION), py39)
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.3.4/download/osx-64/hpp-fcl-2.3.4-py39hd85b194_0.conda
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.1.0/download/osx-64/eigenpy-3.1.0-py39hc4d6e28_0.conda
LXML_ARCH:=macosx_11_0_x86_64
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.4.0/download/osx-64/hpp-fcl-2.4.0-py39h26350b9_0.conda
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.2.0/download/osx-64/eigenpy-3.2.0-py39h3323527_0.conda
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.78.0/download/osx-64/boost-1.78.0-py39h953a6b8_4.tar.bz2
LXML_URL:=https://files.pythonhosted.org/packages/b8/74/a71f7ad72e8db54ce899efab84507b801660750cbbfa6a39e6717557d36a/lxml-4.6.3-cp39-cp39-macosx_10_9_x86_64.whl
SHAPELY_URL:=https://files.pythonhosted.org/packages/36/a4/7e542a209f862f967d7cb8e939eff155f4294a27d17e16441fb8bdd51a2c/shapely-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl
PILLOW_URL:=https://files.pythonhosted.org/packages/88/7a/ddfe28b485b623361457d4783007c1f9ba83a87f93e7fec32f64793efb6c/Pillow-9.2.0-cp39-cp39-macosx_10_10_x86_64.whl
endif
ifeq ($(PYVERSION), py310)
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.3.4/download/osx-64/hpp-fcl-2.3.4-py310h1db6f5f_0.conda
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.1.0/download/osx-64/eigenpy-3.1.0-py310h43da829_0.conda
LXML_ARCH:=macosx_11_0_x86_64
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.4.0/download/osx-64/hpp-fcl-2.4.0-py310ha216469_0.conda
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.2.0/download/osx-64/eigenpy-3.2.0-py310h33a8c9b_0.conda
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.78.0/download/osx-64/boost-1.78.0-py310h3e792ce_4.tar.bz2
LXML_URL:=https://files.pythonhosted.org/packages/a1/44/17b7dac7a18807d30e2fe10c3328c152808f5464565e230bfd0e77f178c6/lxml-4.8.0-cp310-cp310-macosx_10_15_x86_64.whl
SHAPELY_URL:=https://files.pythonhosted.org/packages/1f/2a/dc3353c2431cf53e8d04bb8fba27e584410ca3435c9c85f76d71bf0c0e80/shapely-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl
PILLOW_URL:=https://files.pythonhosted.org/packages/d8/60/b13c00d403f34110e96c1b5c0afa73ce461efe3fe960c3a7e3e7fe190d82/Pillow-9.2.0-cp310-cp310-macosx_10_10_x86_64.whl
endif
ifeq ($(PYVERSION), py311)
LXML_ARCH:=macosx_11_0_universal2
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.4.0/download/osx-64/hpp-fcl-2.4.0-py311hebdd1a2_0.conda
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.2.0/download/osx-64/eigenpy-3.2.0-py311h62e90b6_0.conda
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.78.0/download/osx-64/boost-1.78.0-py311h349b758_4.tar.bz2
endif
ASSIMP_URL:=https://anaconda.org/conda-forge/assimp/5.0.1/download/osx-64/assimp-5.0.1-h1224e73_6.tar.bz2
QHULL_URL:=https://anaconda.org/conda-forge/qhull/2020.2/download/osx-64/qhull-2020.2-h940c156_2.tar.bz2
@@ -103,21 +114,23 @@ endif
ifeq ($(PLATFORM), macosm1)
PYPI_PLATFORM:=--platform macosx_11_0_arm64
LXML_ARCH:=macosx_11_0_universal2
SHAPELY_ARCH:=macosx_11_0_arm64
PILLOW_ARCH:=macosx_11_0_arm64
ifeq ($(PYVERSION), py39)
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.3.4/download/osx-arm64/hpp-fcl-2.3.4-py39hc34188a_0.conda
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.1.0/download/osx-arm64/eigenpy-3.1.0-py39h13cfc01_0.conda
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.4.0/download/osx-arm64/hpp-fcl-2.4.0-py39h82b2ff2_0.conda
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.2.0/download/osx-arm64/eigenpy-3.2.0-py39h3a9e884_0.conda
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.78.0/download/osx-arm64/boost-1.78.0-py39h99de9ae_4.tar.bz2
LXML_URL:=https://anaconda.org/conda-forge/lxml/4.9.1/download/osx-arm64/lxml-4.9.1-py39h9eb174b_0.tar.bz2
SHAPELY_URL:=https://files.pythonhosted.org/packages/ea/aa/45fbd031edf3149cb767d8b9f9db45d5faf0324d743c6b8fb0298cc022d0/shapely-2.0.1-cp39-cp39-macosx_11_0_arm64.whl
PILLOW_URL:=https://files.pythonhosted.org/packages/aa/bc/21097cd891dd2fa02f2b3d767e02e883e026482e59d29975d1bc30024aa3/Pillow-9.2.0-cp39-cp39-macosx_11_0_arm64.whl
endif
ifeq ($(PYVERSION), py310)
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.3.4/download/osx-arm64/hpp-fcl-2.3.4-py310h46fc4cd_0.conda
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.1.0/download/osx-arm64/eigenpy-3.1.0-py310ha2643af_0.conda
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.4.0/download/osx-arm64/hpp-fcl-2.4.0-py310hdb6c3eb_0.conda
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.2.0/download/osx-arm64/eigenpy-3.2.0-py310h96e9823_0.conda
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.78.0/download/osx-arm64/boost-1.78.0-py310h629746b_4.tar.bz2
LXML_URL:=https://anaconda.org/conda-forge/lxml/4.9.1/download/osx-arm64/lxml-4.9.1-py310h02f21da_0.tar.bz2
SHAPELY_URL:=https://files.pythonhosted.org/packages/ec/41/d59208743e737184e1b403e95a937aebb022b8459e99efbcd5208fc8be46/shapely-2.0.1-cp310-cp310-macosx_11_0_arm64.whl
PILLOW_URL:=https://files.pythonhosted.org/packages/0c/5f/117b653cad585f3aedfe0de996c292e67d4b020ed77f652e5a6c8c24f908/Pillow-9.2.0-cp310-cp310-macosx_11_0_arm64.whl
endif
ifeq ($(PYVERSION), py311)
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.4.0/download/osx-arm64/hpp-fcl-2.4.0-py311hc0c29b9_0.conda
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.2.0/download/osx-arm64/eigenpy-3.2.0-py311h24fa84d_0.conda
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.78.0/download/osx-arm64/boost-1.78.0-py311h5c64717_4.tar.bz2
endif
ASSIMP_URL:=https://anaconda.org/conda-forge/assimp/5.0.1/download/osx-arm64/assimp-5.0.1-h0f81e16_7.tar.bz2
QHULL_URL:=https://anaconda.org/conda-forge/qhull/2020.2/download/osx-arm64/qhull-2020.2-hc021e02_2.tar.bz2
@@ -128,21 +141,23 @@ endif
ifeq ($(PLATFORM), win)
PYPI_PLATFORM:=--platform win_amd64
LXML_ARCH:=win_amd64
SHAPELY_ARCH:=win_amd64
PILLOW_ARCH:=win_amd64
ifeq ($(PYVERSION), py39)
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.3.4/download/win-64/hpp-fcl-2.3.4-py39h39b25cb_0.conda
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.1.0/download/win-64/eigenpy-3.1.0-py39hb6c915b_0.conda
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.4.0/download/win-64/hpp-fcl-2.4.0-py39hf3ebc58_0.conda
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.2.0/download/win-64/eigenpy-3.2.0-py39ha1f6a47_0.conda
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.78.0/download/win-64/boost-1.78.0-py39hea4d8d1_4.tar.bz2
LXML_URL:=https://files.pythonhosted.org/packages/72/d4/426ecb8849c47c3e370c87aa0ac05d85768df917ffea27fcd6686a5e6495/lxml-4.6.3-cp39-cp39-win_amd64.whl
SHAPELY_URL:=https://files.pythonhosted.org/packages/a7/ae/eab645c4075093584b7a65ab71cb8ff4563a015bd935c9b55dba14b2c1eb/shapely-2.0.1-cp39-cp39-win_amd64.whl
PILLOW_URL:=https://files.pythonhosted.org/packages/19/3f/b4d4bcf05dbcbe07f2e9613a8f4180c297395e73a91d8ad22c32c6624f8c/Pillow-9.2.0-cp39-cp39-win_amd64.whl
endif
ifeq ($(PYVERSION), py310)
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.3.4/download/win-64/hpp-fcl-2.3.4-py310heb59267_0.conda
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.1.0/download/win-64/eigenpy-3.1.0-py310hd146abe_0.conda
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.4.0/download/win-64/hpp-fcl-2.4.0-py310h1bf3f0a_0.conda
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.2.0/download/win-64/eigenpy-3.2.0-py310h7fb871d_0.conda
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.78.0/download/win-64/boost-1.78.0-py310h220cb41_4.tar.bz2
LXML_URL:=https://files.pythonhosted.org/packages/f6/71/65c80a4caa1617a4c6e8fe1500cebb179db96232e2f623bfe6a1f4294e39/lxml-4.8.0-cp310-cp310-win_amd64.whl
SHAPELY_URL:=https://files.pythonhosted.org/packages/81/8a/7ac076a86b2632f1872284c5e60ed5f2fc26094875a85b35d9fa17b52504/shapely-2.0.1-cp310-cp310-win_amd64.whl
PILLOW_URL:=https://files.pythonhosted.org/packages/02/55/67a3c17b9e7d972ed8c246f104da99ca4f3ea42fba566697e479011b84b6/Pillow-9.2.0-cp310-cp310-win_amd64.whl
endif
ifeq ($(PYVERSION), py311)
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.4.0/download/win-64/hpp-fcl-2.4.0-py311hd263b2c_0.conda
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.2.0/download/win-64/eigenpy-3.2.0-py311hd354db1_0.conda
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.78.0/download/win-64/boost-1.78.0-py311h12feb9d_3.tar.bz2
endif
ASSIMP_URL:=https://anaconda.org/conda-forge/assimp/5.2.5/download/win-64/assimp-5.2.5-h4dcb625_0.tar.bz2
QHULL_URL:=https://anaconda.org/conda-forge/qhull/2020.2/download/win-64/qhull-2020.2-h70d2c02_2.tar.bz2
@@ -151,6 +166,33 @@ BOOSTCPP_URL:=https://anaconda.org/conda-forge/boost-cpp/1.78.0/download/win-64/
ZLIB_URL:=https://anaconda.org/conda-forge/zlib/1.2.11/download/win-64/zlib-1.2.11-h62dcd97_1010.tar.bz2
endif
LXML_NAME:=lxml
LXML_VER:=4.9.4
SHAPELY_NAME:=shapely
SHAPELY_VER:=2.0.2
PILLOW_NAME:=pillow
PILLOW_PKG_NAME:=Pillow
PILLOW_VER:=9.5.0
SHAPELY_URL:=https://files.pythonhosted.org/packages/cp$(PYNUMBER)/s/$(SHAPELY_NAME)/$(SHAPELY_NAME)-$(SHAPELY_VER)-cp$(PYNUMBER)-cp$(PYNUMBER)-$(SHAPELY_ARCH).whl
PILLOW_URL:=https://files.pythonhosted.org/packages/cp$(PYNUMBER)/p/$(PILLOW_NAME)/$(PILLOW_PKG_NAME)-$(PILLOW_VER)-cp$(PYNUMBER)-cp$(PYNUMBER)-$(PILLOW_ARCH).whl
ifeq ($(PLATFORM), macosm1)
ifeq ($(PYVERSION), py39)
LXML_URL:=https://anaconda.org/conda-forge/lxml/4.9.1/download/osx-arm64/lxml-4.9.1-py39h0520ce3_1.tar.bz2
endif
ifeq ($(PYVERSION), py310)
LXML_URL:=https://anaconda.org/conda-forge/lxml/4.9.1/download/osx-arm64/lxml-4.9.1-py310h02f21da_0.tar.bz2
endif
ifeq ($(PYVERSION), py311)
LXML_URL:=https://anaconda.org/conda-forge/lxml/4.9.1/download/osx-arm64/lxml-4.9.1-py311h246f609_1.tar.bz2
endif
else
LXML_URL:=https://files.pythonhosted.org/packages/cp$(PYNUMBER)/l/$(LXML_NAME)/$(LXML_NAME)-$(LXML_VER)-cp$(PYNUMBER)-cp$(PYNUMBER)-$(LXML_ARCH).whl
endif
$(info $$LXML_URL is [${LXML_URL}])
$(info $$SHAPELY_URL is [${SHAPELY_URL}])
$(info $$PILLOW_URL is [${PILLOW_URL}])
.PHONY: bump
bump:
cd . && $(SED) "s/$(OLD)/$(NEW)/" Makefile
@@ -629,13 +671,6 @@ endif
cp -r dist/working/IFC2JSON_python-master/file_converters/ifcjson dist/blenderbim/libs/site/packages/
rm -rf dist/working
# Provides OpenLCA functionality
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/85/33/f91b96e9e8608ff65a003b692e8a9cdd60f2178f60617e5b1d21334c009c/olca-ipc-0.0.10.tar.gz
cd dist/working && tar -xzvf olca-ipc*
cd dist/working/olca-ipc-0.0.10/ && cp -r olca ../../blenderbim/libs/site/packages/
rm -rf dist/working
# Provides Brickschema functionality
mkdir dist/working
# For now lets bundle the latest nightly schema
@@ -661,6 +696,11 @@ endif
# Required for Desktop icon and file association
cp -r blenderbim/libs/desktop dist/blenderbim/libs/
# generate translations module for BBIM build
git clone https://github.com/IfcOpenShell/blenderbim-translations.git dist/working
python scripts/bbim_translations.py -i "dist/working" -o "dist/blenderbim"
rm -rf dist/working
# Remove dependencies also bundled with Blender
rm -rf dist/blenderbim/libs/site/packages/numpy
rm -rf dist/blenderbim/libs/site/packages/numpy.libs
+24 -23
View File
@@ -24,6 +24,10 @@ import blenderbim
import importlib
from . import handler, ui, prop, operator, helper
try:
from blenderbim.translations import translations_dict
except ImportError:
translations_dict = {}
cwd = os.path.dirname(os.path.realpath(__file__))
modules = {
@@ -44,6 +48,7 @@ modules = {
"spatial": None,
"void": None,
"aggregate": None,
"nest": None,
"geometry": None,
"fm": None,
"resource": None,
@@ -67,7 +72,6 @@ modules = {
"document": None,
"pset_template": None,
"clash": None,
"lca": None,
"csv": None,
"tester": None,
"bimtester": None,
@@ -92,28 +96,28 @@ for name in modules.keys():
classes = [
operator.AddIfcFile,
operator.BIM_OT_add_section_plane,
operator.BIM_OT_open_webbrowser,
operator.BIM_OT_remove_section_plane,
operator.BIM_OT_show_description,
operator.EditBlenderCollection,
operator.FileAssociate,
operator.FileUnassociate,
operator.OpenUpstream,
operator.OpenUri,
operator.SwitchTab,
operator.SetTab,
operator.ReloadIfcFile,
operator.RemoveIfcFile,
operator.SelectDataDir,
operator.SelectIfcFile,
operator.ReloadSelectedIfcFile,
operator.SelectSchemaDir,
operator.FileAssociate,
operator.FileUnassociate,
operator.SelectURIAttribute,
operator.EditBlenderCollection,
operator.BIM_OT_open_webbrowser,
operator.BIM_OT_show_description,
operator.SetTab,
operator.SwitchTab,
prop.StrProperty,
operator.BIM_OT_enum_property_search, # /!\ Register AFTER prop.StrProperty
prop.ObjProperty,
prop.Attribute,
prop.BIMAreaProperties,
prop.BIMTabProperties,
prop.BIMProperties,
prop.IfcParameter,
prop.PsetQto,
@@ -180,6 +184,7 @@ addon_keymaps = []
icons = None
is_registering = False
last_commit_hash = "8888888"
overridden_scene_panels = dict()
def on_register(scene):
@@ -204,6 +209,7 @@ def register():
bpy.app.handlers.load_post.append(handler.loadIfcStore)
bpy.types.Scene.BIMProperties = bpy.props.PointerProperty(type=prop.BIMProperties)
bpy.types.Screen.BIMAreaProperties = bpy.props.CollectionProperty(type=prop.BIMAreaProperties)
bpy.types.Screen.BIMTabProperties = bpy.props.PointerProperty(type=prop.BIMTabProperties)
bpy.types.Collection.BIMCollectionProperties = bpy.props.PointerProperty(type=prop.BIMCollectionProperties)
bpy.types.Object.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties)
bpy.types.Material.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties)
@@ -247,6 +253,8 @@ def register():
except:
pass
bpy.app.translations.register("blenderbim", translations_dict)
def unregister():
global icons
@@ -284,17 +292,10 @@ def unregister():
km.keymap_items.remove(kmi)
addon_keymaps.clear()
for panel in [
"SCENE_PT_scene",
"SCENE_PT_unit",
"SCENE_PT_physics",
"SCENE_PT_rigid_body_world",
"SCENE_PT_audio",
"SCENE_PT_keying_sets",
"SCENE_PT_simulation",
"SCENE_PT_custom_props",
]:
try:
bpy.utils.unregister_class(getattr(handler, f"Override_{panel}"))
except:
pass
for panel in tuple(overridden_scene_panels.keys()):
original_panel, override_panel = overridden_scene_panels[panel]
bpy.utils.unregister_class(override_panel)
bpy.utils.register_class(original_panel)
del overridden_scene_panels[panel]
bpy.app.translations.unregister("blenderbim")
@@ -0,0 +1,993 @@
ISO-10303-21;
HEADER;
FILE_DESCRIPTION(('ViewDefinition[DesignTransferView]'),'2;1');
FILE_NAME('/dev/null','2024-01-24T15:32:17+05:00',(),(),'IfcOpenShell v0.7.0-6c9e130ca','IfcOpenShell v0.7.0-6c9e130ca','Nobody');
FILE_SCHEMA(('IFC4'));
ENDSEC;
DATA;
#1=IFCPROJECT('3GRx5GJtv7EvI86hmjeQD6',$,'Landscape Assets Library',$,$,$,$,(#10,#14),#5);
#2=IFCPROJECTLIBRARY('1Go1D2HIr9AR4M6PdJb8DW',$,'Landscape Assets Library',$,$,$,$,$,$);
#3=IFCRELDECLARES('2Nc$sSRrvCzOJbe4Q6ZcVa',$,$,$,#1,(#2));
#4=IFCSIUNIT(*,.LENGTHUNIT.,.MILLI.,.METRE.);
#5=IFCUNITASSIGNMENT((#4));
#6=IFCCARTESIANPOINT((0.,0.,0.));
#7=IFCDIRECTION((0.,0.,1.));
#8=IFCDIRECTION((1.,0.,0.));
#9=IFCAXIS2PLACEMENT3D(#6,#7,#8);
#10=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-05,#9,$);
#11=IFCCARTESIANPOINT((0.,0.));
#12=IFCDIRECTION((1.,0.));
#13=IFCAXIS2PLACEMENT2D(#11,#12);
#14=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Plan',2,1.E-05,#13,$);
#15=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Body','Model',*,*,*,*,#10,$,.MODEL_VIEW.,$);
#16=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Body','Plan',*,*,*,*,#14,$,.PLAN_VIEW.,$);
#17=IFCCARTESIANPOINTLIST2D(((-331.408782958984,488.559936523438),(-428.486114501953,371.370391845703),(-408.744873046875,331.425659179688),(-492.345001220703,263.470184326172),(-519.991760253906,89.2266616821289),(-366.53076171875,65.1004943847656),(-523.9287109375,51.0255508422852),(-542.778625488281,-161.150390625),(-455.430816650391,-198.061676025391),(-467.317413330078,-264.261047363281),(-389.842987060547,-378.319213867188),(-351.178894042969,-382.372741699219),(-243.823715209961,-498.601684570312),(-171.287063598633,-509.852447509766),(-182.036499023438,-454.125518798828),(-123.127815246582,-538.493591308594),(208.583557128906,-495.693969726562),(174.199142456055,-387.101898193359),(269.968353271484,-457.362365722656),(517.720397949219,-208.39501953125),(467.592559814453,-165.981643676758),(535.54443359375,-138.483825683594),(546.240173339844,47.7887229919434),(425.388610839844,29.4520435333252),(514.287658691406,107.907821655273),(467.776489257812,312.002685546875),(374.896423339844,365.066589355469),(325.083190917969,461.613433837891),(224.657806396484,496.0244140625),(105.845329284668,424.517761230469),(191.164672851562,514.677307128906),(-7.41858577728271,553.592407226562),(-105.371849060059,531.902709960938),(-180.417175292969,499.200317382812)));
#18=IFCINDEXEDPOLYCURVE(#17,(IFCLINEINDEX((1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,1))),$);
#19=IFCCARTESIANPOINTLIST3D(((80.,0.,0.),(40.,69.2820323027551,0.),(-40.,69.2820323027551,0.),(-80.,9.79717439317883E-15,0.),(-40.,-69.2820323027551,0.),(40.,-69.2820323027551,0.),(80.,0.,280.),(40.,69.2820323027551,280.),(-40.,69.2820323027551,280.),(-80.,9.79717439317883E-15,280.),(-40.,-69.2820323027551,280.),(40.,-69.2820323027551,280.),(570.140986817674,-5.11998806339175,502.765430824539),(261.339072714044,511.863483235935,520.441255965265),(-271.449977559115,464.840881207563,498.631051036932),(-577.400357563209,-18.0002392785791,529.745674601363),(-246.747896537159,-522.096293601777,523.07445182747),(289.929723989709,-497.828722152031,481.563368832141),(502.264135660384,-3.10815712380199,727.667741239885),(256.486268377103,397.638199139059,733.570036889682),(-210.099579404528,457.859720268934,722.445245365077),(-522.479901100414,-35.7080296205946,687.931736233252),(-213.081169792834,-438.257812253687,704.648197605265),(273.503462940155,-437.429578762916,717.812431534607),(409.72756228227,-31.3204193188668,937.51589967911),(231.939555298324,334.085354425246,911.578097173966),(-213.440011711198,325.481782873574,931.184023328963),(-391.4385670944,11.3681475906559,933.839400395571),(-181.597613330968,-384.045646583411,936.269525762716),(198.107253544529,-351.52926490917,903.558501964971),(358.836914477244,-29.0443800107266,1124.54961826854),(191.636382145782,325.816574427016,1143.76409675514),(-175.686180114122,275.257306965059,1132.35007279295),(-330.55780048986,-23.1508504281077,1121.17416888324),(-172.687090174965,-317.707089809191,1159.279616419),(192.616008554028,-273.601791847896,1147.14103266758),(252.691797114565,-4.70084047240072,1330.07490310776),(147.138603790187,216.221351494253,1352.71275883304),(-162.078730999078,224.60205813657,1371.67941135993),(-271.544406853979,26.607279268613,1378.19571732018),(-150.451939922955,-226.516554345382,1395.27272058484),(133.281231324475,-220.990480915293,1334.23398662872),(187.5691337229,-2.77622907865673,1575.7384942221),(88.4380124532162,169.216774598146,1585.64117034361),(-94.8941720337192,139.038794764388,1578.6341137138),(-154.588503036945,-6.10749408831602,1549.40002660187),(-129.962875945536,-160.551322551302,1550.49839466833),(64.8441918513045,-191.270989336927,1579.55078264226),(0.05,0.,1800.),(0.025,0.0433012701892219,1800.),(-0.025,0.0433012701892219,1800.),(-0.05,6.12323399573677E-18,1800.),(-0.025,-0.0433012701892219,1800.),(0.025,-0.0433012701892219,1800.)));
#20=IFCINDEXEDPOLYGONALFACE((1,2,3,4,5,6));
#21=IFCINDEXEDPOLYGONALFACE((6,1,7,12));
#22=IFCINDEXEDPOLYGONALFACE((1,2,8,7));
#23=IFCINDEXEDPOLYGONALFACE((2,3,9,8));
#24=IFCINDEXEDPOLYGONALFACE((3,4,10,9));
#25=IFCINDEXEDPOLYGONALFACE((4,5,11,10));
#26=IFCINDEXEDPOLYGONALFACE((5,6,12,11));
#27=IFCINDEXEDPOLYGONALFACE((12,7,13,18));
#28=IFCINDEXEDPOLYGONALFACE((7,8,14,13));
#29=IFCINDEXEDPOLYGONALFACE((8,9,15,14));
#30=IFCINDEXEDPOLYGONALFACE((9,10,16,15));
#31=IFCINDEXEDPOLYGONALFACE((10,11,17,16));
#32=IFCINDEXEDPOLYGONALFACE((11,12,18,17));
#33=IFCINDEXEDPOLYGONALFACE((18,13,19,24));
#34=IFCINDEXEDPOLYGONALFACE((13,14,20,19));
#35=IFCINDEXEDPOLYGONALFACE((14,15,21,20));
#36=IFCINDEXEDPOLYGONALFACE((15,16,22,21));
#37=IFCINDEXEDPOLYGONALFACE((16,17,23,22));
#38=IFCINDEXEDPOLYGONALFACE((17,18,24,23));
#39=IFCINDEXEDPOLYGONALFACE((24,19,25,30));
#40=IFCINDEXEDPOLYGONALFACE((19,20,26,25));
#41=IFCINDEXEDPOLYGONALFACE((20,21,27,26));
#42=IFCINDEXEDPOLYGONALFACE((21,22,28,27));
#43=IFCINDEXEDPOLYGONALFACE((22,23,29,28));
#44=IFCINDEXEDPOLYGONALFACE((23,24,30,29));
#45=IFCINDEXEDPOLYGONALFACE((30,25,31,36));
#46=IFCINDEXEDPOLYGONALFACE((25,26,32,31));
#47=IFCINDEXEDPOLYGONALFACE((26,27,33,32));
#48=IFCINDEXEDPOLYGONALFACE((27,28,34,33));
#49=IFCINDEXEDPOLYGONALFACE((28,29,35,34));
#50=IFCINDEXEDPOLYGONALFACE((29,30,36,35));
#51=IFCINDEXEDPOLYGONALFACE((36,31,37,42));
#52=IFCINDEXEDPOLYGONALFACE((31,32,38,37));
#53=IFCINDEXEDPOLYGONALFACE((32,33,39,38));
#54=IFCINDEXEDPOLYGONALFACE((33,34,40,39));
#55=IFCINDEXEDPOLYGONALFACE((34,35,41,40));
#56=IFCINDEXEDPOLYGONALFACE((35,36,42,41));
#57=IFCINDEXEDPOLYGONALFACE((42,37,43,48));
#58=IFCINDEXEDPOLYGONALFACE((37,38,44,43));
#59=IFCINDEXEDPOLYGONALFACE((38,39,45,44));
#60=IFCINDEXEDPOLYGONALFACE((39,40,46,45));
#61=IFCINDEXEDPOLYGONALFACE((40,41,47,46));
#62=IFCINDEXEDPOLYGONALFACE((41,42,48,47));
#63=IFCINDEXEDPOLYGONALFACE((48,43,49,54));
#64=IFCINDEXEDPOLYGONALFACE((43,44,50,49));
#65=IFCINDEXEDPOLYGONALFACE((44,45,51,50));
#66=IFCINDEXEDPOLYGONALFACE((45,46,52,51));
#67=IFCINDEXEDPOLYGONALFACE((46,47,53,52));
#68=IFCINDEXEDPOLYGONALFACE((47,48,54,53));
#69=IFCINDEXEDPOLYGONALFACE((49,50,51,52,53,54));
#70=IFCPOLYGONALFACESET(#19,$,(#20,#21,#22,#23,#24,#25,#26,#27,#28,#29,#30,#31,#32,#33,#34,#35,#36,#37,#38,#39,#40,#41,#42,#43,#44,#45,#46,#47,#48,#49,#50,#51,#52,#53,#54,#55,#56,#57,#58,#59,#60,#61,#62,#63,#64,#65,#66,#67,#68,#69),$);
#71=IFCSHAPEREPRESENTATION(#16,'Body','Curve2D',(#18));
#72=IFCSHAPEREPRESENTATION(#15,'Body','Tessellation',(#70));
#73=IFCGEOGRAPHICELEMENTTYPE('34zHVx8cX0dRoaynJ1QKTg',$,'Cone Tree (1.8m)',$,$,$,(#78,#83),$,$,.NOTDEFINED.);
#74=IFCCARTESIANPOINT((0.,0.,0.));
#75=IFCDIRECTION((1.,0.,0.));
#76=IFCDIRECTION((0.,0.,1.));
#77=IFCAXIS2PLACEMENT3D(#74,#76,#75);
#78=IFCREPRESENTATIONMAP(#77,#72);
#79=IFCCARTESIANPOINT((0.,0.,0.));
#80=IFCDIRECTION((1.,0.,0.));
#81=IFCDIRECTION((0.,0.,1.));
#82=IFCAXIS2PLACEMENT3D(#79,#81,#80);
#83=IFCREPRESENTATIONMAP(#82,#71);
#84=IFCRELDECLARES('2hc5gFJQT6VP_pTBscCuS_',$,$,$,#2,(#840,#409,#680,#208,#613,#73,#342,#773,#275,#479,#742,#907,#711,#141,#546,#974,#444));
#85=IFCCARTESIANPOINTLIST2D(((-1281.64282226562,1889.38659667969),(-1657.06579589844,1436.1845703125),(-1580.72131347656,1281.70812988281),(-1904.02453613281,1018.90686035156),(-2010.94165039062,345.062408447266),(-1417.46850585938,251.760314941406),(-2026.16674804688,197.328903198242),(-2099.06420898438,-623.209899902344),(-1761.26782226562,-765.955322265625),(-1807.236328125,-1021.96527099609),(-1507.62292480469,-1463.05749511719),(-1358.09887695312,-1478.73352050781),(-942.928833007812,-1928.220703125),(-662.411010742188,-1971.73022460938),(-703.981872558594,-1756.21984863281),(-476.166870117188,-2082.49291992188),(806.646179199219,-1916.97583007812),(673.672790527344,-1497.02233886719),(1044.03686523438,-1768.73767089844),(2002.15771484375,-805.917053222656),(1808.30041503906,-641.893615722656),(2071.087890625,-535.552490234375),(2112.45092773438,184.811248779297),(1645.0869140625,113.898612976074),(1988.88244628906,417.307220458984),(1809.01171875,1206.59436035156),(1449.82067871094,1411.80627441406),(1257.18005371094,1785.17761230469),(868.809387207031,1918.25366210938),(409.3310546875,1641.71911621094),(739.282836914062,1990.38916015625),(-28.6895751953125,2140.8837890625),(-407.5,2057.00415039062),(-697.719543457031,1930.53576660156)));
#86=IFCINDEXEDPOLYCURVE(#85,(IFCLINEINDEX((1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,1))),$);
#87=IFCCARTESIANPOINTLIST3D(((220.,0.,0.),(110.,190.525588832576,0.),(-110.,190.525588832577,0.),(-220.,2.69422295812418E-14,0.),(-110.,-190.525588832576,0.),(110.,-190.525588832576,0.),(220.,0.,740.),(110.,190.525588832576,740.),(-110.,190.525588832577,740.),(-220.,2.69422295812418E-14,740.),(-110.,-190.525588832576,740.),(110.,-190.525588832576,740.),(2227.84155681395,-42.6665671949312,2823.99763782354),(916.158939283701,2080.25825808339,2971.29618066292),(-1000.41647965929,1688.40324118029,2789.54447292681),(-2288.33631302674,-150.00199398816,3048.8330026304),(-794.565804476328,-2165.53167779874,2993.23947951463),(1154.41436658091,-1963.30191571753,2647.31378788689),(1998.64557494764,-25.9013093650166,4925.80260557047),(1043.94112536474,1419.75032646046,4974.98840265211),(-657.385383926618,1921.59633587608,4882.28180661374),(-2167.11028694789,-297.566913504955,4594.66923051519),(-682.231970495834,-1758.24710241569,4733.97307528197),(1185.75108005685,-1751.34515665926,4843.67502469316),(1563.95190790781,-261.00349432389,6902.15630684973),(1007.60740526381,1181.51272302971,6686.0079526402),(-853.444542037762,1109.81629343244,6849.3906705985),(-1411.54361467555,94.7345632554656,6871.51881282023),(-588.091222202507,-1597.84849101442,6891.76985754645),(725.67155731552,-1326.87864372908,6619.17799256523),(1476.3076206437,-242.036500089388,8688.38967604738),(839.969851214854,1403.97565889549,8848.51033010232),(-707.051500951019,982.648430045852,8753.39346375077),(-1240.6483374155,-192.923753567565,8660.26093116989),(-682.059084791378,-1336.39662041362,8977.80632730116),(848.133404616898,-968.852470736159,8876.65146270604),(928.209420399157,-39.1736706033394,10628.7194306599),(637.377253807116,782.05157030683,10817.3682283706),(-761.878313881205,851.890792326142,10975.4236660946),(-1085.31450156094,221.727327238442,11029.7262157634),(-664.988388246851,-867.844927399578,11172.0345763022),(521.899149926181,-821.794315482163,10663.3784600012),(721.965003246392,-23.1352423221394,12903.5350708985),(316.42788155458,681.716198690303,12986.057371911),(-370.229211392105,430.233033408991,12927.6652333293),(-447.126414196768,-50.8957840693003,12684.0478407298),(-662.468410657245,-609.504098299938,12693.2009079504),(119.812709871982,-865.501321513485,12935.3041410664),(0.05,0.,15000.),(0.025,0.0433012701892219,15000.),(-0.025,0.0433012701892219,15000.),(-0.05,6.12323399573677E-18,15000.),(-0.025,-0.0433012701892219,15000.),(0.025,-0.0433012701892219,15000.)));
#88=IFCINDEXEDPOLYGONALFACE((1,2,3,4,5,6));
#89=IFCINDEXEDPOLYGONALFACE((6,1,7,12));
#90=IFCINDEXEDPOLYGONALFACE((1,2,8,7));
#91=IFCINDEXEDPOLYGONALFACE((2,3,9,8));
#92=IFCINDEXEDPOLYGONALFACE((3,4,10,9));
#93=IFCINDEXEDPOLYGONALFACE((4,5,11,10));
#94=IFCINDEXEDPOLYGONALFACE((5,6,12,11));
#95=IFCINDEXEDPOLYGONALFACE((12,7,13,18));
#96=IFCINDEXEDPOLYGONALFACE((7,8,14,13));
#97=IFCINDEXEDPOLYGONALFACE((8,9,15,14));
#98=IFCINDEXEDPOLYGONALFACE((9,10,16,15));
#99=IFCINDEXEDPOLYGONALFACE((10,11,17,16));
#100=IFCINDEXEDPOLYGONALFACE((11,12,18,17));
#101=IFCINDEXEDPOLYGONALFACE((18,13,19,24));
#102=IFCINDEXEDPOLYGONALFACE((13,14,20,19));
#103=IFCINDEXEDPOLYGONALFACE((14,15,21,20));
#104=IFCINDEXEDPOLYGONALFACE((15,16,22,21));
#105=IFCINDEXEDPOLYGONALFACE((16,17,23,22));
#106=IFCINDEXEDPOLYGONALFACE((17,18,24,23));
#107=IFCINDEXEDPOLYGONALFACE((24,19,25,30));
#108=IFCINDEXEDPOLYGONALFACE((19,20,26,25));
#109=IFCINDEXEDPOLYGONALFACE((20,21,27,26));
#110=IFCINDEXEDPOLYGONALFACE((21,22,28,27));
#111=IFCINDEXEDPOLYGONALFACE((22,23,29,28));
#112=IFCINDEXEDPOLYGONALFACE((23,24,30,29));
#113=IFCINDEXEDPOLYGONALFACE((30,25,31,36));
#114=IFCINDEXEDPOLYGONALFACE((25,26,32,31));
#115=IFCINDEXEDPOLYGONALFACE((26,27,33,32));
#116=IFCINDEXEDPOLYGONALFACE((27,28,34,33));
#117=IFCINDEXEDPOLYGONALFACE((28,29,35,34));
#118=IFCINDEXEDPOLYGONALFACE((29,30,36,35));
#119=IFCINDEXEDPOLYGONALFACE((36,31,37,42));
#120=IFCINDEXEDPOLYGONALFACE((31,32,38,37));
#121=IFCINDEXEDPOLYGONALFACE((32,33,39,38));
#122=IFCINDEXEDPOLYGONALFACE((33,34,40,39));
#123=IFCINDEXEDPOLYGONALFACE((34,35,41,40));
#124=IFCINDEXEDPOLYGONALFACE((35,36,42,41));
#125=IFCINDEXEDPOLYGONALFACE((42,37,43,48));
#126=IFCINDEXEDPOLYGONALFACE((37,38,44,43));
#127=IFCINDEXEDPOLYGONALFACE((38,39,45,44));
#128=IFCINDEXEDPOLYGONALFACE((39,40,46,45));
#129=IFCINDEXEDPOLYGONALFACE((40,41,47,46));
#130=IFCINDEXEDPOLYGONALFACE((41,42,48,47));
#131=IFCINDEXEDPOLYGONALFACE((48,43,49,54));
#132=IFCINDEXEDPOLYGONALFACE((43,44,50,49));
#133=IFCINDEXEDPOLYGONALFACE((44,45,51,50));
#134=IFCINDEXEDPOLYGONALFACE((45,46,52,51));
#135=IFCINDEXEDPOLYGONALFACE((46,47,53,52));
#136=IFCINDEXEDPOLYGONALFACE((47,48,54,53));
#137=IFCINDEXEDPOLYGONALFACE((49,50,51,52,53,54));
#138=IFCPOLYGONALFACESET(#87,$,(#88,#89,#90,#91,#92,#93,#94,#95,#96,#97,#98,#99,#100,#101,#102,#103,#104,#105,#106,#107,#108,#109,#110,#111,#112,#113,#114,#115,#116,#117,#118,#119,#120,#121,#122,#123,#124,#125,#126,#127,#128,#129,#130,#131,#132,#133,#134,#135,#136,#137),$);
#139=IFCSHAPEREPRESENTATION(#16,'Body','Curve2D',(#86));
#140=IFCSHAPEREPRESENTATION(#15,'Body','Tessellation',(#138));
#141=IFCGEOGRAPHICELEMENTTYPE('1up5YjiS1CSvOopNo9qhzA',$,'Cone Tree (15m)',$,$,$,(#146,#151),$,$,.NOTDEFINED.);
#142=IFCCARTESIANPOINT((0.,0.,0.));
#143=IFCDIRECTION((1.,0.,0.));
#144=IFCDIRECTION((0.,0.,1.));
#145=IFCAXIS2PLACEMENT3D(#142,#144,#143);
#146=IFCREPRESENTATIONMAP(#145,#140);
#147=IFCCARTESIANPOINT((0.,0.,0.));
#148=IFCDIRECTION((1.,0.,0.));
#149=IFCDIRECTION((0.,0.,1.));
#150=IFCAXIS2PLACEMENT3D(#147,#149,#148);
#151=IFCREPRESENTATIONMAP(#150,#139);
#152=IFCCARTESIANPOINTLIST2D(((-498.579620361328,735.001647949219),(-644.625122070312,558.698791503906),(-614.925903320312,498.60498046875),(-740.696044921875,396.37109375),(-782.288513183594,134.234802246094),(-551.41796875,97.9387969970703),(-788.211303710938,76.7640991210938),(-816.569641113281,-242.43864440918),(-685.161376953125,-297.968902587891),(-703.043884277344,-397.560852050781),(-586.489440917969,-569.15283203125),(-528.322204589844,-575.251037597656),(-366.814422607422,-750.108703613281),(-257.688507080078,-767.03466796875),(-273.860229492188,-683.197692871094),(-185.236541748047,-810.123107910156),(313.798278808594,-745.734313964844),(262.069519042969,-582.365661621094),(406.147064208984,-688.067260742188),(778.871398925781,-313.5146484375),(703.457824707031,-249.706893920898),(805.686340332031,-208.338500976562),(821.777282714844,71.8945388793945),(639.965148925781,44.3083839416504),(773.707153320312,162.339202880859),(703.734558105469,469.384552001953),(564.003479003906,549.215209960938),(489.063201904297,694.462707519531),(337.980773925781,746.2314453125),(159.236328125,638.655029296875),(287.592864990234,774.293273925781),(-11.1607036590576,832.838073730469),(-158.524017333984,800.207580566406),(-271.424072265625,751.009338378906)));
#153=IFCINDEXEDPOLYCURVE(#152,(IFCLINEINDEX((1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,1))),$);
#154=IFCCARTESIANPOINTLIST3D(((110.,0.,0.),(55.,95.2627944162882,0.),(-55.,95.2627944162883,0.),(-110.,1.34711147906209E-14,0.),(-55.,-95.2627944162882,0.),(55.,-95.2627944162882,0.),(110.,0.,320.),(55.,95.2627944162882,320.),(-55.,95.2627944162883,320.),(-110.,1.34711147906209E-14,320.),(-55.,-95.2627944162882,320.),(55.,-95.2627944162882,320.),(864.280518937983,-14.2221890649771,1004.18968879832),(366.2196464279,798.785843488237,1053.28920307812),(-394.305493219765,668.167504520537,992.705300499414),(-884.445437675581,-50.0006646627198,1079.13481040061),(-325.688601492109,-827.210316726686,1060.60363602869),(445.638122193637,-759.800396032949,945.295072152772),(771.659636093658,-8.63376978833887,1694.31515423778),(400.702597343804,564.567676396979,1710.71041993166),(-271.850683531095,731.84967953552,1679.8082212522),(-827.814540093743,-99.1889711683184,1583.93736255268),(-280.132879054167,-677.399935048722,1630.37197747494),(447.972582241171,-675.099286463246,1666.93929394534),(610.539524858158,-87.0011647746299,2342.62353085467),(380.480246199047,471.106285369781,2270.57407945149),(-329.092625123698,447.207475504026,2325.0349854376),(-559.736760447407,31.5781877518219,2332.41103284484),(-240.641518511947,-609.88487469802,2339.16138108691),(286.501630216284,-519.561592269572,2248.29742609317),(565.102540214566,-80.6788333631295,2927.55846344436),(316.489950404951,531.211740774762,2980.93201479601),(-272.18383365034,390.769331158215,2949.22639267883),(-486.549445805167,-64.3079178558549,2918.18221515187),(-263.853028263793,-508.685394614137,3024.03068052896),(319.211134872299,-386.170678054984,2990.31239233058),(366.18091791083,-13.0578902011131,3563.85885783901),(240.847973491261,309.854854694926,3626.74179040925),(-282.348326849291,333.134595368031,3679.42693631726),(-418.54927829809,73.9091090794806,3697.52778620685),(-250.051684971173,-338.452640392509,3744.96390638646),(202.355272197616,-323.102436420037,3575.41186761945),(281.210556637686,-7.71174744071313,4311.65454744235),(125.753738295971,262.36087427247,4339.1619811132),(-143.687514908479,178.533152512032,4319.69793491929),(-189.597693621145,-16.9652613564334,4238.49213738614),(-241.100581330193,-238.290174142348,4241.54315979299),(60.215347735105,-323.622581880197,4322.24423749834),(0.05,0.,5000.),(0.025,0.0433012701892219,5000.),(-0.025,0.0433012701892219,5000.),(-0.05,6.12323399573677E-18,5000.),(-0.025,-0.0433012701892219,5000.),(0.025,-0.0433012701892219,5000.)));
#155=IFCINDEXEDPOLYGONALFACE((1,2,3,4,5,6));
#156=IFCINDEXEDPOLYGONALFACE((6,1,7,12));
#157=IFCINDEXEDPOLYGONALFACE((1,2,8,7));
#158=IFCINDEXEDPOLYGONALFACE((2,3,9,8));
#159=IFCINDEXEDPOLYGONALFACE((3,4,10,9));
#160=IFCINDEXEDPOLYGONALFACE((4,5,11,10));
#161=IFCINDEXEDPOLYGONALFACE((5,6,12,11));
#162=IFCINDEXEDPOLYGONALFACE((12,7,13,18));
#163=IFCINDEXEDPOLYGONALFACE((7,8,14,13));
#164=IFCINDEXEDPOLYGONALFACE((8,9,15,14));
#165=IFCINDEXEDPOLYGONALFACE((9,10,16,15));
#166=IFCINDEXEDPOLYGONALFACE((10,11,17,16));
#167=IFCINDEXEDPOLYGONALFACE((11,12,18,17));
#168=IFCINDEXEDPOLYGONALFACE((18,13,19,24));
#169=IFCINDEXEDPOLYGONALFACE((13,14,20,19));
#170=IFCINDEXEDPOLYGONALFACE((14,15,21,20));
#171=IFCINDEXEDPOLYGONALFACE((15,16,22,21));
#172=IFCINDEXEDPOLYGONALFACE((16,17,23,22));
#173=IFCINDEXEDPOLYGONALFACE((17,18,24,23));
#174=IFCINDEXEDPOLYGONALFACE((24,19,25,30));
#175=IFCINDEXEDPOLYGONALFACE((19,20,26,25));
#176=IFCINDEXEDPOLYGONALFACE((20,21,27,26));
#177=IFCINDEXEDPOLYGONALFACE((21,22,28,27));
#178=IFCINDEXEDPOLYGONALFACE((22,23,29,28));
#179=IFCINDEXEDPOLYGONALFACE((23,24,30,29));
#180=IFCINDEXEDPOLYGONALFACE((30,25,31,36));
#181=IFCINDEXEDPOLYGONALFACE((25,26,32,31));
#182=IFCINDEXEDPOLYGONALFACE((26,27,33,32));
#183=IFCINDEXEDPOLYGONALFACE((27,28,34,33));
#184=IFCINDEXEDPOLYGONALFACE((28,29,35,34));
#185=IFCINDEXEDPOLYGONALFACE((29,30,36,35));
#186=IFCINDEXEDPOLYGONALFACE((36,31,37,42));
#187=IFCINDEXEDPOLYGONALFACE((31,32,38,37));
#188=IFCINDEXEDPOLYGONALFACE((32,33,39,38));
#189=IFCINDEXEDPOLYGONALFACE((33,34,40,39));
#190=IFCINDEXEDPOLYGONALFACE((34,35,41,40));
#191=IFCINDEXEDPOLYGONALFACE((35,36,42,41));
#192=IFCINDEXEDPOLYGONALFACE((42,37,43,48));
#193=IFCINDEXEDPOLYGONALFACE((37,38,44,43));
#194=IFCINDEXEDPOLYGONALFACE((38,39,45,44));
#195=IFCINDEXEDPOLYGONALFACE((39,40,46,45));
#196=IFCINDEXEDPOLYGONALFACE((40,41,47,46));
#197=IFCINDEXEDPOLYGONALFACE((41,42,48,47));
#198=IFCINDEXEDPOLYGONALFACE((48,43,49,54));
#199=IFCINDEXEDPOLYGONALFACE((43,44,50,49));
#200=IFCINDEXEDPOLYGONALFACE((44,45,51,50));
#201=IFCINDEXEDPOLYGONALFACE((45,46,52,51));
#202=IFCINDEXEDPOLYGONALFACE((46,47,53,52));
#203=IFCINDEXEDPOLYGONALFACE((47,48,54,53));
#204=IFCINDEXEDPOLYGONALFACE((49,50,51,52,53,54));
#205=IFCPOLYGONALFACESET(#154,$,(#155,#156,#157,#158,#159,#160,#161,#162,#163,#164,#165,#166,#167,#168,#169,#170,#171,#172,#173,#174,#175,#176,#177,#178,#179,#180,#181,#182,#183,#184,#185,#186,#187,#188,#189,#190,#191,#192,#193,#194,#195,#196,#197,#198,#199,#200,#201,#202,#203,#204),$);
#206=IFCSHAPEREPRESENTATION(#16,'Body','Curve2D',(#153));
#207=IFCSHAPEREPRESENTATION(#15,'Body','Tessellation',(#205));
#208=IFCGEOGRAPHICELEMENTTYPE('1cYi681Jz1UQcLu0VCPumj',$,'Cone Tree (5m)',$,$,$,(#213,#218),$,$,.NOTDEFINED.);
#209=IFCCARTESIANPOINT((0.,0.,0.));
#210=IFCDIRECTION((1.,0.,0.));
#211=IFCDIRECTION((0.,0.,1.));
#212=IFCAXIS2PLACEMENT3D(#209,#211,#210);
#213=IFCREPRESENTATIONMAP(#212,#207);
#214=IFCCARTESIANPOINT((0.,0.,0.));
#215=IFCDIRECTION((1.,0.,0.));
#216=IFCDIRECTION((0.,0.,1.));
#217=IFCAXIS2PLACEMENT3D(#214,#216,#215);
#218=IFCREPRESENTATIONMAP(#217,#206);
#219=IFCCARTESIANPOINTLIST2D(((-117.312850952148,172.941558837891),(-151.676498413086,131.458541870117),(-144.688446044922,117.318817138672),(-174.28141784668,93.263786315918),(-184.067886352539,31.584659576416),(-129.745407104492,23.0444221496582),(-185.461486816406,18.062141418457),(-192.134033203125,-57.0443878173828),(-161.214447021484,-70.1103286743164),(-165.422088623047,-93.5437316894531),(-137.997512817383,-133.918304443359),(-124.311111450195,-135.353179931641),(-86.3092803955078,-176.496170043945),(-60.6325874328613,-180.478744506836),(-64.4376983642578,-160.752395629883),(-43.5850677490234,-190.617202758789),(73.8348922729492,-175.466888427734),(61.6634140014648,-137.027221679688),(95.5640106201172,-161.898178100586),(183.263854980469,-73.7681503295898),(165.519485473633,-58.7545623779297),(189.573257446289,-49.020824432373),(193.359359741211,16.9163608551025),(150.580032348633,10.4255018234253),(182.048736572266,38.1974563598633),(165.584594726562,110.443428039551),(132.706695556641,129.227111816406),(115.073692321777,163.402984619141),(79.5248870849609,175.583862304688),(37.4673728942871,150.271774291992),(67.6689071655273,182.186645507812),(-2.62604808807373,195.961898803711),(-37.2997703552246,188.284133911133),(-63.8644866943359,176.708084106445)));
#220=IFCINDEXEDPOLYCURVE(#219,(IFCLINEINDEX((1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,1))),$);
#221=IFCCARTESIANPOINTLIST3D(((0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(189.142441515039,-1.13777512519817,58.3923179610087),(89.297571714232,167.825915933192,62.3202791033923),(-91.5444394575811,157.376448815776,57.4735668970959),(-190.755635014047,-4.00005317301757,64.3879276891917),(-86.0550881193688,-170.099873792268,62.9054337394378),(95.651049775491,-164.707080136769,53.6807486293646),(196.799437554159,-0.69070158306711,117.259498053308),(99.5895411208377,162.136578649577,118.571119308818),(-89.2813880158209,175.51913890066,116.098943414462),(-201.291829874166,-7.93511769346546,108.4292747185),(-89.9439636576667,-171.163159341716,112.144043912281),(103.371139912627,-170.979107454878,115.069429229913),(198.976495321986,-6.96009318197039,172.781311039802),(105.50508636259,167.707783451086,167.017354927548),(-101.394076676563,165.795878661826,171.374227406436),(-194.912274169126,2.52625502014577,171.964311199016),(-94.3179881476225,-178.810070597345,172.504339058381),(97.9867970839694,-171.58420800307,165.235222658882),(198.408203217165,-6.45430666905036,223.233248504121),(101.919196032396,175.172031121757,227.503132612252),(-98.3747066920271,163.936638352433,224.966682842878),(-192.123955664413,-5.14463342846837,222.483148640721),(-97.7082422611035,-173.369923428907,230.951025870888),(102.136890789784,-163.568746104175,228.253562815018),(185.561140099533,-1.04463121608905,277.794422912835),(97.4011712126342,160.119291473642,282.825057518454),(-100.721199481277,161.981670727491,287.039869191095),(-189.750608930514,5.91272872635847,288.487937182262),(-98.1374681310272,-162.407114329449,292.282826796631),(94.3217551091426,-161.179098011651,278.71866369527),(181.830177864348,-0.61693979525705,341.275220938245),(89.7269657303444,158.975584278118,343.475815631913),(-91.161667859345,152.269366537283,341.9186919364),(-174.501148823025,-1.35722090851465,335.422228133748),(-98.9547131730822,-157.049928267708,335.666309926296),(84.4838944854751,-163.876520886736,342.122396142724),(0.05,0.,400.),(0.025,0.0433012701892219,400.),(-0.025,0.0433012701892219,400.),(-0.05,6.12323399573677E-18,400.),(-0.025,-0.0433012701892219,400.),(0.025,-0.0433012701892219,400.)));
#222=IFCINDEXEDPOLYGONALFACE((1,2,3,4,5,6));
#223=IFCINDEXEDPOLYGONALFACE((6,1,7,12));
#224=IFCINDEXEDPOLYGONALFACE((1,2,8,7));
#225=IFCINDEXEDPOLYGONALFACE((2,3,9,8));
#226=IFCINDEXEDPOLYGONALFACE((3,4,10,9));
#227=IFCINDEXEDPOLYGONALFACE((4,5,11,10));
#228=IFCINDEXEDPOLYGONALFACE((5,6,12,11));
#229=IFCINDEXEDPOLYGONALFACE((12,7,13,18));
#230=IFCINDEXEDPOLYGONALFACE((7,8,14,13));
#231=IFCINDEXEDPOLYGONALFACE((8,9,15,14));
#232=IFCINDEXEDPOLYGONALFACE((9,10,16,15));
#233=IFCINDEXEDPOLYGONALFACE((10,11,17,16));
#234=IFCINDEXEDPOLYGONALFACE((11,12,18,17));
#235=IFCINDEXEDPOLYGONALFACE((18,13,19,24));
#236=IFCINDEXEDPOLYGONALFACE((13,14,20,19));
#237=IFCINDEXEDPOLYGONALFACE((14,15,21,20));
#238=IFCINDEXEDPOLYGONALFACE((15,16,22,21));
#239=IFCINDEXEDPOLYGONALFACE((16,17,23,22));
#240=IFCINDEXEDPOLYGONALFACE((17,18,24,23));
#241=IFCINDEXEDPOLYGONALFACE((24,19,25,30));
#242=IFCINDEXEDPOLYGONALFACE((19,20,26,25));
#243=IFCINDEXEDPOLYGONALFACE((20,21,27,26));
#244=IFCINDEXEDPOLYGONALFACE((21,22,28,27));
#245=IFCINDEXEDPOLYGONALFACE((22,23,29,28));
#246=IFCINDEXEDPOLYGONALFACE((23,24,30,29));
#247=IFCINDEXEDPOLYGONALFACE((30,25,31,36));
#248=IFCINDEXEDPOLYGONALFACE((25,26,32,31));
#249=IFCINDEXEDPOLYGONALFACE((26,27,33,32));
#250=IFCINDEXEDPOLYGONALFACE((27,28,34,33));
#251=IFCINDEXEDPOLYGONALFACE((28,29,35,34));
#252=IFCINDEXEDPOLYGONALFACE((29,30,36,35));
#253=IFCINDEXEDPOLYGONALFACE((36,31,37,42));
#254=IFCINDEXEDPOLYGONALFACE((31,32,38,37));
#255=IFCINDEXEDPOLYGONALFACE((32,33,39,38));
#256=IFCINDEXEDPOLYGONALFACE((33,34,40,39));
#257=IFCINDEXEDPOLYGONALFACE((34,35,41,40));
#258=IFCINDEXEDPOLYGONALFACE((35,36,42,41));
#259=IFCINDEXEDPOLYGONALFACE((42,37,43,48));
#260=IFCINDEXEDPOLYGONALFACE((37,38,44,43));
#261=IFCINDEXEDPOLYGONALFACE((38,39,45,44));
#262=IFCINDEXEDPOLYGONALFACE((39,40,46,45));
#263=IFCINDEXEDPOLYGONALFACE((40,41,47,46));
#264=IFCINDEXEDPOLYGONALFACE((41,42,48,47));
#265=IFCINDEXEDPOLYGONALFACE((48,43,49,54));
#266=IFCINDEXEDPOLYGONALFACE((43,44,50,49));
#267=IFCINDEXEDPOLYGONALFACE((44,45,51,50));
#268=IFCINDEXEDPOLYGONALFACE((45,46,52,51));
#269=IFCINDEXEDPOLYGONALFACE((46,47,53,52));
#270=IFCINDEXEDPOLYGONALFACE((47,48,54,53));
#271=IFCINDEXEDPOLYGONALFACE((49,50,51,52,53,54));
#272=IFCPOLYGONALFACESET(#221,$,(#222,#223,#224,#225,#226,#227,#228,#229,#230,#231,#232,#233,#234,#235,#236,#237,#238,#239,#240,#241,#242,#243,#244,#245,#246,#247,#248,#249,#250,#251,#252,#253,#254,#255,#256,#257,#258,#259,#260,#261,#262,#263,#264,#265,#266,#267,#268,#269,#270,#271),$);
#273=IFCSHAPEREPRESENTATION(#16,'Body','Curve2D',(#220));
#274=IFCSHAPEREPRESENTATION(#15,'Body','Tessellation',(#272));
#275=IFCGEOGRAPHICELEMENTTYPE('1mO0fZa2b4sflxAiM3QTIb',$,'Hedge Small (0.4m)',$,$,$,(#280,#285),$,$,.NOTDEFINED.);
#276=IFCCARTESIANPOINT((0.,0.,0.));
#277=IFCDIRECTION((1.,0.,0.));
#278=IFCDIRECTION((0.,0.,1.));
#279=IFCAXIS2PLACEMENT3D(#276,#278,#277);
#280=IFCREPRESENTATIONMAP(#279,#274);
#281=IFCCARTESIANPOINT((0.,0.,0.));
#282=IFCDIRECTION((1.,0.,0.));
#283=IFCDIRECTION((0.,0.,1.));
#284=IFCAXIS2PLACEMENT3D(#281,#283,#282);
#285=IFCREPRESENTATIONMAP(#284,#273);
#286=IFCCARTESIANPOINTLIST2D(((-234.625701904297,345.883117675781),(-303.352996826172,262.917083740234),(-289.376892089844,234.637634277344),(-348.562835693359,186.527572631836),(-368.135772705078,63.169319152832),(-259.490814208984,46.0888442993164),(-370.922973632812,36.1242828369141),(-384.26806640625,-114.088775634766),(-322.428894042969,-140.220657348633),(-330.844177246094,-187.087463378906),(-275.995025634766,-267.836608886719),(-248.622222900391,-270.706359863281),(-172.618560791016,-352.992340087891),(-121.265174865723,-360.957489013672),(-128.875396728516,-321.504791259766),(-87.1701354980469,-381.234405517578),(147.669784545898,-350.933776855469),(123.32682800293,-274.054443359375),(191.128021240234,-323.796356201172),(366.527709960938,-147.53630065918),(331.038970947266,-117.509124755859),(379.146514892578,-98.0416488647461),(386.718719482422,33.8327217102051),(301.160064697266,20.8510036468506),(364.097473144531,76.3949127197266),(331.169189453125,220.886856079102),(265.413391113281,258.454223632812),(230.147384643555,326.805969238281),(159.049774169922,351.167724609375),(74.9347457885742,300.543548583984),(135.337814331055,364.373291015625),(-5.25209617614746,391.923797607422),(-74.5995407104492,376.568267822266),(-127.728973388672,353.416168212891)));
#287=IFCINDEXEDPOLYCURVE(#286,(IFCLINEINDEX((1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,1))),$);
#288=IFCCARTESIANPOINTLIST3D(((0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(378.284883030077,-2.27555025039633,116.784635922017),(178.595143428464,335.651831866383,124.640558206785),(-183.088878915162,314.752897631551,114.947133794192),(-381.511270028093,-8.00010634603514,128.775855378383),(-172.110176238738,-340.199747584535,125.810867478876),(191.302099550982,-329.414160273537,107.361497258729),(393.598875108319,-1.38140316613422,234.518996106616),(199.179082241675,324.273157299153,237.142238617636),(-178.562776031642,351.03827780132,232.197886828923),(-402.583659748332,-15.8702353869309,216.858549437001),(-179.887927315333,-342.326318683432,224.288087824562),(206.742279825254,-341.958214909756,230.138858459826),(397.952990643972,-13.9201863639408,345.562622079605),(211.010172725181,335.415566902172,334.034709855096),(-202.788153353125,331.591757323652,342.748454812872),(-389.824548338252,5.05251004029154,343.928622398032),(-188.635976295245,-357.620141194691,345.008678116763),(195.973594167939,-343.168416006139,330.470445317765),(396.81640643433,-12.9086133381007,446.466497008241),(203.838392064792,350.344062243514,455.006265224505),(-196.749413384054,327.873276704866,449.933365685756),(-384.247911328827,-10.2892668569367,444.966297281442),(-195.416484522207,-346.739846857814,461.902051741776),(204.273781579568,-327.137492208349,456.507125630036),(371.122280199066,-2.0892624321781,555.588845825671),(194.802342425268,320.238582947285,565.650115036909),(-201.442398962553,323.963341454982,574.07973838219),(-379.501217861028,11.8254574527169,576.975874364524),(-196.274936262054,-324.814228658898,584.565653593263),(188.643510218285,-322.358196023303,557.43732739054),(363.660355728696,-1.2338795905141,682.550441876491),(179.453931460689,317.951168556236,686.951631263826),(-182.32333571869,304.538733074566,683.837383872801),(-349.00229764605,-2.71444181702931,670.844456267496),(-197.909426346164,-314.099856535417,671.332619852593),(168.96778897095,-327.753041773473,684.244792285448),(0.05,0.,800.),(0.025,0.0433012701892219,800.),(-0.025,0.0433012701892219,800.),(-0.05,6.12323399573677E-18,800.),(-0.025,-0.0433012701892219,800.),(0.025,-0.0433012701892219,800.)));
#289=IFCINDEXEDPOLYGONALFACE((1,2,3,4,5,6));
#290=IFCINDEXEDPOLYGONALFACE((6,1,7,12));
#291=IFCINDEXEDPOLYGONALFACE((1,2,8,7));
#292=IFCINDEXEDPOLYGONALFACE((2,3,9,8));
#293=IFCINDEXEDPOLYGONALFACE((3,4,10,9));
#294=IFCINDEXEDPOLYGONALFACE((4,5,11,10));
#295=IFCINDEXEDPOLYGONALFACE((5,6,12,11));
#296=IFCINDEXEDPOLYGONALFACE((12,7,13,18));
#297=IFCINDEXEDPOLYGONALFACE((7,8,14,13));
#298=IFCINDEXEDPOLYGONALFACE((8,9,15,14));
#299=IFCINDEXEDPOLYGONALFACE((9,10,16,15));
#300=IFCINDEXEDPOLYGONALFACE((10,11,17,16));
#301=IFCINDEXEDPOLYGONALFACE((11,12,18,17));
#302=IFCINDEXEDPOLYGONALFACE((18,13,19,24));
#303=IFCINDEXEDPOLYGONALFACE((13,14,20,19));
#304=IFCINDEXEDPOLYGONALFACE((14,15,21,20));
#305=IFCINDEXEDPOLYGONALFACE((15,16,22,21));
#306=IFCINDEXEDPOLYGONALFACE((16,17,23,22));
#307=IFCINDEXEDPOLYGONALFACE((17,18,24,23));
#308=IFCINDEXEDPOLYGONALFACE((24,19,25,30));
#309=IFCINDEXEDPOLYGONALFACE((19,20,26,25));
#310=IFCINDEXEDPOLYGONALFACE((20,21,27,26));
#311=IFCINDEXEDPOLYGONALFACE((21,22,28,27));
#312=IFCINDEXEDPOLYGONALFACE((22,23,29,28));
#313=IFCINDEXEDPOLYGONALFACE((23,24,30,29));
#314=IFCINDEXEDPOLYGONALFACE((30,25,31,36));
#315=IFCINDEXEDPOLYGONALFACE((25,26,32,31));
#316=IFCINDEXEDPOLYGONALFACE((26,27,33,32));
#317=IFCINDEXEDPOLYGONALFACE((27,28,34,33));
#318=IFCINDEXEDPOLYGONALFACE((28,29,35,34));
#319=IFCINDEXEDPOLYGONALFACE((29,30,36,35));
#320=IFCINDEXEDPOLYGONALFACE((36,31,37,42));
#321=IFCINDEXEDPOLYGONALFACE((31,32,38,37));
#322=IFCINDEXEDPOLYGONALFACE((32,33,39,38));
#323=IFCINDEXEDPOLYGONALFACE((33,34,40,39));
#324=IFCINDEXEDPOLYGONALFACE((34,35,41,40));
#325=IFCINDEXEDPOLYGONALFACE((35,36,42,41));
#326=IFCINDEXEDPOLYGONALFACE((42,37,43,48));
#327=IFCINDEXEDPOLYGONALFACE((37,38,44,43));
#328=IFCINDEXEDPOLYGONALFACE((38,39,45,44));
#329=IFCINDEXEDPOLYGONALFACE((39,40,46,45));
#330=IFCINDEXEDPOLYGONALFACE((40,41,47,46));
#331=IFCINDEXEDPOLYGONALFACE((41,42,48,47));
#332=IFCINDEXEDPOLYGONALFACE((48,43,49,54));
#333=IFCINDEXEDPOLYGONALFACE((43,44,50,49));
#334=IFCINDEXEDPOLYGONALFACE((44,45,51,50));
#335=IFCINDEXEDPOLYGONALFACE((45,46,52,51));
#336=IFCINDEXEDPOLYGONALFACE((46,47,53,52));
#337=IFCINDEXEDPOLYGONALFACE((47,48,54,53));
#338=IFCINDEXEDPOLYGONALFACE((49,50,51,52,53,54));
#339=IFCPOLYGONALFACESET(#288,$,(#289,#290,#291,#292,#293,#294,#295,#296,#297,#298,#299,#300,#301,#302,#303,#304,#305,#306,#307,#308,#309,#310,#311,#312,#313,#314,#315,#316,#317,#318,#319,#320,#321,#322,#323,#324,#325,#326,#327,#328,#329,#330,#331,#332,#333,#334,#335,#336,#337,#338),$);
#340=IFCSHAPEREPRESENTATION(#16,'Body','Curve2D',(#287));
#341=IFCSHAPEREPRESENTATION(#15,'Body','Tessellation',(#339));
#342=IFCGEOGRAPHICELEMENTTYPE('1esPZTKFXE3Af39$mWJyUW',$,'Hedge Medium (0.8m)',$,$,$,(#347,#352),$,$,.NOTDEFINED.);
#343=IFCCARTESIANPOINT((0.,0.,0.));
#344=IFCDIRECTION((1.,0.,0.));
#345=IFCDIRECTION((0.,0.,1.));
#346=IFCAXIS2PLACEMENT3D(#343,#345,#344);
#347=IFCREPRESENTATIONMAP(#346,#341);
#348=IFCCARTESIANPOINT((0.,0.,0.));
#349=IFCDIRECTION((1.,0.,0.));
#350=IFCDIRECTION((0.,0.,1.));
#351=IFCAXIS2PLACEMENT3D(#348,#350,#349);
#352=IFCREPRESENTATIONMAP(#351,#340);
#353=IFCCARTESIANPOINTLIST2D(((-527.907836914062,778.237060546875),(-682.544250488281,591.5634765625),(-651.098022460938,527.934692382812),(-784.266357421875,419.687042236328),(-828.305480957031,142.130966186523),(-583.854309082031,103.699897766113),(-834.57666015625,81.2796401977539),(-864.603149414062,-256.699737548828),(-725.465026855469,-315.496459960938),(-744.3994140625,-420.946807861328),(-620.988830566406,-602.632385253906),(-559.400024414062,-609.08935546875),(-388.391754150391,-794.232788085938),(-272.846649169922,-812.154357910156),(-289.969635009766,-723.3857421875),(-196.1328125,-857.777404785156),(332.257019042969,-789.601013183594),(277.4853515625,-616.622497558594),(430.038055419922,-728.541809082031),(824.687377929688,-331.956665039062),(744.837707519531,-264.395538330078),(853.079650878906,-220.593704223633),(870.117126464844,76.1236267089844),(677.610168457031,46.9147605895996),(819.219299316406,171.888565063477),(745.130676269531,496.995422363281),(597.18017578125,581.522033691406),(517.831604003906,735.313415527344),(357.861999511719,790.127380371094),(168.603179931641,676.222961425781),(304.510101318359,819.839904785156),(-11.8172159194946,881.828552246094),(-167.848968505859,847.278625488281),(-287.390197753906,795.186340332031)));
#354=IFCINDEXEDPOLYCURVE(#353,(IFCLINEINDEX((1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,1))),$);
#355=IFCCARTESIANPOINTLIST3D(((0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(851.140986817674,-5.11998806339175,262.765430824539),(401.839072714044,755.216621699362,280.441255965265),(-411.949977559115,708.19401967099,258.631051036932),(-858.400357563209,-18.0002392785791,289.745674601363),(-387.24789653716,-765.449432065204,283.07445182747),(430.429723989709,-741.181860615459,241.563368832141),(885.597468993717,-3.10815712380199,527.667741239885),(448.152935043769,729.614603923094,533.570036889682),(-401.766246071194,789.836125052969,522.445245365077),(-905.813234433747,-35.7080296205946,487.931736233252),(-404.7478364595,-770.234217037722,504.648197605265),(465.170129606822,-769.40598354695,517.812431534608),(895.394228948937,-31.3204193188668,777.515899679111),(474.772888631657,754.685025529888,751.578097173967),(-456.273345044531,746.081453978216,771.184023328963),(-877.105233761067,11.368147590656,773.839400395571),(-424.430946664301,-804.645317688054,776.269525762717),(440.940586877863,-772.128936013813,743.558501964971),(892.836914477244,-29.0443800107266,1004.54961826854),(458.636382145783,788.274140047906,1023.76409675514),(-442.686180114122,737.714872585949,1012.35007279295),(-864.55780048986,-23.1508504281077,1001.17416888324),(-439.687090174966,-780.164655430081,1039.279616419),(459.616008554028,-736.059357468786,1027.14103266758),(835.025130447899,-4.70084047240072,1250.07490310776),(438.305270456854,720.536811631391,1272.71275883304),(-453.245397665745,728.917518273708,1291.67941135993),(-853.877740187312,26.6072792686131,1298.19571732018),(-441.618606589622,-730.832014482521,1315.27272058484),(424.447897991142,-725.305941052431,1254.23398662872),(818.235800389567,-2.77622907865673,1535.7384942221),(403.77134578655,715.390129251532,1545.64117034361),(-410.227505367052,685.212149417774,1538.6341137138),(-785.255169703612,-6.10749408831594,1509.40002660187),(-445.29620927887,-706.724677204688,1510.49839466833),(380.177525184638,-737.444343990313,1539.55078264226),(0.05,0.,1800.),(0.025,0.0433012701892219,1800.),(-0.025,0.0433012701892219,1800.),(-0.05,6.12323399573677E-18,1800.),(-0.025,-0.0433012701892219,1800.),(0.025,-0.0433012701892219,1800.)));
#356=IFCINDEXEDPOLYGONALFACE((1,2,3,4,5,6));
#357=IFCINDEXEDPOLYGONALFACE((6,1,7,12));
#358=IFCINDEXEDPOLYGONALFACE((1,2,8,7));
#359=IFCINDEXEDPOLYGONALFACE((2,3,9,8));
#360=IFCINDEXEDPOLYGONALFACE((3,4,10,9));
#361=IFCINDEXEDPOLYGONALFACE((4,5,11,10));
#362=IFCINDEXEDPOLYGONALFACE((5,6,12,11));
#363=IFCINDEXEDPOLYGONALFACE((12,7,13,18));
#364=IFCINDEXEDPOLYGONALFACE((7,8,14,13));
#365=IFCINDEXEDPOLYGONALFACE((8,9,15,14));
#366=IFCINDEXEDPOLYGONALFACE((9,10,16,15));
#367=IFCINDEXEDPOLYGONALFACE((10,11,17,16));
#368=IFCINDEXEDPOLYGONALFACE((11,12,18,17));
#369=IFCINDEXEDPOLYGONALFACE((18,13,19,24));
#370=IFCINDEXEDPOLYGONALFACE((13,14,20,19));
#371=IFCINDEXEDPOLYGONALFACE((14,15,21,20));
#372=IFCINDEXEDPOLYGONALFACE((15,16,22,21));
#373=IFCINDEXEDPOLYGONALFACE((16,17,23,22));
#374=IFCINDEXEDPOLYGONALFACE((17,18,24,23));
#375=IFCINDEXEDPOLYGONALFACE((24,19,25,30));
#376=IFCINDEXEDPOLYGONALFACE((19,20,26,25));
#377=IFCINDEXEDPOLYGONALFACE((20,21,27,26));
#378=IFCINDEXEDPOLYGONALFACE((21,22,28,27));
#379=IFCINDEXEDPOLYGONALFACE((22,23,29,28));
#380=IFCINDEXEDPOLYGONALFACE((23,24,30,29));
#381=IFCINDEXEDPOLYGONALFACE((30,25,31,36));
#382=IFCINDEXEDPOLYGONALFACE((25,26,32,31));
#383=IFCINDEXEDPOLYGONALFACE((26,27,33,32));
#384=IFCINDEXEDPOLYGONALFACE((27,28,34,33));
#385=IFCINDEXEDPOLYGONALFACE((28,29,35,34));
#386=IFCINDEXEDPOLYGONALFACE((29,30,36,35));
#387=IFCINDEXEDPOLYGONALFACE((36,31,37,42));
#388=IFCINDEXEDPOLYGONALFACE((31,32,38,37));
#389=IFCINDEXEDPOLYGONALFACE((32,33,39,38));
#390=IFCINDEXEDPOLYGONALFACE((33,34,40,39));
#391=IFCINDEXEDPOLYGONALFACE((34,35,41,40));
#392=IFCINDEXEDPOLYGONALFACE((35,36,42,41));
#393=IFCINDEXEDPOLYGONALFACE((42,37,43,48));
#394=IFCINDEXEDPOLYGONALFACE((37,38,44,43));
#395=IFCINDEXEDPOLYGONALFACE((38,39,45,44));
#396=IFCINDEXEDPOLYGONALFACE((39,40,46,45));
#397=IFCINDEXEDPOLYGONALFACE((40,41,47,46));
#398=IFCINDEXEDPOLYGONALFACE((41,42,48,47));
#399=IFCINDEXEDPOLYGONALFACE((48,43,49,54));
#400=IFCINDEXEDPOLYGONALFACE((43,44,50,49));
#401=IFCINDEXEDPOLYGONALFACE((44,45,51,50));
#402=IFCINDEXEDPOLYGONALFACE((45,46,52,51));
#403=IFCINDEXEDPOLYGONALFACE((46,47,53,52));
#404=IFCINDEXEDPOLYGONALFACE((47,48,54,53));
#405=IFCINDEXEDPOLYGONALFACE((49,50,51,52,53,54));
#406=IFCPOLYGONALFACESET(#355,$,(#356,#357,#358,#359,#360,#361,#362,#363,#364,#365,#366,#367,#368,#369,#370,#371,#372,#373,#374,#375,#376,#377,#378,#379,#380,#381,#382,#383,#384,#385,#386,#387,#388,#389,#390,#391,#392,#393,#394,#395,#396,#397,#398,#399,#400,#401,#402,#403,#404,#405),$);
#407=IFCSHAPEREPRESENTATION(#16,'Body','Curve2D',(#354));
#408=IFCSHAPEREPRESENTATION(#15,'Body','Tessellation',(#406));
#409=IFCGEOGRAPHICELEMENTTYPE('2ITyucrb9DnxtmPeA_FCBG',$,'Hedge Big (1.8m)',$,$,$,(#414,#419),$,$,.NOTDEFINED.);
#410=IFCCARTESIANPOINT((0.,0.,0.));
#411=IFCDIRECTION((1.,0.,0.));
#412=IFCDIRECTION((0.,0.,1.));
#413=IFCAXIS2PLACEMENT3D(#410,#412,#411);
#414=IFCREPRESENTATIONMAP(#413,#408);
#415=IFCCARTESIANPOINT((0.,0.,0.));
#416=IFCDIRECTION((1.,0.,0.));
#417=IFCDIRECTION((0.,0.,1.));
#418=IFCAXIS2PLACEMENT3D(#415,#417,#416);
#419=IFCREPRESENTATIONMAP(#418,#407);
#420=IFCCARTESIANPOINTLIST3D(((-2517.70166015625,-2646.30053710938,3901.58129882812),(-255.675521850586,-1248.60815429688,5000.),(-1133.70935058594,-349.566070556641,4688.61865234375),(0.,0.,4658.5205078125),(3472.91040039062,-1142.93579101562,3901.58129882812),(1250.74682617188,317.302124023438,5000.),(806.568603515625,-858.251220703125,4688.61865234375),(1171.36364746094,3483.57202148438,3901.58129882812),(-298.783111572266,1267.951171875,5000.),(874.774963378906,818.52783203125,4688.61865234375),(-3164.8310546875,1859.35131835938,3901.58129882812),(-1289.18603515625,-25.3927478790283,5000.),(-613.466796875,1034.14660644531,4688.61865234375)));
#421=IFCINDEXEDPOLYGONALFACE((3,2,1));
#422=IFCINDEXEDPOLYGONALFACE((3,4,2));
#423=IFCINDEXEDPOLYGONALFACE((7,6,5));
#424=IFCINDEXEDPOLYGONALFACE((7,4,6));
#425=IFCINDEXEDPOLYGONALFACE((10,9,8));
#426=IFCINDEXEDPOLYGONALFACE((10,4,9));
#427=IFCINDEXEDPOLYGONALFACE((13,12,11));
#428=IFCINDEXEDPOLYGONALFACE((13,4,12));
#429=IFCPOLYGONALFACESET(#420,$,(#421,#422,#423,#424,#425,#426,#427,#428),$);
#430=IFCCARTESIANPOINT((0.,0.));
#431=IFCAXIS2PLACEMENT2D(#430,$);
#432=IFCCIRCLE(#431,100.);
#433=IFCARBITRARYCLOSEDPROFILEDEF(.AREA.,$,#432);
#434=IFCCARTESIANPOINT((0.,0.,0.));
#435=IFCDIRECTION((0.,0.,1.));
#436=IFCDIRECTION((1.,0.,0.));
#437=IFCAXIS2PLACEMENT3D(#434,#435,#436);
#438=IFCDIRECTION((0.,0.,1.));
#439=IFCEXTRUDEDAREASOLID(#433,#437,#438,4658.5204646405);
#440=IFCCARTESIANPOINTLIST2D(((-2517.70166015625,-2646.30053710938,0.),(-255.675521850586,-1248.60815429688,0.),(-1133.70935058594,-349.566070556641,0.),(0.,0.,0.),(3472.91040039062,-1142.93579101562,0.),(1250.74682617188,317.302124023438,0.),(806.568603515625,-858.251220703125,0.),(1171.36364746094,3483.57202148438,0.),(-298.783111572266,1267.951171875,0.),(874.774963378906,818.52783203125,0.),(-3164.8310546875,1859.35131835938,0.),(-1289.18603515625,-25.3927478790283,0.),(-613.466796875,1034.14660644531,0.)));
#441=IFCINDEXEDPOLYCURVE(#440,(IFCLINEINDEX((4,12,11,13,4)),IFCLINEINDEX((4,2,1,3,4)),IFCLINEINDEX((4,6,5,7,4)),IFCLINEINDEX((4,9,8,10,4))),$);
#442=IFCSHAPEREPRESENTATION(#16,'Body','Curve2D',(#441));
#443=IFCSHAPEREPRESENTATION(#15,'Body','SweptSolid',(#429,#439));
#444=IFCGEOGRAPHICELEMENTTYPE('0If5oJIRTAxw5EHjhL6zX$',$,'Palm Tree (5m)',$,$,$,(#449,#454),$,$,.NOTDEFINED.);
#445=IFCCARTESIANPOINT((0.,0.,0.));
#446=IFCDIRECTION((1.,0.,0.));
#447=IFCDIRECTION((0.,0.,1.));
#448=IFCAXIS2PLACEMENT3D(#445,#447,#446);
#449=IFCREPRESENTATIONMAP(#448,#443);
#450=IFCCARTESIANPOINT((0.,0.,0.));
#451=IFCDIRECTION((1.,0.,0.));
#452=IFCDIRECTION((0.,0.,1.));
#453=IFCAXIS2PLACEMENT3D(#450,#452,#451);
#454=IFCREPRESENTATIONMAP(#453,#442);
#455=IFCCARTESIANPOINTLIST3D(((-5223.76025390625,-5490.57861328125,12720.986328125),(-530.478881835938,-2590.62841796875,15000.),(-2352.23486328125,-725.284240722656,14353.9423828125),(0.,0.,14291.4931640625),(7205.64013671875,-2371.37817382812,12720.986328125),(2595.06591796875,658.342590332031,15000.),(1673.47912597656,-1780.7109375,14353.9423828125),(2430.36059570312,7227.76025390625,12720.986328125),(-619.919128417969,2630.76171875,15000.),(1814.99462890625,1698.29223632812,14353.9423828125),(-6566.43310546875,3857.80639648438,12720.986328125),(-2674.81982421875,-52.6852073669434,15000.),(-1272.82897949219,2145.66088867188,14353.9423828125)));
#456=IFCINDEXEDPOLYGONALFACE((3,2,1));
#457=IFCINDEXEDPOLYGONALFACE((3,4,2));
#458=IFCINDEXEDPOLYGONALFACE((7,6,5));
#459=IFCINDEXEDPOLYGONALFACE((7,4,6));
#460=IFCINDEXEDPOLYGONALFACE((10,9,8));
#461=IFCINDEXEDPOLYGONALFACE((10,4,9));
#462=IFCINDEXEDPOLYGONALFACE((13,12,11));
#463=IFCINDEXEDPOLYGONALFACE((13,4,12));
#464=IFCPOLYGONALFACESET(#455,$,(#456,#457,#458,#459,#460,#461,#462,#463),$);
#465=IFCCARTESIANPOINT((0.,0.));
#466=IFCAXIS2PLACEMENT2D(#465,$);
#467=IFCCIRCLE(#466,200.);
#468=IFCARBITRARYCLOSEDPROFILEDEF(.AREA.,$,#467);
#469=IFCCARTESIANPOINT((0.,0.,0.));
#470=IFCDIRECTION((0.,0.,1.));
#471=IFCDIRECTION((1.,0.,0.));
#472=IFCAXIS2PLACEMENT3D(#469,#470,#471);
#473=IFCDIRECTION((0.,0.,1.));
#474=IFCEXTRUDEDAREASOLID(#468,#472,#473,14291.4938033876);
#475=IFCCARTESIANPOINTLIST2D(((-5223.76025390625,-5490.57861328125,0.),(-530.478881835938,-2590.62841796875,0.),(-2352.23486328125,-725.284240722656,0.),(0.,0.,0.),(7205.64013671875,-2371.37817382812,0.),(2595.06591796875,658.342590332031,0.),(1673.47912597656,-1780.7109375,0.),(2430.36059570312,7227.76025390625,0.),(-619.919128417969,2630.76171875,0.),(1814.99462890625,1698.29223632812,0.),(-6566.43310546875,3857.80639648438,0.),(-2674.81982421875,-52.6852073669434,0.),(-1272.82897949219,2145.66088867188,0.)));
#476=IFCINDEXEDPOLYCURVE(#475,(IFCLINEINDEX((4,12,11,13,4)),IFCLINEINDEX((4,2,1,3,4)),IFCLINEINDEX((4,6,5,7,4)),IFCLINEINDEX((4,9,8,10,4))),$);
#477=IFCSHAPEREPRESENTATION(#16,'Body','Curve2D',(#476));
#478=IFCSHAPEREPRESENTATION(#15,'Body','SweptSolid',(#464,#474));
#479=IFCGEOGRAPHICELEMENTTYPE('11RlAtH99EAwxYuNeuU3sf',$,'Palm Tree (15m)',$,$,$,(#484,#489),$,$,.NOTDEFINED.);
#480=IFCCARTESIANPOINT((0.,0.,0.));
#481=IFCDIRECTION((1.,0.,0.));
#482=IFCDIRECTION((0.,0.,1.));
#483=IFCAXIS2PLACEMENT3D(#480,#482,#481);
#484=IFCREPRESENTATIONMAP(#483,#478);
#485=IFCCARTESIANPOINT((0.,0.,0.));
#486=IFCDIRECTION((1.,0.,0.));
#487=IFCDIRECTION((0.,0.,1.));
#488=IFCAXIS2PLACEMENT3D(#485,#487,#486);
#489=IFCREPRESENTATIONMAP(#488,#477);
#490=IFCCARTESIANPOINTLIST2D(((-117.312850952148,172.941558837891),(-151.676498413086,131.458541870117),(-144.688446044922,117.318817138672),(-174.28141784668,93.263786315918),(-184.067886352539,31.584659576416),(-129.745407104492,23.0444221496582),(-185.461486816406,18.062141418457),(-192.134033203125,-57.0443878173828),(-161.214447021484,-70.1103286743164),(-165.422088623047,-93.5437316894531),(-137.997512817383,-133.918304443359),(-124.311111450195,-135.353179931641),(-86.3092803955078,-176.496170043945),(-60.6325874328613,-180.478744506836),(-64.4376983642578,-160.752395629883),(-43.5850677490234,-190.617202758789),(73.8348922729492,-175.466888427734),(61.6634140014648,-137.027221679688),(95.5640106201172,-161.898178100586),(183.263854980469,-73.7681503295898),(165.519485473633,-58.7545623779297),(189.573257446289,-49.020824432373),(193.359359741211,16.9163608551025),(150.580032348633,10.4255018234253),(182.048736572266,38.1974563598633),(165.584594726562,110.443428039551),(132.706695556641,129.227111816406),(115.073692321777,163.402984619141),(79.5248870849609,175.583862304688),(37.4673728942871,150.271774291992),(67.6689071655273,182.186645507812),(-2.62604808807373,195.961898803711),(-37.2997703552246,188.284133911133),(-63.8644866943359,176.708084106445)));
#491=IFCINDEXEDPOLYCURVE(#490,(IFCLINEINDEX((1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,1))),$);
#492=IFCCARTESIANPOINTLIST3D(((0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(117.302441515039,-1.13777512519817,58.3923179610087),(53.377571714232,105.610650925318,62.3202791033923),(-55.6244394575812,95.1611838079015,57.4735668970959),(-118.915635014047,-4.00005317301758,64.3879276891917),(-50.1350881193688,-107.884608784393,62.9054337394378),(59.731049775491,-102.491815128895,53.6807486293646),(146.906104220826,-0.69070158306711,117.259498053308),(74.642874454171,118.927684503425,118.571119308818),(-64.3347213491543,132.310244754508,116.098943414462),(-151.398496540833,-7.93511769346546,108.4292747185),(-64.9972969910001,-127.954265195564,112.144043912281),(78.4244732459604,-127.770213308726,115.069429229913),(171.029828655319,-6.96009318197039,172.781311039802),(91.5317530292571,143.505260166657,167.017354927548),(-87.4207433432292,141.593355377397,171.374227406436),(-166.965607502459,2.52625502014577,171.964311199016),(-80.3446548142891,-154.607547312916,172.504339058381),(84.0134637506361,-147.38168471864,165.235222658882),(204.408203217165,-6.45430666905036,223.233248504121),(104.919196032396,180.368183544464,227.503132612252),(-101.374706692027,169.13279077514,224.966682842878),(-198.123955664413,-5.14463342846837,222.483148640721),(-100.708242261103,-178.566075851614,230.951025870888),(105.136890789784,-168.764898526881,228.253562815018),(169.614473432866,-1.04463121608905,277.794422912835),(89.4278378793009,146.309073034627,282.825057518454),(-92.7478661479432,148.171452288475,287.039869191095),(-173.803942263847,5.91272872635847,288.487937182262),(-90.1641347976938,-148.596895890433,292.282826796631),(86.3484217758093,-147.368879572635,278.71866369527),(143.936844531015,-0.61693979525705,341.275220938245),(70.7802990636777,126.15899497738,343.475815631913),(-72.2150011926783,119.452777236545,341.9186919364),(-136.607815489692,-1.35722090851466,335.422228133748),(-80.0080465064155,-124.23333896697,335.666309926296),(65.5372278188084,-131.059931585998,342.122396142724),(0.05,0.,400.),(0.025,0.0433012701892219,400.),(-0.025,0.0433012701892219,400.),(-0.05,6.12323399573677E-18,400.),(-0.025,-0.0433012701892219,400.),(0.025,-0.0433012701892219,400.)));
#493=IFCINDEXEDPOLYGONALFACE((1,2,3,4,5,6));
#494=IFCINDEXEDPOLYGONALFACE((6,1,7,12));
#495=IFCINDEXEDPOLYGONALFACE((1,2,8,7));
#496=IFCINDEXEDPOLYGONALFACE((2,3,9,8));
#497=IFCINDEXEDPOLYGONALFACE((3,4,10,9));
#498=IFCINDEXEDPOLYGONALFACE((4,5,11,10));
#499=IFCINDEXEDPOLYGONALFACE((5,6,12,11));
#500=IFCINDEXEDPOLYGONALFACE((12,7,13,18));
#501=IFCINDEXEDPOLYGONALFACE((7,8,14,13));
#502=IFCINDEXEDPOLYGONALFACE((8,9,15,14));
#503=IFCINDEXEDPOLYGONALFACE((9,10,16,15));
#504=IFCINDEXEDPOLYGONALFACE((10,11,17,16));
#505=IFCINDEXEDPOLYGONALFACE((11,12,18,17));
#506=IFCINDEXEDPOLYGONALFACE((18,13,19,24));
#507=IFCINDEXEDPOLYGONALFACE((13,14,20,19));
#508=IFCINDEXEDPOLYGONALFACE((14,15,21,20));
#509=IFCINDEXEDPOLYGONALFACE((15,16,22,21));
#510=IFCINDEXEDPOLYGONALFACE((16,17,23,22));
#511=IFCINDEXEDPOLYGONALFACE((17,18,24,23));
#512=IFCINDEXEDPOLYGONALFACE((24,19,25,30));
#513=IFCINDEXEDPOLYGONALFACE((19,20,26,25));
#514=IFCINDEXEDPOLYGONALFACE((20,21,27,26));
#515=IFCINDEXEDPOLYGONALFACE((21,22,28,27));
#516=IFCINDEXEDPOLYGONALFACE((22,23,29,28));
#517=IFCINDEXEDPOLYGONALFACE((23,24,30,29));
#518=IFCINDEXEDPOLYGONALFACE((30,25,31,36));
#519=IFCINDEXEDPOLYGONALFACE((25,26,32,31));
#520=IFCINDEXEDPOLYGONALFACE((26,27,33,32));
#521=IFCINDEXEDPOLYGONALFACE((27,28,34,33));
#522=IFCINDEXEDPOLYGONALFACE((28,29,35,34));
#523=IFCINDEXEDPOLYGONALFACE((29,30,36,35));
#524=IFCINDEXEDPOLYGONALFACE((36,31,37,42));
#525=IFCINDEXEDPOLYGONALFACE((31,32,38,37));
#526=IFCINDEXEDPOLYGONALFACE((32,33,39,38));
#527=IFCINDEXEDPOLYGONALFACE((33,34,40,39));
#528=IFCINDEXEDPOLYGONALFACE((34,35,41,40));
#529=IFCINDEXEDPOLYGONALFACE((35,36,42,41));
#530=IFCINDEXEDPOLYGONALFACE((42,37,43,48));
#531=IFCINDEXEDPOLYGONALFACE((37,38,44,43));
#532=IFCINDEXEDPOLYGONALFACE((38,39,45,44));
#533=IFCINDEXEDPOLYGONALFACE((39,40,46,45));
#534=IFCINDEXEDPOLYGONALFACE((40,41,47,46));
#535=IFCINDEXEDPOLYGONALFACE((41,42,48,47));
#536=IFCINDEXEDPOLYGONALFACE((48,43,49,54));
#537=IFCINDEXEDPOLYGONALFACE((43,44,50,49));
#538=IFCINDEXEDPOLYGONALFACE((44,45,51,50));
#539=IFCINDEXEDPOLYGONALFACE((45,46,52,51));
#540=IFCINDEXEDPOLYGONALFACE((46,47,53,52));
#541=IFCINDEXEDPOLYGONALFACE((47,48,54,53));
#542=IFCINDEXEDPOLYGONALFACE((49,50,51,52,53,54));
#543=IFCPOLYGONALFACESET(#492,$,(#493,#494,#495,#496,#497,#498,#499,#500,#501,#502,#503,#504,#505,#506,#507,#508,#509,#510,#511,#512,#513,#514,#515,#516,#517,#518,#519,#520,#521,#522,#523,#524,#525,#526,#527,#528,#529,#530,#531,#532,#533,#534,#535,#536,#537,#538,#539,#540,#541,#542),$);
#544=IFCSHAPEREPRESENTATION(#16,'Body','Curve2D',(#491));
#545=IFCSHAPEREPRESENTATION(#15,'Body','Tessellation',(#543));
#546=IFCGEOGRAPHICELEMENTTYPE('224k6o88949Ak$WjDPqiY0',$,'Shrub Small (0.4m)',$,$,$,(#551,#556),$,$,.NOTDEFINED.);
#547=IFCCARTESIANPOINT((0.,0.,0.));
#548=IFCDIRECTION((1.,0.,0.));
#549=IFCDIRECTION((0.,0.,1.));
#550=IFCAXIS2PLACEMENT3D(#547,#549,#548);
#551=IFCREPRESENTATIONMAP(#550,#545);
#552=IFCCARTESIANPOINT((0.,0.,0.));
#553=IFCDIRECTION((1.,0.,0.));
#554=IFCDIRECTION((0.,0.,1.));
#555=IFCAXIS2PLACEMENT3D(#552,#554,#553);
#556=IFCREPRESENTATIONMAP(#555,#544);
#557=IFCCARTESIANPOINTLIST2D(((-234.625701904297,345.883117675781),(-303.352996826172,262.917083740234),(-289.376892089844,234.637634277344),(-348.562835693359,186.527572631836),(-368.135772705078,63.169319152832),(-259.490814208984,46.0888442993164),(-370.922973632812,36.1242828369141),(-384.26806640625,-114.088775634766),(-322.428894042969,-140.220657348633),(-330.844177246094,-187.087463378906),(-275.995025634766,-267.836608886719),(-248.622222900391,-270.706359863281),(-172.618560791016,-352.992340087891),(-121.265174865723,-360.957489013672),(-128.875396728516,-321.504791259766),(-87.1701354980469,-381.234405517578),(147.669784545898,-350.933776855469),(123.32682800293,-274.054443359375),(191.128021240234,-323.796356201172),(366.527709960938,-147.53630065918),(331.038970947266,-117.509124755859),(379.146514892578,-98.0416488647461),(386.718719482422,33.8327217102051),(301.160064697266,20.8510036468506),(364.097473144531,76.3949127197266),(331.169189453125,220.886856079102),(265.413391113281,258.454223632812),(230.147384643555,326.805969238281),(159.049774169922,351.167724609375),(74.9347457885742,300.543548583984),(135.337814331055,364.373291015625),(-5.25209617614746,391.923797607422),(-74.5995407104492,376.568267822266),(-127.728973388672,353.416168212891)));
#558=IFCINDEXEDPOLYCURVE(#557,(IFCLINEINDEX((1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,1))),$);
#559=IFCCARTESIANPOINTLIST3D(((0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(234.604883030077,-2.27555025039633,116.784635922017),(106.755143428464,211.221301850635,124.640558206785),(-111.248878915162,190.322367615803,114.947133794192),(-237.831270028093,-8.00010634603516,128.775855378383),(-100.270176238738,-215.769217568787,125.810867478876),(119.462099550982,-204.983630257789,107.361497258729),(293.812208441652,-1.38140316613422,234.518996106616),(149.285748908342,237.85536900685,237.142238617636),(-128.669442698309,264.620489509016,232.197886828923),(-302.796993081665,-15.8702353869309,216.858549437001),(-129.994593982,-255.908530391129,224.288087824562),(156.848946491921,-255.540426617453,230.138858459826),(342.059657310639,-13.9201863639408,345.562622079605),(183.063506058514,287.010520333314,334.034709855096),(-174.841486686458,283.186710754793,342.748454812872),(-333.931215004918,5.05251004029153,343.928622398032),(-160.689309628578,-309.215094625832,345.008678116763),(168.026927501272,-294.763369437281,330.470445317765),(408.81640643433,-12.9086133381007,446.466497008241),(209.838392064792,360.736367088927,455.006265224505),(-202.749413384054,338.26558155028,449.933365685756),(-396.247911328827,-10.2892668569367,444.966297281442),(-201.416484522207,-357.132151703227,461.902051741776),(210.273781579568,-337.529797053763,456.507125630036),(339.228946865733,-2.0892624321781,555.588845825671),(178.855675758602,292.618146069253,565.650115036909),(-185.495732295886,296.34290457695,574.07973838219),(-347.607884527694,11.8254574527169,576.975874364524),(-180.328269595388,-297.193791780866,584.565653593263),(172.696843551619,-294.737759145271,557.43732739054),(287.87368906203,-1.2338795905141,682.550441876491),(141.560598127355,252.31798995476,686.951631263826),(-144.430002385357,238.90555447309,683.837383872801),(-273.215630979383,-2.71444181702932,670.844456267496),(-160.016093012831,-248.46667793394,671.332619852593),(131.074455637617,-262.119863171996,684.244792285448),(0.05,0.,800.),(0.025,0.0433012701892219,800.),(-0.025,0.0433012701892219,800.),(-0.05,6.12323399573677E-18,800.),(-0.025,-0.0433012701892219,800.),(0.025,-0.0433012701892219,800.)));
#560=IFCINDEXEDPOLYGONALFACE((1,2,3,4,5,6));
#561=IFCINDEXEDPOLYGONALFACE((6,1,7,12));
#562=IFCINDEXEDPOLYGONALFACE((1,2,8,7));
#563=IFCINDEXEDPOLYGONALFACE((2,3,9,8));
#564=IFCINDEXEDPOLYGONALFACE((3,4,10,9));
#565=IFCINDEXEDPOLYGONALFACE((4,5,11,10));
#566=IFCINDEXEDPOLYGONALFACE((5,6,12,11));
#567=IFCINDEXEDPOLYGONALFACE((12,7,13,18));
#568=IFCINDEXEDPOLYGONALFACE((7,8,14,13));
#569=IFCINDEXEDPOLYGONALFACE((8,9,15,14));
#570=IFCINDEXEDPOLYGONALFACE((9,10,16,15));
#571=IFCINDEXEDPOLYGONALFACE((10,11,17,16));
#572=IFCINDEXEDPOLYGONALFACE((11,12,18,17));
#573=IFCINDEXEDPOLYGONALFACE((18,13,19,24));
#574=IFCINDEXEDPOLYGONALFACE((13,14,20,19));
#575=IFCINDEXEDPOLYGONALFACE((14,15,21,20));
#576=IFCINDEXEDPOLYGONALFACE((15,16,22,21));
#577=IFCINDEXEDPOLYGONALFACE((16,17,23,22));
#578=IFCINDEXEDPOLYGONALFACE((17,18,24,23));
#579=IFCINDEXEDPOLYGONALFACE((24,19,25,30));
#580=IFCINDEXEDPOLYGONALFACE((19,20,26,25));
#581=IFCINDEXEDPOLYGONALFACE((20,21,27,26));
#582=IFCINDEXEDPOLYGONALFACE((21,22,28,27));
#583=IFCINDEXEDPOLYGONALFACE((22,23,29,28));
#584=IFCINDEXEDPOLYGONALFACE((23,24,30,29));
#585=IFCINDEXEDPOLYGONALFACE((30,25,31,36));
#586=IFCINDEXEDPOLYGONALFACE((25,26,32,31));
#587=IFCINDEXEDPOLYGONALFACE((26,27,33,32));
#588=IFCINDEXEDPOLYGONALFACE((27,28,34,33));
#589=IFCINDEXEDPOLYGONALFACE((28,29,35,34));
#590=IFCINDEXEDPOLYGONALFACE((29,30,36,35));
#591=IFCINDEXEDPOLYGONALFACE((36,31,37,42));
#592=IFCINDEXEDPOLYGONALFACE((31,32,38,37));
#593=IFCINDEXEDPOLYGONALFACE((32,33,39,38));
#594=IFCINDEXEDPOLYGONALFACE((33,34,40,39));
#595=IFCINDEXEDPOLYGONALFACE((34,35,41,40));
#596=IFCINDEXEDPOLYGONALFACE((35,36,42,41));
#597=IFCINDEXEDPOLYGONALFACE((42,37,43,48));
#598=IFCINDEXEDPOLYGONALFACE((37,38,44,43));
#599=IFCINDEXEDPOLYGONALFACE((38,39,45,44));
#600=IFCINDEXEDPOLYGONALFACE((39,40,46,45));
#601=IFCINDEXEDPOLYGONALFACE((40,41,47,46));
#602=IFCINDEXEDPOLYGONALFACE((41,42,48,47));
#603=IFCINDEXEDPOLYGONALFACE((48,43,49,54));
#604=IFCINDEXEDPOLYGONALFACE((43,44,50,49));
#605=IFCINDEXEDPOLYGONALFACE((44,45,51,50));
#606=IFCINDEXEDPOLYGONALFACE((45,46,52,51));
#607=IFCINDEXEDPOLYGONALFACE((46,47,53,52));
#608=IFCINDEXEDPOLYGONALFACE((47,48,54,53));
#609=IFCINDEXEDPOLYGONALFACE((49,50,51,52,53,54));
#610=IFCPOLYGONALFACESET(#559,$,(#560,#561,#562,#563,#564,#565,#566,#567,#568,#569,#570,#571,#572,#573,#574,#575,#576,#577,#578,#579,#580,#581,#582,#583,#584,#585,#586,#587,#588,#589,#590,#591,#592,#593,#594,#595,#596,#597,#598,#599,#600,#601,#602,#603,#604,#605,#606,#607,#608,#609),$);
#611=IFCSHAPEREPRESENTATION(#16,'Body','Curve2D',(#558));
#612=IFCSHAPEREPRESENTATION(#15,'Body','Tessellation',(#610));
#613=IFCGEOGRAPHICELEMENTTYPE('0sCZ5iWS904ODo2Mqo8nHu',$,'Shrum Medium (0.8m)',$,$,$,(#618,#623),$,$,.NOTDEFINED.);
#614=IFCCARTESIANPOINT((0.,0.,0.));
#615=IFCDIRECTION((1.,0.,0.));
#616=IFCDIRECTION((0.,0.,1.));
#617=IFCAXIS2PLACEMENT3D(#614,#616,#615);
#618=IFCREPRESENTATIONMAP(#617,#612);
#619=IFCCARTESIANPOINT((0.,0.,0.));
#620=IFCDIRECTION((1.,0.,0.));
#621=IFCDIRECTION((0.,0.,1.));
#622=IFCAXIS2PLACEMENT3D(#619,#621,#620);
#623=IFCREPRESENTATIONMAP(#622,#611);
#624=IFCCARTESIANPOINTLIST2D(((-527.907836914062,778.237060546875),(-682.544250488281,591.5634765625),(-651.098022460938,527.934692382812),(-784.266357421875,419.687042236328),(-828.305480957031,142.130966186523),(-583.854309082031,103.699897766113),(-834.57666015625,81.2796401977539),(-864.603149414062,-256.699737548828),(-725.465026855469,-315.496459960938),(-744.3994140625,-420.946807861328),(-620.988830566406,-602.632385253906),(-559.400024414062,-609.08935546875),(-388.391754150391,-794.232788085938),(-272.846649169922,-812.154357910156),(-289.969635009766,-723.3857421875),(-196.1328125,-857.777404785156),(332.257019042969,-789.601013183594),(277.4853515625,-616.622497558594),(430.038055419922,-728.541809082031),(824.687377929688,-331.956665039062),(744.837707519531,-264.395538330078),(853.079650878906,-220.593704223633),(870.117126464844,76.1236267089844),(677.610168457031,46.9147605895996),(819.219299316406,171.888565063477),(745.130676269531,496.995422363281),(597.18017578125,581.522033691406),(517.831604003906,735.313415527344),(357.861999511719,790.127380371094),(168.603179931641,676.222961425781),(304.510101318359,819.839904785156),(-11.8172159194946,881.828552246094),(-167.848968505859,847.278625488281),(-287.390197753906,795.186340332031)));
#625=IFCINDEXEDPOLYCURVE(#624,(IFCLINEINDEX((1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,1))),$);
#626=IFCCARTESIANPOINTLIST3D(((0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(0.,0.,0.),(527.860986817674,-5.11998806339175,262.765430824539),(240.199072714044,475.247929163929,280.441255965265),(-250.309977559115,428.225327135557,258.631051036932),(-535.120357563209,-18.0002392785791,289.745674601363),(-225.607896537159,-485.480739529771,283.07445182747),(268.789723989709,-461.213168080025,241.563368832141),(661.077468993717,-3.10815712380199,527.667741239885),(335.892935043769,535.174580265412,533.570036889682),(-289.506246071194,595.396101395287,522.445245365077),(-681.293234433747,-35.7080296205946,487.931736233252),(-292.4878364595,-575.794193380039,504.648197605265),(352.910129606822,-574.965959889268,517.812431534608),(769.634228948937,-31.3204193188668,777.515899679111),(411.892888631657,645.773670749957,751.578097173967),(-393.393345044531,637.170099198285,771.184023328963),(-751.345233761067,11.3681475906559,773.839400395571),(-361.550946664301,-695.733962908123,776.269525762717),(378.060586877863,-663.217581233882,743.558501964971),(919.836914477244,-29.0443800107266,1004.54961826854),(472.136382145783,811.656825950086,1023.76409675514),(-456.186180114122,761.097558488129,1012.35007279295),(-891.55780048986,-23.1508504281077,1001.17416888324),(-453.187090174966,-803.547341332261,1039.279616419),(473.116008554028,-759.442043370966,1027.14103266758),(763.265130447899,-4.70084047240072,1250.07490310776),(402.425270456854,658.39082865582,1272.71275883304),(-417.365397665745,666.771535298137,1291.67941135993),(-782.117740187312,26.6072792686131,1298.19571732018),(-405.738606589622,-668.686031506949,1315.27272058484),(388.567897991142,-663.159958076859,1254.23398662872),(647.715800389567,-2.77622907865673,1535.7384942221),(318.51134578655,567.715477398209,1545.64117034361),(-324.967505367052,537.537497564452,1538.6341137138),(-614.735169703612,-6.10749408831597,1509.40002660187),(-360.03620927887,-559.050025351365,1510.49839466833),(294.917525184638,-589.769692136991,1539.55078264226),(0.05,0.,1800.),(0.025,0.0433012701892219,1800.),(-0.025,0.0433012701892219,1800.),(-0.05,6.12323399573677E-18,1800.),(-0.025,-0.0433012701892219,1800.),(0.025,-0.0433012701892219,1800.)));
#627=IFCINDEXEDPOLYGONALFACE((1,2,3,4,5,6));
#628=IFCINDEXEDPOLYGONALFACE((6,1,7,12));
#629=IFCINDEXEDPOLYGONALFACE((1,2,8,7));
#630=IFCINDEXEDPOLYGONALFACE((2,3,9,8));
#631=IFCINDEXEDPOLYGONALFACE((3,4,10,9));
#632=IFCINDEXEDPOLYGONALFACE((4,5,11,10));
#633=IFCINDEXEDPOLYGONALFACE((5,6,12,11));
#634=IFCINDEXEDPOLYGONALFACE((12,7,13,18));
#635=IFCINDEXEDPOLYGONALFACE((7,8,14,13));
#636=IFCINDEXEDPOLYGONALFACE((8,9,15,14));
#637=IFCINDEXEDPOLYGONALFACE((9,10,16,15));
#638=IFCINDEXEDPOLYGONALFACE((10,11,17,16));
#639=IFCINDEXEDPOLYGONALFACE((11,12,18,17));
#640=IFCINDEXEDPOLYGONALFACE((18,13,19,24));
#641=IFCINDEXEDPOLYGONALFACE((13,14,20,19));
#642=IFCINDEXEDPOLYGONALFACE((14,15,21,20));
#643=IFCINDEXEDPOLYGONALFACE((15,16,22,21));
#644=IFCINDEXEDPOLYGONALFACE((16,17,23,22));
#645=IFCINDEXEDPOLYGONALFACE((17,18,24,23));
#646=IFCINDEXEDPOLYGONALFACE((24,19,25,30));
#647=IFCINDEXEDPOLYGONALFACE((19,20,26,25));
#648=IFCINDEXEDPOLYGONALFACE((20,21,27,26));
#649=IFCINDEXEDPOLYGONALFACE((21,22,28,27));
#650=IFCINDEXEDPOLYGONALFACE((22,23,29,28));
#651=IFCINDEXEDPOLYGONALFACE((23,24,30,29));
#652=IFCINDEXEDPOLYGONALFACE((30,25,31,36));
#653=IFCINDEXEDPOLYGONALFACE((25,26,32,31));
#654=IFCINDEXEDPOLYGONALFACE((26,27,33,32));
#655=IFCINDEXEDPOLYGONALFACE((27,28,34,33));
#656=IFCINDEXEDPOLYGONALFACE((28,29,35,34));
#657=IFCINDEXEDPOLYGONALFACE((29,30,36,35));
#658=IFCINDEXEDPOLYGONALFACE((36,31,37,42));
#659=IFCINDEXEDPOLYGONALFACE((31,32,38,37));
#660=IFCINDEXEDPOLYGONALFACE((32,33,39,38));
#661=IFCINDEXEDPOLYGONALFACE((33,34,40,39));
#662=IFCINDEXEDPOLYGONALFACE((34,35,41,40));
#663=IFCINDEXEDPOLYGONALFACE((35,36,42,41));
#664=IFCINDEXEDPOLYGONALFACE((42,37,43,48));
#665=IFCINDEXEDPOLYGONALFACE((37,38,44,43));
#666=IFCINDEXEDPOLYGONALFACE((38,39,45,44));
#667=IFCINDEXEDPOLYGONALFACE((39,40,46,45));
#668=IFCINDEXEDPOLYGONALFACE((40,41,47,46));
#669=IFCINDEXEDPOLYGONALFACE((41,42,48,47));
#670=IFCINDEXEDPOLYGONALFACE((48,43,49,54));
#671=IFCINDEXEDPOLYGONALFACE((43,44,50,49));
#672=IFCINDEXEDPOLYGONALFACE((44,45,51,50));
#673=IFCINDEXEDPOLYGONALFACE((45,46,52,51));
#674=IFCINDEXEDPOLYGONALFACE((46,47,53,52));
#675=IFCINDEXEDPOLYGONALFACE((47,48,54,53));
#676=IFCINDEXEDPOLYGONALFACE((49,50,51,52,53,54));
#677=IFCPOLYGONALFACESET(#626,$,(#627,#628,#629,#630,#631,#632,#633,#634,#635,#636,#637,#638,#639,#640,#641,#642,#643,#644,#645,#646,#647,#648,#649,#650,#651,#652,#653,#654,#655,#656,#657,#658,#659,#660,#661,#662,#663,#664,#665,#666,#667,#668,#669,#670,#671,#672,#673,#674,#675,#676),$);
#678=IFCSHAPEREPRESENTATION(#16,'Body','Curve2D',(#625));
#679=IFCSHAPEREPRESENTATION(#15,'Body','Tessellation',(#677));
#680=IFCGEOGRAPHICELEMENTTYPE('3mKZtHjt12Rh_uInKdrcGZ',$,'Shrub Big (1.8m)',$,$,$,(#685,#690),$,$,.NOTDEFINED.);
#681=IFCCARTESIANPOINT((0.,0.,0.));
#682=IFCDIRECTION((1.,0.,0.));
#683=IFCDIRECTION((0.,0.,1.));
#684=IFCAXIS2PLACEMENT3D(#681,#683,#682);
#685=IFCREPRESENTATIONMAP(#684,#679);
#686=IFCCARTESIANPOINT((0.,0.,0.));
#687=IFCDIRECTION((1.,0.,0.));
#688=IFCDIRECTION((0.,0.,1.));
#689=IFCAXIS2PLACEMENT3D(#686,#688,#687);
#690=IFCREPRESENTATIONMAP(#689,#678);
#691=IFCCARTESIANPOINT((0.,0.));
#692=IFCAXIS2PLACEMENT2D(#691,$);
#693=IFCCIRCLE(#692,100.);
#694=IFCARBITRARYCLOSEDPROFILEDEF(.AREA.,$,#693);
#695=IFCCARTESIANPOINT((0.,0.,0.));
#696=IFCDIRECTION((0.,0.,1.));
#697=IFCDIRECTION((1.,0.,0.));
#698=IFCAXIS2PLACEMENT3D(#695,#696,#697);
#699=IFCDIRECTION((0.,0.,1.));
#700=IFCEXTRUDEDAREASOLID(#694,#698,#699,460.);
#701=IFCCARTESIANPOINT((0.,0.,1130.));
#702=IFCDIRECTION((0.,0.,1.));
#703=IFCDIRECTION((1.,0.,0.));
#704=IFCAXIS2PLACEMENT3D(#701,#702,#703);
#705=IFCSPHERE(#704,670.);
#706=IFCCARTESIANPOINT((0.,0.));
#707=IFCAXIS2PLACEMENT2D(#706,$);
#708=IFCCIRCLE(#707,670.);
#709=IFCSHAPEREPRESENTATION(#16,'Body','Curve2D',(#708));
#710=IFCSHAPEREPRESENTATION(#15,'Body','SweptSolid',(#700,#705));
#711=IFCGEOGRAPHICELEMENTTYPE('24b7xpbZb38f1BXogqUXhD',$,'Simple Tree (1.8m)',$,$,$,(#716,#721),$,$,.NOTDEFINED.);
#712=IFCCARTESIANPOINT((0.,0.,0.));
#713=IFCDIRECTION((1.,0.,0.));
#714=IFCDIRECTION((0.,0.,1.));
#715=IFCAXIS2PLACEMENT3D(#712,#714,#713);
#716=IFCREPRESENTATIONMAP(#715,#710);
#717=IFCCARTESIANPOINT((0.,0.,0.));
#718=IFCDIRECTION((1.,0.,0.));
#719=IFCDIRECTION((0.,0.,1.));
#720=IFCAXIS2PLACEMENT3D(#717,#719,#718);
#721=IFCREPRESENTATIONMAP(#720,#709);
#722=IFCCARTESIANPOINT((0.,0.));
#723=IFCAXIS2PLACEMENT2D(#722,$);
#724=IFCCIRCLE(#723,100.);
#725=IFCARBITRARYCLOSEDPROFILEDEF(.AREA.,$,#724);
#726=IFCCARTESIANPOINT((0.,0.,0.));
#727=IFCDIRECTION((0.,0.,1.));
#728=IFCDIRECTION((1.,0.,0.));
#729=IFCAXIS2PLACEMENT3D(#726,#727,#728);
#730=IFCDIRECTION((0.,0.,1.));
#731=IFCEXTRUDEDAREASOLID(#725,#729,#730,1710.);
#732=IFCCARTESIANPOINT((0.,0.,3355.));
#733=IFCDIRECTION((0.,0.,1.));
#734=IFCDIRECTION((1.,0.,0.));
#735=IFCAXIS2PLACEMENT3D(#732,#733,#734);
#736=IFCSPHERE(#735,1645.);
#737=IFCCARTESIANPOINT((0.,0.));
#738=IFCAXIS2PLACEMENT2D(#737,$);
#739=IFCCIRCLE(#738,1645.);
#740=IFCSHAPEREPRESENTATION(#16,'Body','Curve2D',(#739));
#741=IFCSHAPEREPRESENTATION(#15,'Body','SweptSolid',(#731,#736));
#742=IFCGEOGRAPHICELEMENTTYPE('3VL8Zl9Xb2AwPSDD7jujfl',$,'Simple Tree (5m)',$,$,$,(#747,#752),$,$,.NOTDEFINED.);
#743=IFCCARTESIANPOINT((0.,0.,0.));
#744=IFCDIRECTION((1.,0.,0.));
#745=IFCDIRECTION((0.,0.,1.));
#746=IFCAXIS2PLACEMENT3D(#743,#745,#744);
#747=IFCREPRESENTATIONMAP(#746,#741);
#748=IFCCARTESIANPOINT((0.,0.,0.));
#749=IFCDIRECTION((1.,0.,0.));
#750=IFCDIRECTION((0.,0.,1.));
#751=IFCAXIS2PLACEMENT3D(#748,#750,#749);
#752=IFCREPRESENTATIONMAP(#751,#740);
#753=IFCCARTESIANPOINT((0.,0.));
#754=IFCAXIS2PLACEMENT2D(#753,$);
#755=IFCCIRCLE(#754,220.);
#756=IFCARBITRARYCLOSEDPROFILEDEF(.AREA.,$,#755);
#757=IFCCARTESIANPOINT((0.,0.,0.));
#758=IFCDIRECTION((0.,0.,1.));
#759=IFCDIRECTION((1.,0.,0.));
#760=IFCAXIS2PLACEMENT3D(#757,#758,#759);
#761=IFCDIRECTION((0.,0.,1.));
#762=IFCEXTRUDEDAREASOLID(#756,#760,#761,5790.);
#763=IFCCARTESIANPOINT((0.,0.,10395.));
#764=IFCDIRECTION((0.,0.,1.));
#765=IFCDIRECTION((1.,0.,0.));
#766=IFCAXIS2PLACEMENT3D(#763,#764,#765);
#767=IFCSPHERE(#766,4605.);
#768=IFCCARTESIANPOINT((0.,0.));
#769=IFCAXIS2PLACEMENT2D(#768,$);
#770=IFCCIRCLE(#769,4605.);
#771=IFCSHAPEREPRESENTATION(#16,'Body','Curve2D',(#770));
#772=IFCSHAPEREPRESENTATION(#15,'Body','SweptSolid',(#762,#767));
#773=IFCGEOGRAPHICELEMENTTYPE('3jGYIuJcL0phM2h4r9r95Q',$,'Simple Tree (15m)',$,$,$,(#778,#783),$,$,.NOTDEFINED.);
#774=IFCCARTESIANPOINT((0.,0.,0.));
#775=IFCDIRECTION((1.,0.,0.));
#776=IFCDIRECTION((0.,0.,1.));
#777=IFCAXIS2PLACEMENT3D(#774,#776,#775);
#778=IFCREPRESENTATIONMAP(#777,#772);
#779=IFCCARTESIANPOINT((0.,0.,0.));
#780=IFCDIRECTION((1.,0.,0.));
#781=IFCDIRECTION((0.,0.,1.));
#782=IFCAXIS2PLACEMENT3D(#779,#781,#780);
#783=IFCREPRESENTATIONMAP(#782,#771);
#784=IFCCARTESIANPOINTLIST2D(((-498.579620361328,735.001647949219),(-644.625122070312,558.698791503906),(-614.925903320312,498.60498046875),(-740.696044921875,396.37109375),(-782.288513183594,134.234802246094),(-551.41796875,97.9387969970703),(-788.211303710938,76.7640991210938),(-816.569641113281,-242.43864440918),(-685.161376953125,-297.968902587891),(-703.043884277344,-397.560852050781),(-586.489440917969,-569.15283203125),(-528.322204589844,-575.251037597656),(-366.814422607422,-750.108703613281),(-257.688507080078,-767.03466796875),(-273.860229492188,-683.197692871094),(-185.236541748047,-810.123107910156),(313.798278808594,-745.734313964844),(262.069519042969,-582.365661621094),(406.147064208984,-688.067260742188),(778.871398925781,-313.5146484375),(703.457824707031,-249.706893920898),(805.686340332031,-208.338500976562),(821.777282714844,71.8945388793945),(639.965148925781,44.3083839416504),(773.707153320312,162.339202880859),(703.734558105469,469.384552001953),(564.003479003906,549.215209960938),(489.063201904297,694.462707519531),(337.980773925781,746.2314453125),(159.236328125,638.655029296875),(287.592864990234,774.293273925781),(-11.1607036590576,832.838073730469),(-158.524017333984,800.207580566406),(-271.424072265625,751.009338378906)));
#785=IFCINDEXEDPOLYCURVE(#784,(IFCLINEINDEX((1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,1))),$);
#786=IFCCARTESIANPOINTLIST3D(((80.,0.,0.),(40.,69.2820323027551,0.),(-40.,69.2820323027551,0.),(-80.,9.79717439317883E-15,0.),(-40.,-69.2820323027551,0.),(40.,-69.2820323027551,0.),(80.,0.,280.),(40.,69.2820323027551,280.),(-40.,69.2820323027551,280.),(-80.,9.79717439317883E-15,280.),(-40.,-69.2820323027551,280.),(40.,-69.2820323027551,280.),(610.340986817674,-5.11998806339175,502.765430824539),(281.439072714044,546.677704468069,520.441255965265),(-291.549977559115,499.655102439697,498.631051036932),(-617.600357563209,-18.0002392785791,529.745674601363),(-266.847896537159,-556.910514833911,523.07445182747),(310.029723989709,-532.642943384166,481.563368832141),(699.397468993717,-3.10815712380199,727.667741239885),(355.052935043769,568.360673738432,733.570036889682),(-308.666246071194,628.582194868307,722.445245365077),(-719.613234433747,-35.7080296205946,687.931736233252),(-311.6478364595,-608.980286853059,704.648197605265),(372.070129606822,-608.152053362288,717.812431534607),(763.794228948937,-31.3204193188668,937.51589967911),(408.972888631657,640.716082391856,911.578097173966),(-390.473345044531,632.112510840184,931.184023328963),(-745.505233761067,11.3681475906559,933.839400395571),(-358.630946664301,-690.676374550022,936.269525762716),(375.140586877863,-658.159992875781,903.558501964971),(869.836914477244,-29.0443800107266,1124.54961826854),(447.136382145783,768.355555760864,1143.76409675514),(-431.186180114122,717.796288298907,1132.35007279295),(-841.55780048986,-23.1508504281077,1121.17416888324),(-428.187090174966,-760.246071143039,1159.279616419),(448.116008554028,-716.140773181744,1147.14103266758),(757.425130447899,-4.70084047240072,1330.07490310776),(399.505270456854,653.333240297718,1352.71275883304),(-414.445397665745,661.713946940036,1371.67941135993),(-776.277740187312,26.6072792686131,1378.19571732018),(-402.818606589622,-663.628443148848,1395.27272058484),(385.647897991142,-658.102369718758,1334.23398662872),(686.035800389567,-2.77622907865673,1575.7384942221),(337.67134578655,600.901570871229,1585.64117034361),(-344.127505367052,570.723591037471,1578.6341137138),(-653.055169703612,-6.10749408831596,1549.40002660187),(-379.19620927887,-592.236118824385,1550.49839466833),(314.077525184638,-622.955785610011,1579.55078264226),(0.05,0.,1800.),(0.025,0.0433012701892219,1800.),(-0.025,0.0433012701892219,1800.),(-0.05,6.12323399573677E-18,1800.),(-0.025,-0.0433012701892219,1800.),(0.025,-0.0433012701892219,1800.)));
#787=IFCINDEXEDPOLYGONALFACE((1,2,3,4,5,6));
#788=IFCINDEXEDPOLYGONALFACE((6,1,7,12));
#789=IFCINDEXEDPOLYGONALFACE((1,2,8,7));
#790=IFCINDEXEDPOLYGONALFACE((2,3,9,8));
#791=IFCINDEXEDPOLYGONALFACE((3,4,10,9));
#792=IFCINDEXEDPOLYGONALFACE((4,5,11,10));
#793=IFCINDEXEDPOLYGONALFACE((5,6,12,11));
#794=IFCINDEXEDPOLYGONALFACE((12,7,13,18));
#795=IFCINDEXEDPOLYGONALFACE((7,8,14,13));
#796=IFCINDEXEDPOLYGONALFACE((8,9,15,14));
#797=IFCINDEXEDPOLYGONALFACE((9,10,16,15));
#798=IFCINDEXEDPOLYGONALFACE((10,11,17,16));
#799=IFCINDEXEDPOLYGONALFACE((11,12,18,17));
#800=IFCINDEXEDPOLYGONALFACE((18,13,19,24));
#801=IFCINDEXEDPOLYGONALFACE((13,14,20,19));
#802=IFCINDEXEDPOLYGONALFACE((14,15,21,20));
#803=IFCINDEXEDPOLYGONALFACE((15,16,22,21));
#804=IFCINDEXEDPOLYGONALFACE((16,17,23,22));
#805=IFCINDEXEDPOLYGONALFACE((17,18,24,23));
#806=IFCINDEXEDPOLYGONALFACE((24,19,25,30));
#807=IFCINDEXEDPOLYGONALFACE((19,20,26,25));
#808=IFCINDEXEDPOLYGONALFACE((20,21,27,26));
#809=IFCINDEXEDPOLYGONALFACE((21,22,28,27));
#810=IFCINDEXEDPOLYGONALFACE((22,23,29,28));
#811=IFCINDEXEDPOLYGONALFACE((23,24,30,29));
#812=IFCINDEXEDPOLYGONALFACE((30,25,31,36));
#813=IFCINDEXEDPOLYGONALFACE((25,26,32,31));
#814=IFCINDEXEDPOLYGONALFACE((26,27,33,32));
#815=IFCINDEXEDPOLYGONALFACE((27,28,34,33));
#816=IFCINDEXEDPOLYGONALFACE((28,29,35,34));
#817=IFCINDEXEDPOLYGONALFACE((29,30,36,35));
#818=IFCINDEXEDPOLYGONALFACE((36,31,37,42));
#819=IFCINDEXEDPOLYGONALFACE((31,32,38,37));
#820=IFCINDEXEDPOLYGONALFACE((32,33,39,38));
#821=IFCINDEXEDPOLYGONALFACE((33,34,40,39));
#822=IFCINDEXEDPOLYGONALFACE((34,35,41,40));
#823=IFCINDEXEDPOLYGONALFACE((35,36,42,41));
#824=IFCINDEXEDPOLYGONALFACE((42,37,43,48));
#825=IFCINDEXEDPOLYGONALFACE((37,38,44,43));
#826=IFCINDEXEDPOLYGONALFACE((38,39,45,44));
#827=IFCINDEXEDPOLYGONALFACE((39,40,46,45));
#828=IFCINDEXEDPOLYGONALFACE((40,41,47,46));
#829=IFCINDEXEDPOLYGONALFACE((41,42,48,47));
#830=IFCINDEXEDPOLYGONALFACE((48,43,49,54));
#831=IFCINDEXEDPOLYGONALFACE((43,44,50,49));
#832=IFCINDEXEDPOLYGONALFACE((44,45,51,50));
#833=IFCINDEXEDPOLYGONALFACE((45,46,52,51));
#834=IFCINDEXEDPOLYGONALFACE((46,47,53,52));
#835=IFCINDEXEDPOLYGONALFACE((47,48,54,53));
#836=IFCINDEXEDPOLYGONALFACE((49,50,51,52,53,54));
#837=IFCPOLYGONALFACESET(#786,$,(#787,#788,#789,#790,#791,#792,#793,#794,#795,#796,#797,#798,#799,#800,#801,#802,#803,#804,#805,#806,#807,#808,#809,#810,#811,#812,#813,#814,#815,#816,#817,#818,#819,#820,#821,#822,#823,#824,#825,#826,#827,#828,#829,#830,#831,#832,#833,#834,#835,#836),$);
#838=IFCSHAPEREPRESENTATION(#16,'Body','Curve2D',(#785));
#839=IFCSHAPEREPRESENTATION(#15,'Body','Tessellation',(#837));
#840=IFCGEOGRAPHICELEMENTTYPE('1CLHhCfcnFaBY1EM4kLSOR',$,'Tree Small (1.8m)',$,$,$,(#845,#850),$,$,.NOTDEFINED.);
#841=IFCCARTESIANPOINT((0.,0.,0.));
#842=IFCDIRECTION((1.,0.,0.));
#843=IFCDIRECTION((0.,0.,1.));
#844=IFCAXIS2PLACEMENT3D(#841,#843,#842);
#845=IFCREPRESENTATIONMAP(#844,#839);
#846=IFCCARTESIANPOINT((0.,0.,0.));
#847=IFCDIRECTION((1.,0.,0.));
#848=IFCDIRECTION((0.,0.,1.));
#849=IFCAXIS2PLACEMENT3D(#846,#848,#847);
#850=IFCREPRESENTATIONMAP(#849,#838);
#851=IFCCARTESIANPOINTLIST2D(((-2932.8212890625,4323.5390625),(-3791.91259765625,3286.46362304688),(-3617.21118164062,2932.97045898438),(-4357.03564453125,2331.5947265625),(-4601.697265625,789.616455078125),(-3243.63500976562,576.110534667969),(-4636.537109375,451.553527832031),(-4803.35107421875,-1426.10961914062),(-4030.36108398438,-1752.75817871094),(-4135.55224609375,-2338.59326171875),(-3449.93798828125,-3347.95776367188),(-3107.77783203125,-3383.82958984375),(-2157.73193359375,-4412.404296875),(-1515.81469726562,-4511.96826171875),(-1610.94250488281,-4018.80981445312),(-1089.62670898438,-4765.43017578125),(1845.87219238281,-4386.67236328125),(1541.58532714844,-3425.68041992188),(2389.10034179688,-4047.45458984375),(4581.5966796875,-1844.20373535156),(4137.9873046875,-1468.86413574219),(4739.33154296875,-1225.52062988281),(4833.98388671875,422.909027099609),(3764.5009765625,260.637542724609),(4551.21826171875,954.936462402344),(4139.615234375,2761.08569335938),(3317.66748046875,3230.677734375),(2876.84228515625,4085.07470703125),(1988.12219238281,4389.5966796875),(936.684326171875,3756.79443359375),(1691.72277832031,4554.66650390625),(-65.6511993408203,4899.0478515625),(-932.494262695312,4707.103515625),(-1596.61218261719,4417.7021484375)));
#852=IFCINDEXEDPOLYCURVE(#851,(IFCLINEINDEX((1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,1))),$);
#853=IFCCARTESIANPOINTLIST3D(((220.,0.,0.),(110.,190.525588832576,0.),(-110.,190.525588832577,0.),(-220.,2.69422295812418E-14,0.),(-110.,-190.525588832576,0.),(110.,-190.525588832576,0.),(220.,0.,1830.),(110.,190.525588832576,1830.),(-110.,190.525588832577,1830.),(-220.,2.69422295812418E-14,1830.),(-110.,-190.525588832576,1830.),(110.,-190.525588832576,1830.),(3602.84155681395,-42.6665671949312,3758.28335210925),(1603.6589392837,3271.04318828699,3905.58189494864),(-1687.91647965929,2879.18817138389,3723.83018721253),(-3663.33631302674,-150.001993988159,3983.11871691612),(-1482.06580447633,-3356.31660800234,3927.52519380035),(1841.91436658091,-3154.08684592113,3581.5995021726),(4144.97890828097,-25.9013093650166,5704.3740341419),(2117.10779203141,3278.52951811646,5753.55983122354),(-1730.55205059328,3780.37552753208,5660.85323518517),(-4313.44362028123,-297.566913504955,5373.24065908662),(-1755.3986371625,-3617.02629407169,5512.5445038534),(2258.91774672351,-3610.12434831526,5622.24645326459),(4481.61857457447,-261.00349432389,7525.01344970687),(2466.44073859714,3708.28617613811,7308.86509549734),(-2312.27787537109,3636.58974654084,7472.24781345564),(-4329.21028134222,94.734563255466,7494.37595567738),(-2046.92455553584,-4124.62194412282,7514.62700040359),(2184.50489064885,-3853.65209683748,7242.03513542238),(5165.3076206437,-242.036500089388,9155.53253319023),(2684.46985121485,4598.74337345629,9315.65318724518),(-2551.55150095102,4177.41614460665,9220.53632089363),(-4929.6483374155,-192.923753567564,9127.40378831275),(-2526.55908479138,-4531.16433497441,9444.94918444402),(2692.6334046169,-4163.62018529695,9343.7943198489),(4428.54275373249,-39.1736706033394,10940.1480020885),(2387.54392047378,3813.42915868696,11128.7967997992),(-2512.04498054787,3883.26838070627,11286.8522375232),(-4585.64783489427,221.727327238442,11341.154787192),(-2415.15505491352,-3899.22251577971,11483.4631477308),(2272.06581659285,-3853.17190386229,10974.8070314298),(4033.63166991306,-23.1352423221394,13059.2493566128),(1972.26121488791,3549.70366088977,13141.7716576253),(-2026.06254472544,3298.22049560846,13083.3795190436),(-3758.79308086343,-50.8957840692999,12839.7621264441),(-2318.30174399058,-3477.4915604994,12848.9151936647),(1775.64604320532,-3733.48878371295,13091.0184267807),(0.05,0.,15000.),(0.025,0.0433012701892219,15000.),(-0.025,0.0433012701892219,15000.),(-0.05,6.12323399573677E-18,15000.),(-0.025,-0.0433012701892219,15000.),(0.025,-0.0433012701892219,15000.)));
#854=IFCINDEXEDPOLYGONALFACE((1,2,3,4,5,6));
#855=IFCINDEXEDPOLYGONALFACE((6,1,7,12));
#856=IFCINDEXEDPOLYGONALFACE((1,2,8,7));
#857=IFCINDEXEDPOLYGONALFACE((2,3,9,8));
#858=IFCINDEXEDPOLYGONALFACE((3,4,10,9));
#859=IFCINDEXEDPOLYGONALFACE((4,5,11,10));
#860=IFCINDEXEDPOLYGONALFACE((5,6,12,11));
#861=IFCINDEXEDPOLYGONALFACE((12,7,13,18));
#862=IFCINDEXEDPOLYGONALFACE((7,8,14,13));
#863=IFCINDEXEDPOLYGONALFACE((8,9,15,14));
#864=IFCINDEXEDPOLYGONALFACE((9,10,16,15));
#865=IFCINDEXEDPOLYGONALFACE((10,11,17,16));
#866=IFCINDEXEDPOLYGONALFACE((11,12,18,17));
#867=IFCINDEXEDPOLYGONALFACE((18,13,19,24));
#868=IFCINDEXEDPOLYGONALFACE((13,14,20,19));
#869=IFCINDEXEDPOLYGONALFACE((14,15,21,20));
#870=IFCINDEXEDPOLYGONALFACE((15,16,22,21));
#871=IFCINDEXEDPOLYGONALFACE((16,17,23,22));
#872=IFCINDEXEDPOLYGONALFACE((17,18,24,23));
#873=IFCINDEXEDPOLYGONALFACE((24,19,25,30));
#874=IFCINDEXEDPOLYGONALFACE((19,20,26,25));
#875=IFCINDEXEDPOLYGONALFACE((20,21,27,26));
#876=IFCINDEXEDPOLYGONALFACE((21,22,28,27));
#877=IFCINDEXEDPOLYGONALFACE((22,23,29,28));
#878=IFCINDEXEDPOLYGONALFACE((23,24,30,29));
#879=IFCINDEXEDPOLYGONALFACE((30,25,31,36));
#880=IFCINDEXEDPOLYGONALFACE((25,26,32,31));
#881=IFCINDEXEDPOLYGONALFACE((26,27,33,32));
#882=IFCINDEXEDPOLYGONALFACE((27,28,34,33));
#883=IFCINDEXEDPOLYGONALFACE((28,29,35,34));
#884=IFCINDEXEDPOLYGONALFACE((29,30,36,35));
#885=IFCINDEXEDPOLYGONALFACE((36,31,37,42));
#886=IFCINDEXEDPOLYGONALFACE((31,32,38,37));
#887=IFCINDEXEDPOLYGONALFACE((32,33,39,38));
#888=IFCINDEXEDPOLYGONALFACE((33,34,40,39));
#889=IFCINDEXEDPOLYGONALFACE((34,35,41,40));
#890=IFCINDEXEDPOLYGONALFACE((35,36,42,41));
#891=IFCINDEXEDPOLYGONALFACE((42,37,43,48));
#892=IFCINDEXEDPOLYGONALFACE((37,38,44,43));
#893=IFCINDEXEDPOLYGONALFACE((38,39,45,44));
#894=IFCINDEXEDPOLYGONALFACE((39,40,46,45));
#895=IFCINDEXEDPOLYGONALFACE((40,41,47,46));
#896=IFCINDEXEDPOLYGONALFACE((41,42,48,47));
#897=IFCINDEXEDPOLYGONALFACE((48,43,49,54));
#898=IFCINDEXEDPOLYGONALFACE((43,44,50,49));
#899=IFCINDEXEDPOLYGONALFACE((44,45,51,50));
#900=IFCINDEXEDPOLYGONALFACE((45,46,52,51));
#901=IFCINDEXEDPOLYGONALFACE((46,47,53,52));
#902=IFCINDEXEDPOLYGONALFACE((47,48,54,53));
#903=IFCINDEXEDPOLYGONALFACE((49,50,51,52,53,54));
#904=IFCPOLYGONALFACESET(#853,$,(#854,#855,#856,#857,#858,#859,#860,#861,#862,#863,#864,#865,#866,#867,#868,#869,#870,#871,#872,#873,#874,#875,#876,#877,#878,#879,#880,#881,#882,#883,#884,#885,#886,#887,#888,#889,#890,#891,#892,#893,#894,#895,#896,#897,#898,#899,#900,#901,#902,#903),$);
#905=IFCSHAPEREPRESENTATION(#16,'Body','Curve2D',(#852));
#906=IFCSHAPEREPRESENTATION(#15,'Body','Tessellation',(#904));
#907=IFCGEOGRAPHICELEMENTTYPE('1$Hd6qXnz8ff4y_T2Agko8',$,'Tree Big (15m)',$,$,$,(#912,#917),$,$,.NOTDEFINED.);
#908=IFCCARTESIANPOINT((0.,0.,0.));
#909=IFCDIRECTION((1.,0.,0.));
#910=IFCDIRECTION((0.,0.,1.));
#911=IFCAXIS2PLACEMENT3D(#908,#910,#909);
#912=IFCREPRESENTATIONMAP(#911,#906);
#913=IFCCARTESIANPOINT((0.,0.,0.));
#914=IFCDIRECTION((1.,0.,0.));
#915=IFCDIRECTION((0.,0.,1.));
#916=IFCAXIS2PLACEMENT3D(#913,#915,#914);
#917=IFCREPRESENTATIONMAP(#916,#905);
#918=IFCCARTESIANPOINTLIST2D(((-1466.41064453125,2161.76953125),(-1895.95629882812,1643.23181152344),(-1808.60559082031,1466.48522949219),(-2178.51782226562,1165.79736328125),(-2300.8486328125,394.808227539062),(-1621.81750488281,288.055267333984),(-2318.2685546875,225.776763916016),(-2401.67553710938,-713.054809570312),(-2015.18054199219,-876.379089355469),(-2067.77612304688,-1169.29663085938),(-1724.96899414062,-1673.97888183594),(-1553.88891601562,-1691.91479492188),(-1078.86596679688,-2206.2021484375),(-757.907348632812,-2255.98413085938),(-805.471252441406,-2009.40490722656),(-544.813354492188,-2382.71508789062),(922.936096191406,-2193.33618164062),(770.792663574219,-1712.84020996094),(1194.55017089844,-2023.72729492188),(2290.79833984375,-922.101867675781),(2068.99365234375,-734.432067871094),(2369.66577148438,-612.760314941406),(2416.99194335938,211.454513549805),(1882.25048828125,130.318771362305),(2275.60913085938,477.468231201172),(2069.8076171875,1380.54284667969),(1658.83374023438,1615.3388671875),(1438.42114257812,2042.53735351562),(994.061096191406,2194.79833984375),(468.342163085938,1878.39721679688),(845.861389160156,2277.33325195312),(-32.8255996704102,2449.52392578125),(-466.247131347656,2353.5517578125),(-798.306091308594,2208.85107421875)));
#919=IFCINDEXEDPOLYCURVE(#918,(IFCLINEINDEX((1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,1))),$);
#920=IFCCARTESIANPOINTLIST3D(((110.,0.,0.),(55.,95.2627944162882,0.),(-55.,95.2627944162883,0.),(-110.,1.34711147906209E-14,0.),(-55.,-95.2627944162882,0.),(55.,-95.2627944162882,0.),(110.,0.,840.),(55.,95.2627944162882,840.),(-55.,95.2627944162883,840.),(-110.,1.34711147906209E-14,840.),(-55.,-95.2627944162882,840.),(55.,-95.2627944162882,840.),(1794.28051893798,-14.2221890649771,1449.90397451261),(831.219646427901,1604.18946900776,1499.0034887924),(-859.305493219764,1473.57113004006,1438.4195862137),(-1814.44543767558,-50.0006646627197,1524.8490961149),(-790.68860149211,-1632.61394224621,1506.31792174297),(910.638122193637,-1565.20402155248,1391.00935786706),(2054.99296942699,-8.63376978833887,2065.74372566635),(1042.36926401047,1675.96694458701,2082.13899136023),(-913.517350197761,1843.24894772555,2051.23679268077),(-2111.14787342708,-99.1889711683183,1955.36593398126),(-921.799545720834,-1788.79920323875,2001.80054890351),(1089.63924890784,-1786.49855465328,2038.36786537391),(2247.20619152482,-87.0011647746299,2639.76638799753),(1198.81357953238,1888.50119623031,2567.71693659435),(-1147.42595845703,1864.60238636456,2622.17784258045),(-2196.40342711407,31.5781877518221,2629.5538899877),(-1058.97485184528,-2027.27978555855,2636.30423822977),(1104.83496354962,-1936.9565031301,2545.44028323603),(2555.10254021457,-80.6788333631295,3150.41560630151),(1311.48995040495,2254.60229430579,3203.78915765315),(-1267.18383365034,2114.15988468925,3172.08353553597),(-2476.54944580517,-64.3079178558546,3141.03935800901),(-1258.85302826379,-2232.07594814517,3246.8878233861),(1314.2111348723,-2109.56123158602,3213.16953518773),(2229.51425124416,-13.0578902011131,3712.43028641044),(1172.51464015793,1923.54885707993,3775.31321898068),(-1214.01499351596,1946.82859775303,3827.99836488869),(-2281.88261163142,73.9091090794808,3846.09921477827),(-1181.71835163784,-1952.14664277751,3893.53533495789),(1134.02193886428,-1936.79643880504,3723.98329619088),(2017.87722330435,-7.71174744071313,4385.94026172807),(994.087071629305,1766.35832551144,4413.44769539892),(-1012.02084824181,1682.53060375101,4393.983649205),(-1926.26436028781,-16.9652613564332,4312.77785167185),(-1109.43391466353,-1742.28762538132,4315.82887407871),(928.548681068438,-1827.62003311917,4396.52995178405),(0.05,0.,5000.),(0.025,0.0433012701892219,5000.),(-0.025,0.0433012701892219,5000.),(-0.05,6.12323399573677E-18,5000.),(-0.025,-0.0433012701892219,5000.),(0.025,-0.0433012701892219,5000.)));
#921=IFCINDEXEDPOLYGONALFACE((1,2,3,4,5,6));
#922=IFCINDEXEDPOLYGONALFACE((6,1,7,12));
#923=IFCINDEXEDPOLYGONALFACE((1,2,8,7));
#924=IFCINDEXEDPOLYGONALFACE((2,3,9,8));
#925=IFCINDEXEDPOLYGONALFACE((3,4,10,9));
#926=IFCINDEXEDPOLYGONALFACE((4,5,11,10));
#927=IFCINDEXEDPOLYGONALFACE((5,6,12,11));
#928=IFCINDEXEDPOLYGONALFACE((12,7,13,18));
#929=IFCINDEXEDPOLYGONALFACE((7,8,14,13));
#930=IFCINDEXEDPOLYGONALFACE((8,9,15,14));
#931=IFCINDEXEDPOLYGONALFACE((9,10,16,15));
#932=IFCINDEXEDPOLYGONALFACE((10,11,17,16));
#933=IFCINDEXEDPOLYGONALFACE((11,12,18,17));
#934=IFCINDEXEDPOLYGONALFACE((18,13,19,24));
#935=IFCINDEXEDPOLYGONALFACE((13,14,20,19));
#936=IFCINDEXEDPOLYGONALFACE((14,15,21,20));
#937=IFCINDEXEDPOLYGONALFACE((15,16,22,21));
#938=IFCINDEXEDPOLYGONALFACE((16,17,23,22));
#939=IFCINDEXEDPOLYGONALFACE((17,18,24,23));
#940=IFCINDEXEDPOLYGONALFACE((24,19,25,30));
#941=IFCINDEXEDPOLYGONALFACE((19,20,26,25));
#942=IFCINDEXEDPOLYGONALFACE((20,21,27,26));
#943=IFCINDEXEDPOLYGONALFACE((21,22,28,27));
#944=IFCINDEXEDPOLYGONALFACE((22,23,29,28));
#945=IFCINDEXEDPOLYGONALFACE((23,24,30,29));
#946=IFCINDEXEDPOLYGONALFACE((30,25,31,36));
#947=IFCINDEXEDPOLYGONALFACE((25,26,32,31));
#948=IFCINDEXEDPOLYGONALFACE((26,27,33,32));
#949=IFCINDEXEDPOLYGONALFACE((27,28,34,33));
#950=IFCINDEXEDPOLYGONALFACE((28,29,35,34));
#951=IFCINDEXEDPOLYGONALFACE((29,30,36,35));
#952=IFCINDEXEDPOLYGONALFACE((36,31,37,42));
#953=IFCINDEXEDPOLYGONALFACE((31,32,38,37));
#954=IFCINDEXEDPOLYGONALFACE((32,33,39,38));
#955=IFCINDEXEDPOLYGONALFACE((33,34,40,39));
#956=IFCINDEXEDPOLYGONALFACE((34,35,41,40));
#957=IFCINDEXEDPOLYGONALFACE((35,36,42,41));
#958=IFCINDEXEDPOLYGONALFACE((42,37,43,48));
#959=IFCINDEXEDPOLYGONALFACE((37,38,44,43));
#960=IFCINDEXEDPOLYGONALFACE((38,39,45,44));
#961=IFCINDEXEDPOLYGONALFACE((39,40,46,45));
#962=IFCINDEXEDPOLYGONALFACE((40,41,47,46));
#963=IFCINDEXEDPOLYGONALFACE((41,42,48,47));
#964=IFCINDEXEDPOLYGONALFACE((48,43,49,54));
#965=IFCINDEXEDPOLYGONALFACE((43,44,50,49));
#966=IFCINDEXEDPOLYGONALFACE((44,45,51,50));
#967=IFCINDEXEDPOLYGONALFACE((45,46,52,51));
#968=IFCINDEXEDPOLYGONALFACE((46,47,53,52));
#969=IFCINDEXEDPOLYGONALFACE((47,48,54,53));
#970=IFCINDEXEDPOLYGONALFACE((49,50,51,52,53,54));
#971=IFCPOLYGONALFACESET(#920,$,(#921,#922,#923,#924,#925,#926,#927,#928,#929,#930,#931,#932,#933,#934,#935,#936,#937,#938,#939,#940,#941,#942,#943,#944,#945,#946,#947,#948,#949,#950,#951,#952,#953,#954,#955,#956,#957,#958,#959,#960,#961,#962,#963,#964,#965,#966,#967,#968,#969,#970),$);
#972=IFCSHAPEREPRESENTATION(#16,'Body','Curve2D',(#919));
#973=IFCSHAPEREPRESENTATION(#15,'Body','Tessellation',(#971));
#974=IFCGEOGRAPHICELEMENTTYPE('1t393Epk10ahdDnhnFR8sr',$,'Tree Medium (5m)',$,$,$,(#979,#984),$,$,.NOTDEFINED.);
#975=IFCCARTESIANPOINT((0.,0.,0.));
#976=IFCDIRECTION((1.,0.,0.));
#977=IFCDIRECTION((0.,0.,1.));
#978=IFCAXIS2PLACEMENT3D(#975,#977,#976);
#979=IFCREPRESENTATIONMAP(#978,#973);
#980=IFCCARTESIANPOINT((0.,0.,0.));
#981=IFCDIRECTION((1.,0.,0.));
#982=IFCDIRECTION((0.,0.,1.));
#983=IFCAXIS2PLACEMENT3D(#980,#982,#981);
#984=IFCREPRESENTATIONMAP(#983,#972);
ENDSEC;
END-ISO-10303-21;
+57 -98
View File
@@ -21,6 +21,7 @@ import bpy
import json
import addon_utils
import ifcopenshell.api.owner.settings
import blenderbim.bim
import blenderbim.tool as tool
import blenderbim.core.owner as core_owner
from bpy.app.handlers import persistent
@@ -253,85 +254,58 @@ def viewport_shading_changed_callback(area):
bpy.context.scene.BIMStylesProperties.active_style_type = "External"
if getattr(bpy.types, "SCENE_PT_scene"):
@classmethod
def poll_check_blender_tab(cls, context):
return tool.Blender.is_tab(context, "BLENDER")
class Override_SCENE_PT_scene(bpy.types.SCENE_PT_scene):
bl_idname = "SCENE_PT_scene_override"
def get_override_scene_panel(panel_name):
original_panel = getattr(bpy.types, panel_name)
override_panel = type(f"Override_{panel_name}", (original_panel,), {})
override_panel.bl_idname = f"{panel_name}_override"
# some blender panel depend on other and register_class will throw an error
# if we won't override their `bl_parent_id`
bl_parent_id = getattr(override_panel, "bl_parent_id", None)
if bl_parent_id is not None:
override_panel.bl_parent_id = f"{bl_parent_id}_override"
# override poll method
poll = getattr(override_panel, "poll", None)
if poll is None:
override_panel.poll = poll_check_blender_tab
else:
@classmethod
def poll(cls, context):
return tool.Blender.is_tab(context, "BLENDER")
def wrapped_poll(cls, context):
return super(override_panel, cls).poll(context) and poll_check_blender_tab.__func__(cls, context)
override_panel.poll = wrapped_poll
return override_panel
if getattr(bpy.types, "SCENE_PT_unit"):
class Override_SCENE_PT_unit(bpy.types.SCENE_PT_unit):
bl_idname = "SCENE_PT_unit_override"
@classmethod
def poll(cls, context):
return tool.Blender.is_tab(context, "BLENDER")
if getattr(bpy.types, "SCENE_PT_physics"):
class Override_SCENE_PT_physics(bpy.types.SCENE_PT_physics):
bl_idname = "SCENE_PT_physics_override"
@classmethod
def poll(cls, context):
return tool.Blender.is_tab(context, "BLENDER")
if getattr(bpy.types, "SCENE_PT_rigid_body_world"):
class Override_SCENE_PT_rigid_body_world(bpy.types.SCENE_PT_rigid_body_world):
bl_idname = "SCENE_PT_rigid_body_world_override"
@classmethod
def poll(cls, context):
return tool.Blender.is_tab(context, "BLENDER")
if getattr(bpy.types, "SCENE_PT_audio"):
class Override_SCENE_PT_audio(bpy.types.SCENE_PT_audio):
bl_idname = "SCENE_PT_audio_override"
@classmethod
def poll(cls, context):
return tool.Blender.is_tab(context, "BLENDER")
if getattr(bpy.types, "SCENE_PT_keying_sets"):
class Override_SCENE_PT_keying_sets(bpy.types.SCENE_PT_keying_sets):
bl_idname = "SCENE_PT_keying_sets_override"
@classmethod
def poll(cls, context):
return tool.Blender.is_tab(context, "BLENDER")
if getattr(bpy.types, "SCENE_PT_custom_props"):
class Override_SCENE_PT_custom_props(bpy.types.SCENE_PT_custom_props):
bl_idname = "SCENE_PT_custom_props_override"
@classmethod
def poll(cls, context):
return tool.Blender.is_tab(context, "BLENDER")
# available on Blender 4.0+
if getattr(bpy.types, "SCENE_PT_simulation", None):
class Override_SCENE_PT_simulation(bpy.types.SCENE_PT_simulation):
bl_idname = "SCENE_PT_simulation_override"
@classmethod
def poll(cls, context):
return tool.Blender.is_tab(context, "BLENDER")
# TODO: possibly override scene panels from other addons too?
# list can be updated from
# https://projects.blender.org/blender/blender/src/branch/main/scripts/startup/bl_ui/properties_scene.py#L421
OVERRIDE_SCENE_PANELS = (
"SCENE_PT_scene",
"SCENE_PT_unit",
"SCENE_PT_physics",
"SCENE_PT_rigid_body_world",
"SCENE_PT_rigid_body_world_settings",
"SCENE_PT_rigid_body_cache",
"SCENE_PT_rigid_body_field_weights",
"SCENE_PT_audio",
"SCENE_PT_keying_sets",
"SCENE_PT_custom_props",
# after SCENE_PT_keying_sets
"SCENE_PT_keying_set_paths",
"SCENE_PT_keyframing_settings",
)
if bpy.app.version >= (4, 0):
OVERRIDE_SCENE_PANELS += ("SCENE_PT_simulation",)
@persistent
@@ -372,27 +346,12 @@ def load_post(scene):
# To improve usability for new users, we hijack the scene properties
# tab. We override default scene properties panels with our own poll
# to hide them unless the user has chosen to view Blender properties.
for panel in [
"SCENE_PT_scene",
"SCENE_PT_unit",
"SCENE_PT_physics",
"SCENE_PT_rigid_body_world",
"SCENE_PT_audio",
"SCENE_PT_keying_sets",
"SCENE_PT_simulation",
"SCENE_PT_custom_props",
]:
if getattr(bpy.types, panel, None):
try:
bpy.utils.register_class(globals()[f"Override_{panel}"])
bpy.utils.unregister_class(getattr(bpy.types, panel))
except:
pass
# https://blender.stackexchange.com/questions/140644/how-can-make-the-state-of-a-boolean-property-relative-to-the-3d-view-area
for screen in bpy.data.screens:
if len(screen.BIMAreaProperties) == 20:
continue
screen.BIMAreaProperties.clear()
for i in range(20): # 20 is an arbitrary value of split areas
screen.BIMAreaProperties.add()
for panel in OVERRIDE_SCENE_PANELS:
if panel in blenderbim.bim.overridden_scene_panels:
continue
override_panel = get_override_scene_panel(panel)
original_panel = getattr(bpy.types, panel)
bpy.utils.register_class(override_panel)
bpy.utils.unregister_class(original_panel)
blenderbim.bim.overridden_scene_panels[panel] = (original_panel, override_panel)
tool.Blender.setup_tabs()
+7 -1
View File
@@ -245,7 +245,9 @@ def get_obj_ifc_definition_id(context, obj, obj_type):
].ifc_definition_id
elif obj_type == "WorkSchedule":
return context.scene.BIMWorkScheduleProperties.active_work_schedule_id
elif obj_type == "Group":
prop = context.scene.BIMGroupProperties
return prop.groups[prop.active_group_index].ifc_definition_id
# hack to close popup
# https://blender.stackexchange.com/a/202576/130742
@@ -321,6 +323,7 @@ def draw_filter(layout, filter_groups, data, module):
row = box.row(align=True)
row.prop(ifc_filter, "pset", text="", icon="PROPERTIES")
row.prop(ifc_filter, "name", text="")
row.prop(ifc_filter, "comparison", text="")
row.prop(ifc_filter, "value", text="")
elif ifc_filter.type == "classification":
row = box.row(align=True)
@@ -328,6 +331,9 @@ def draw_filter(layout, filter_groups, data, module):
elif ifc_filter.type == "location":
row = box.row(align=True)
row.prop(ifc_filter, "value", text="", icon="PACKAGE")
elif ifc_filter.type == "group":
row = box.row(align=True)
row.prop(ifc_filter, "value", text="", icon="OUTLINER_COLLECTION")
elif ifc_filter.type == "query":
row = box.row(align=True)
row.prop(ifc_filter, "name", text="", icon="POINTCLOUD_DATA")
+7 -2
View File
@@ -80,8 +80,8 @@ class IfcStore:
if IfcStore.path:
try:
IfcStore.load_file(IfcStore.path)
except:
pass
except Exception as e:
print(f"Failed to load file {IfcStore.path}. Error details: {e}")
return IfcStore.file
@staticmethod
@@ -163,8 +163,12 @@ class IfcStore:
if not IfcStore.get_file():
return
for obj in bpy.data.objects:
if obj.library:
continue
IfcStore.relink_object(obj)
for obj in bpy.data.materials:
if obj.library:
continue
IfcStore.relink_object(obj)
@staticmethod
@@ -317,6 +321,7 @@ class IfcStore:
@staticmethod
def execute_ifc_operator(operator, context, is_invoke=False):
bpy.context.scene.BIMProperties.is_dirty = True
is_top_level_operator = not bool(IfcStore.current_transaction)
if is_top_level_operator:
+48 -14
View File
@@ -188,6 +188,7 @@ class IfcImporter:
self.diff = None
self.file = None
self.context_settings = []
self.gross_context_settings = []
self.contexts = []
self.project = None
self.has_existing_project = False
@@ -375,6 +376,15 @@ class IfcImporter:
settings.set_context_ids([context.id()])
self.context_settings.append(settings)
settings = ifcopenshell.geom.settings()
settings.set_deflection_tolerance(self.ifc_import_settings.deflection_tolerance)
settings.set_angular_tolerance(self.ifc_import_settings.angular_tolerance)
settings.set(settings.STRICT_TOLERANCE, True)
settings.set(settings.INCLUDE_CURVES, True)
settings.set(settings.DISABLE_OPENING_SUBTRACTIONS, True)
settings.set_context_ids([context.id()])
self.gross_context_settings.append(settings)
def process_element_filter(self):
offset = self.ifc_import_settings.element_offset
offset_limit = offset + self.ifc_import_settings.element_limit
@@ -418,6 +428,17 @@ class IfcImporter:
else:
self.spatial_elements = set(self.file.by_type("IfcSpatialElement"))
# Detect excessive voids
self.gross_elements = set(
filter(lambda e: len(getattr(e, "HasOpenings", [])) > self.ifc_import_settings.void_limit, self.elements)
)
self.elements = self.elements.difference(self.gross_elements)
if self.gross_elements:
print("Warning! Excessive voids were found and skipped for the following elements:")
for element in self.gross_elements:
print(element)
def get_spatial_elements_filtered_by_elements(self, elements):
leaf_spatial_elements = set([ifcopenshell.util.element.get_container(e) for e in elements])
results = set()
@@ -640,7 +661,7 @@ class IfcImporter:
if elements_checked > element_checking_threshold:
return
if element.ObjectPlacement and element.ObjectPlacement.is_a("IfcLocalPlacement"):
placement = ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement)[:,3][0:3]
placement = ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement)[:, 3][0:3]
if self.is_point_far_away(placement, is_meters=False):
return placement
if not self.does_element_likely_have_geometry_far_away(element):
@@ -816,6 +837,10 @@ class IfcImporter:
def create_elements(self):
self.create_generic_elements(self.elements)
tmp = self.context_settings
self.context_settings = self.gross_context_settings
self.create_generic_elements(self.gross_elements)
self.context_settings = tmp
def create_generic_shape(self, element):
for settings in self.context_settings:
@@ -1343,7 +1368,7 @@ class IfcImporter:
element_type = ifcopenshell.util.element.get_type(element)
if not element_type:
continue
merge_key = str(element_type.id()) + "-" + element_type.Name or "Unnamed"
merge_key = str(element_type.id()) + "-" + (element_type.Name or "Unnamed")
merge_set.setdefault(merge_key, []).append(obj)
id_set.setdefault(merge_key, []).append(ifc_definition_id)
self.merge_objects(merge_set, id_set)
@@ -1550,20 +1575,27 @@ class IfcImporter:
def create_aggregate_and_nest_collections(self):
if self.ifc_import_settings.has_filter:
rel_aggregates = [e.IsDecomposedBy[0] for e in self.elements if e.IsDecomposedBy]
rel_aggregates += [e.Decomposes[0] for e in self.elements if e.Decomposes]
rel_aggregates += [e.IsNestedBy[0] for e in self.elements if e.IsNestedBy]
rel_aggregates += [e.Nests[0] for e in self.elements if e.Nests]
rel_aggregates = set(rel_aggregates)
rel_aggregates = set()
for element in self.elements:
if element.IsDecomposedBy:
rel_aggregates.add(element.IsDecomposedBy[0])
elif element.Decomposes:
rel_aggregates.add(element.Decomposes[0])
elif getattr(element, "IsNestedBy", []): # IFC2X3 does not have IsNestedBy
if [e for e in element.IsNestedBy[0].RelatedObjects if not e.is_a("IfcPort")]:
rel_aggregates.add(element.IsNestedBy[0])
elif getattr(element, "Nests", []):
rel_aggregates.add(element.Nests[0])
else:
rel_aggregates = [
a
for a in self.file.by_type("IfcRelAggregates")
if a.RelatingObject.is_a("IfcElement") or a.RelatingObject.is_a("IfcElementType")
r
for r in self.file.by_type("IfcRelAggregates")
if r.RelatingObject.is_a("IfcElement") or r.RelatingObject.is_a("IfcElementType")
] + [
a
for a in self.file.by_type("IfcRelNests")
if a.RelatingObject.is_a("IfcElement") or a.RelatingObject.is_a("IfcElementType")
r
for r in self.file.by_type("IfcRelNests")
if (r.RelatingObject.is_a("IfcElement") or r.RelatingObject.is_a("IfcElementType"))
and [e for e in r.RelatedObjects if not e.is_a("IfcPort")]
]
if len(rel_aggregates) > 10000:
@@ -1668,7 +1700,7 @@ class IfcImporter:
elif getattr(element, "Decomposes", None):
aggregate = ifcopenshell.util.element.get_aggregate(element)
return self.collections[aggregate.GlobalId].objects.link(obj)
elif getattr(element, "Nests", None):
elif getattr(element, "Nests", None) and not element.is_a("IfcPort"):
nest = ifcopenshell.util.element.get_nest(element)
return self.collections[nest.GlobalId].objects.link(obj)
else:
@@ -1948,6 +1980,7 @@ class IfcImportSettings:
self.is_coordinating = True
self.deflection_tolerance = 0.001
self.angular_tolerance = 0.5
self.void_limit = 30
self.distance_limit = 1000
self.false_origin = None
self.element_offset = 0
@@ -1979,6 +2012,7 @@ class IfcImportSettings:
settings.is_coordinating = props.is_coordinating
settings.deflection_tolerance = props.deflection_tolerance
settings.angular_tolerance = props.angular_tolerance
settings.void_limit = props.void_limit
settings.distance_limit = props.distance_limit
settings.false_origin = [float(o) for o in props.false_origin.split(",")] if props.false_origin else None
if settings.false_origin == [0, 0, 0]:
@@ -20,14 +20,14 @@ import bpy
from . import ui, prop, operator
classes = (
operator.BIM_OT_assign_object,
operator.BIM_OT_unassign_object,
operator.BIM_OT_enable_editing_aggregate,
operator.BIM_OT_disable_editing_aggregate,
operator.BIM_OT_add_aggregate,
operator.BIM_OT_select_parts,
operator.BIM_OT_select_aggregate,
operator.BIM_OT_add_part_to_object,
operator.BIM_OT_aggregate_assign_object,
operator.BIM_OT_disable_editing_aggregate,
operator.BIM_OT_enable_editing_aggregate,
operator.BIM_OT_select_aggregate,
operator.BIM_OT_select_parts,
operator.BIM_OT_aggregate_unassign_object,
prop.BIMObjectAggregateProperties,
ui.BIM_PT_aggregate,
)
@@ -35,7 +35,7 @@ class AggregateData:
"has_relating_object": cls.has_relating_object(),
"relating_object_label": cls.get_relating_object_label(),
"has_related_objects": cls.has_related_objects(),
"related_objects_amount": cls.get_related_objects_amount(),
"total_parts": cls.total_parts(),
"ifc_class": cls.ifc_class(),
}
cls.is_loaded = True
@@ -59,7 +59,7 @@ class AggregateData:
return ifcopenshell.util.element.get_parts(tool.Ifc.get_entity(bpy.context.active_object))
@classmethod
def get_related_objects_amount(cls):
def total_parts(cls):
parts = cls.get_related_objects()
return len(parts) if parts else 0
@@ -33,11 +33,11 @@ class Operator:
return {"FINISHED"}
class BIM_OT_assign_object(bpy.types.Operator, Operator):
class BIM_OT_aggregate_assign_object(bpy.types.Operator, Operator):
"""Create aggregation relationship between two ifc elements"""
bl_idname = "bim.assign_object"
bl_label = "Assign Object"
bl_idname = "bim.aggregate_assign_object"
bl_label = "Assign Object To Aggregation"
bl_options = {"REGISTER", "UNDO"}
relating_object: bpy.props.IntProperty()
@@ -67,11 +67,11 @@ class BIM_OT_assign_object(bpy.types.Operator, Operator):
self.report({"ERROR"}, f" Cannot aggregate {obj.name} to {relating_obj.name}")
class BIM_OT_unassign_object(bpy.types.Operator, Operator):
class BIM_OT_aggregate_unassign_object(bpy.types.Operator, Operator):
"""Remove aggregation relationship between two ifc elements"""
bl_idname = "bim.unassign_object"
bl_label = "Unassign Object"
bl_idname = "bim.aggregate_unassign_object"
bl_label = "Unassign Object From Aggregation"
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
@@ -164,6 +164,7 @@ class BIM_OT_add_aggregate(bpy.types.Operator, tool.Ifc.Operator):
def create_aggregate(self, context, ifc_class):
aggregate = bpy.data.objects.new("Assembly", None)
aggregate.location = context.scene.cursor.location
bpy.ops.bim.assign_class(obj=aggregate.name, ifc_class=ifc_class)
return aggregate
@@ -54,7 +54,7 @@ class BIM_PT_aggregate(Panel):
row = layout.row(align=True)
row.prop(props, "relating_object", text="")
if props.relating_object:
op = row.operator("bim.assign_object", icon="CHECKMARK", text="")
op = row.operator("bim.aggregate_assign_object", icon="CHECKMARK", text="")
op.relating_object = props.relating_object.BIMObjectProperties.ifc_definition_id
row.operator("bim.disable_editing_aggregate", icon="CANCEL", text="")
else:
@@ -65,15 +65,21 @@ class BIM_PT_aggregate(Panel):
op.obj = context.active_object.name
row.operator("bim.enable_editing_aggregate", icon="GREASEPENCIL", text="")
row.operator("bim.add_aggregate", icon="ADD", text="")
op = row.operator("bim.unassign_object", icon="X", text="")
op = row.operator("bim.aggregate_unassign_object", icon="X", text="")
else:
row.label(text="No Aggregate", icon="TRIA_UP")
row.operator("bim.enable_editing_aggregate", icon="GREASEPENCIL", text="")
row.operator("bim.add_aggregate", icon="ADD", text="")
row = layout.row(align=True)
parts = AggregateData.data["related_objects_amount"]
row.label(text=f"{parts or 'No'} Part{'s' if parts > 1 else ''}", icon="TRIA_DOWN")
total_parts = AggregateData.data["total_parts"]
if total_parts == 0:
row.label(text="No Parts", icon="TRIA_DOWN")
elif total_parts == 1:
row.label(text="1 Part", icon="TRIA_DOWN")
else:
row.label(text=f"{total_parts} Parts", icon="TRIA_DOWN")
if AggregateData.data["has_related_objects"]:
op = row.operator("bim.select_parts", icon="RESTRICT_SELECT_OFF", text="")
op.obj = context.active_object.name
@@ -22,6 +22,7 @@ import logging
import shapely
import mathutils
import numpy as np
import multiprocessing
import ifcopenshell.api
import ifcopenshell.util.unit
import ifcopenshell.util.shape
@@ -30,7 +31,7 @@ import ifcopenshell.util.placement
import ifcopenshell.util.representation
import blenderbim.tool as tool
import blenderbim.bim.import_ifc as import_ifc
from math import pi, inf
from math import pi, inf, degrees, acos, radians
from mathutils import Vector, Matrix
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.module.model.decorator import ProfileDecorator
@@ -63,7 +64,8 @@ def disable_editing_boundary_geometry(context):
class Loader:
def __init__(self):
def __init__(self, operator=None):
self.operator = operator
self.ifc_file = None
self.logger = None
self.ifc_importer = None
@@ -85,14 +87,33 @@ class Loader:
tool.Loader.link_mesh(shape, mesh)
except RuntimeError:
# Fallback solution for invalid geometry provided by Revit. (InnerBoundaries cuting OuterBoundary)
print(f"Failed to create mesh from IfcRelSpaceBoundary with ID {boundary.id()}. Geometry might be invalid")
shape = ifcopenshell.geom.create_shape(self.fallback_settings, surface.OuterBoundary)
self.operator.report(
{"WARNING"},
(
f"IfcRelSpaceBoundary {boundary.id()} mesh creation failed. Geometry might be invalid. Using fallback solution"
),
)
try:
shape = ifcopenshell.geom.create_shape(self.fallback_settings, surface.OuterBoundary)
except RuntimeError:
self.operator.report(
{"WARNING"},
f"Skipping IfcRelSpaceBoundary {boundary.id()}. Fallback solution failed. Geometry might be invalid",
)
return None
mesh = bpy.data.meshes.new(str(surface.id()))
bm = bmesh.new()
verts = [bm.verts.new(shape.verts[i : i + 3]) for i in range(0, len(shape.verts), 3)]
bm.faces.new(verts)
for inner_boundary in surface.InnerBoundaries:
shape = ifcopenshell.geom.create_shape(self.fallback_settings, inner_boundary)
try:
shape = ifcopenshell.geom.create_shape(self.fallback_settings, inner_boundary)
except RuntimeError:
self.operator.report(
{"WARNING"},
f"Skipping an inner boundary for IfcRelSpaceBoundary with ID {boundary.id()}. Geometry might be invalid",
)
return None
verts = [bm.verts.new(shape.verts[i : i + 3]) for i in range(0, len(shape.verts), 3)]
for i in range(len(verts) - 1):
bm.edges.new(verts[i : i + 2])
@@ -103,7 +124,7 @@ class Loader:
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
matrix = mathutils.Matrix(
ifcopenshell.util.placement.get_axis2placement(surface.BasisSurface.Position).tolist()
)
)
matrix.translation *= unit_scale
mesh.transform(matrix)
return mesh
@@ -126,7 +147,15 @@ class Loader:
self.ifc_importer = import_ifc.IfcImporter(ifc_import_settings)
self.ifc_importer.file = self.ifc_file
def load_boundary(self, boundary, blender_space):
def load_boundary(self, boundary, blender_space=None):
if not blender_space:
if not boundary.RelatingSpace:
self.operator.report(
{"WARNING"},
f"Skipping IfcRelSpaceBoundary {boundary.id()} which have no relating space. Invalid boundary.",
)
return
blender_space = tool.Ifc.get_object(boundary.RelatingSpace)
obj = tool.Ifc.get_object(boundary)
if obj:
return obj
@@ -145,9 +174,9 @@ class LoadProjectSpaceBoundaries(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
loader = Loader()
loader = Loader(self)
for rel in tool.Ifc.get().by_type("IfcRelSpaceBoundary"):
loader.load_boundary(rel, tool.Ifc.get_object(rel.RelatingSpace))
loader.load_boundary(rel)
return {"FINISHED"}
@@ -158,7 +187,7 @@ class LoadBoundary(bpy.types.Operator):
boundary_id: bpy.props.IntProperty()
def execute(self, context):
loader = Loader()
loader = Loader(self)
for obj in context.visible_objects:
obj.select_set(False)
obj = loader.load_boundary(tool.Ifc.get().by_id(self.boundary_id), context.active_object)
@@ -173,7 +202,7 @@ class LoadSpaceBoundaries(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
loader = Loader()
loader = Loader(self)
element = tool.Ifc.get_entity(context.active_object)
for rel in element.BoundedBy or []:
loader.load_boundary(rel, context.active_object)
@@ -459,7 +488,7 @@ class EditBoundaryGeometry(bpy.types.Operator, tool.Ifc.Operator):
ifcopenshell.util.element.remove_deep2(tool.Ifc.get(), old_surface)
old_mesh = obj.data
loader = Loader()
loader = Loader(self)
obj.data = loader.create_mesh(element)
tool.Geometry.delete_data(old_mesh)
@@ -484,7 +513,7 @@ class ShowBoundaries(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context):
props = bpy.context.scene.BIMBoundaryProperties
loader = Loader()
loader = Loader(self)
for obj in context.selected_objects:
element = tool.Ifc.get_entity(obj)
if not element or not getattr(element, "BoundedBy", None):
@@ -538,6 +567,7 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
related_building_element = None
relating_space_obj = None
related_building_element_obj = None
parent_boundaries = []
objs = context.selected_objects
if len(objs) == 2:
@@ -552,8 +582,19 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
else:
related_building_element = element
related_building_element_obj = obj
parent_boundary = self.create_element_boundary(
context, relating_space, relating_space_obj, related_building_element, related_building_element_obj
)
if parent_boundary:
parent_boundaries.append(parent_boundary)
elif len(objs) == 1:
# New prototype, old code not yet removed. Still testing.
space = tool.Ifc.get_entity(objs[0])
if space.is_a("IfcSpace"):
self.auto_generate_boundaries(space, objs[0])
elif len(objs) == 1:
# Optionally the user may select just the space, and the building element shall be auto-detected
# TODO : refactor to be able to generate all boundaries for selected space automatically or with an option
def msg(self, context):
self.layout.label(text="NO ACTIVE STOREY")
@@ -563,8 +604,6 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
relating_space = element
relating_space_obj = objs[0]
target = bpy.context.scene.cursor.location
collection = context.view_layer.active_layer_collection.collection
collection_obj = bpy.data.objects.get(collection.name)
if not collection_obj:
@@ -576,19 +615,258 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
return
for subelement in ifcopenshell.util.element.get_decomposition(spatial_element):
if not (subelement.is_a("IfcWall") or subelement.is_a("IfcSlab")):
if not (
subelement.is_a("IfcWall") or subelement.is_a("IfcSlab") or subelement.is_a("IfcVirtualElement")
):
continue
obj = tool.Ifc.get_object(subelement)
if obj:
raycast = obj.closest_point_on_mesh(obj.matrix_world.inverted() @ target, distance=0.1)
if raycast[0]:
related_building_element = subelement
related_building_element_obj = obj
break
related_building_element = subelement
related_building_element_obj = obj
parent_boundary = self.create_element_boundary(
context,
relating_space,
relating_space_obj,
related_building_element,
related_building_element_obj,
)
if parent_boundary:
parent_boundaries.append(parent_boundary)
bpy.ops.bim.show_boundaries()
for parent_boundary in parent_boundaries:
obj = tool.Ifc.get_object(parent_boundary)
obj.select_set(True)
def auto_generate_boundaries(self, space, space_obj):
# Identify all potential building elements
# TODO: don't select everything, use AABB culling in Blender
building_elements = (
tool.Ifc.get().by_type("IfcWall")
+ tool.Ifc.get().by_type("IfcSlab")
+ tool.Ifc.get().by_type("IfcVirtualElement")
)
# Don't generate boundaries of building elements that we've already got bounaries for.
for boundary in space.BoundedBy:
if boundary.RelatedBuildingElement in building_elements:
building_elements.remove(boundary.RelatedBuildingElement)
# Create tree of gross shapes of all potential related building elements
include = building_elements + [space]
tree = ifcopenshell.geom.tree()
shapes = {}
settings = ifcopenshell.geom.settings()
settings.set(settings.STRICT_TOLERANCE, True)
settings.set(settings.DISABLE_OPENING_SUBTRACTIONS, True)
iterator = ifcopenshell.geom.iterator(settings, tool.Ifc.get(), multiprocessing.cpu_count(), include=include)
if iterator.initialize():
while True:
tree.add_element(iterator.get_native())
shape = iterator.get()
shapes[shape.id] = shape
if not iterator.next():
break
# Spatially query all potential boundary elements via a 100mm extension of the space
building_elements = [e for e in tree.select(space, extend=0.1) if e != space]
if not building_elements:
return
# Create a dissolved bmesh for the space
space_bm = bmesh.new()
space_bm.from_mesh(space_obj.data)
bmesh.ops.dissolve_limit(space_bm, angle_limit=pi * 2 / 360, verts=space_bm.verts, edges=space_bm.edges)
# Create dissolved bmeshes for all boundary elements
building_element_bms = {}
for building_element in building_elements:
bm = bmesh.new()
shape = shapes[building_element.id()]
verts = ifcopenshell.util.shape.get_vertices(shape.geometry)
for vert in verts:
bm.verts.new(Vector(vert))
bm.verts.ensure_lookup_table()
faces = ifcopenshell.util.shape.get_faces(shape.geometry)
for face in faces:
bm.faces.new([bm.verts[i] for i in face])
bm.verts.ensure_lookup_table()
bm.faces.ensure_lookup_table()
bm.normal_update() # Needed so that dissolve_limit will work.
bmesh.ops.dissolve_limit(bm, angle_limit=radians(1), verts=bm.verts[:], edges=bm.edges[:])
bm.verts.ensure_lookup_table()
bm.faces.ensure_lookup_table()
building_element_bms[building_element.id()] = bm
# Compare space faces and building element faces to see if they relate to one another
for space_face in space_bm.faces:
space_face_normal = space_obj.matrix_world.to_3x3() @ space_face.normal
space_face_vert = space_obj.matrix_world @ space_face.verts[0].co
for building_element in building_elements:
for face in building_element_bms[building_element.id()].faces:
building_obj = tool.Ifc.get_object(building_element)
face_normal = building_obj.matrix_world.to_3x3() @ face.normal
angle = degrees(acos(max(min(space_face_normal.dot(face_normal), 1), -1)))
if tool.Cad.is_x(angle, 180, tolerance=2):
pass # Faces need to be parallel and have opposite normals to be related.
elif building_element.is_a("IfcVirtualElement") and tool.Cad.is_x(angle, 0, tolerance=2):
pass # Virtual elements only need to be parallel to be related, since they are planes.
else:
continue
# Both faces should be close to one another. Say within 50mm.
space_vert = building_obj.matrix_world.inverted() @ space_face_vert
dist = mathutils.geometry.distance_point_to_plane(space_vert, face.verts[0].co, face.normal)
if abs(dist) > 0.05:
continue
# Project the building element face onto the space face
space_face_verts = [v.co.copy() for v in space_face.verts]
space_face_matrix = self.get_face_matrix(*[v.copy() for v in space_face_verts[0:3]])
space_face_matrix_i = space_face_matrix.inverted()
space_face_polygon = shapely.Polygon(
[tuple((space_face_matrix_i @ v).xy) for v in space_face_verts]
)
space_matrix_world_i = space_obj.matrix_world.inverted()
face_verts = [space_matrix_world_i @ building_obj.matrix_world @ v.co.copy() for v in face.verts]
face_polygon = shapely.Polygon([tuple((space_face_matrix_i @ v).xy) for v in face_verts])
gross_boundary_polygon = space_face_polygon.intersection(face_polygon)
if type(gross_boundary_polygon) == shapely.GeometryCollection:
for geom in gross_boundary_polygon.geoms:
if type(geom) == shapely.Polygon:
gross_boundary_polygon = geom
break
if (
not (isinstance(gross_boundary_polygon, shapely.Polygon) and gross_boundary_polygon.is_valid)
or gross_boundary_polygon.is_empty
):
continue
# The gross boundary polygon may not be a true gross boundary since it
# may have openings already removed, such as in IFC4 Reference View. So
# we cheat by using the exterior boundary to mean "gross".
exterior_boundary_polygon = shapely.Polygon(gross_boundary_polygon.exterior.coords)
parent_boundary = tool.Ifc.run(
"root.create_entity", ifc_class=bpy.context.scene.BIMModelProperties.boundary_class
)
if building_element.is_a("IfcVirtualElement"):
parent_boundary.PhysicalOrVirtualBoundary = "VIRTUAL"
else:
parent_boundary.PhysicalOrVirtualBoundary = "PHYSICAL"
parent_boundary.InternalOrExternalBoundary = "NOTDEFINED"
if building_element.is_a("IfcWall"):
is_external = ifcopenshell.util.element.get_pset(
building_element, "Pset_WallCommon", "IsExternal"
)
if is_external is True:
parent_boundary.InternalOrExternalBoundary = "EXTERNAL"
elif is_external is False:
parent_boundary.InternalOrExternalBoundary = "INTERNAL"
elif building_element.is_a("IfcSlab"):
predefined_type = ifcopenshell.util.element.get_predefined_type(building_element)
if predefined_type == "BASESLAB":
parent_boundary.InternalOrExternalBoundary = "EXTERNAL_EARTH"
else:
is_external = ifcopenshell.util.element.get_pset(
building_element, "Pset_SlabCommon", "IsExternal"
)
if is_external is True:
parent_boundary.InternalOrExternalBoundary = "EXTERNAL"
elif is_external is False:
parent_boundary.InternalOrExternalBoundary = "INTERNAL"
parent_boundary.RelatingSpace = space
parent_boundary.RelatedBuildingElement = building_element
parent_boundary.ConnectionGeometry = self.create_connection_geometry_from_polygon(
exterior_boundary_polygon, space_face_matrix
)
self.set_boundary_name(parent_boundary)
for rel in getattr(building_element, "HasOpenings", []):
opening = rel.RelatedOpeningElement
filling = opening.HasFillings[0].RelatedBuildingElement if opening.HasFillings else None
# Create shape of opening as a dissolved BMesh
settings = ifcopenshell.geom.settings()
settings.set(settings.STRICT_TOLERANCE, True)
shape = ifcopenshell.geom.create_shape(settings, opening)
m = shape.transformation.matrix.data
mat = Matrix(
(
[m[0], m[3], m[6], m[9]],
[m[1], m[4], m[7], m[10]],
[m[2], m[5], m[8], m[11]],
[0, 0, 0, 1],
)
)
opening_bm = bmesh.new()
verts = ifcopenshell.util.shape.get_vertices(shape.geometry)
for vert in verts:
opening_bm.verts.new(Vector(vert))
opening_bm.verts.ensure_lookup_table()
faces = ifcopenshell.util.shape.get_faces(shape.geometry)
for face in faces:
opening_bm.faces.new([opening_bm.verts[i] for i in face])
opening_bm.verts.ensure_lookup_table()
opening_bm.faces.ensure_lookup_table()
opening_bm.normal_update() # Needed so that dissolve_limit will work.
bmesh.ops.dissolve_limit(
opening_bm, angle_limit=radians(1), verts=opening_bm.verts[:], edges=opening_bm.edges[:]
)
opening_bm.verts.ensure_lookup_table()
opening_bm.faces.ensure_lookup_table()
# Get relevant faces of BMesh that can turn into boundaries
opening_polygons = []
for opening_face in opening_bm.faces:
opening_face_normal = mat.to_3x3() @ opening_face.normal
angle = degrees(acos(max(min(opening_face_normal.dot(face_normal), 1), -1)))
if not tool.Cad.is_x(angle, 180, tolerance=2):
continue # Any non-parallel faces are not relevant
opening_face_verts = [space_matrix_world_i @ mat @ v.co.copy() for v in opening_face.verts]
polygon = shapely.Polygon([tuple((space_face_matrix_i @ v).xy) for v in opening_face_verts])
opening_polygons.append(polygon)
# Merge them all into a single opening polygon for our boundary
opening_polygon = shapely.ops.unary_union(opening_polygons)
# Only openings that are projected onto our exterior boundary are relevant.
if opening_polygon.intersection(exterior_boundary_polygon).area == 0:
continue
boundary = tool.Ifc.run(
"root.create_entity", ifc_class=bpy.context.scene.BIMModelProperties.boundary_class
)
boundary.RelatingSpace = space
boundary.RelatedBuildingElement = filling or opening
boundary.ConnectionGeometry = self.create_connection_geometry_from_polygon(
opening_polygon, space_face_matrix
)
if filling:
boundary.PhysicalOrVirtualBoundary = "PHYSICAL"
else:
boundary.PhysicalOrVirtualBoundary = "VIRTUAL"
boundary.InternalOrExternalBoundary = parent_boundary.InternalOrExternalBoundary
if boundary.is_a() != "IfcRelSpaceBoundary":
boundary.ParentBoundary = parent_boundary
self.set_boundary_name(boundary)
def create_element_boundary(
self, context, relating_space, relating_space_obj, related_building_element, related_building_element_obj
):
if not relating_space or not related_building_element:
return
# Find which face on space should be bounded to related building element
# TODO: Handle round wall where multiple faces need to be bound to the same related building element
bm = bmesh.new()
bm.from_mesh(relating_space_obj.data)
bmesh.ops.dissolve_limit(bm, angle_limit=pi * 2 / 360, verts=bm.verts, edges=bm.edges)
@@ -609,8 +887,6 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
if not target_face:
return
parent_boundary = tool.Ifc.run("root.create_entity", ifc_class=context.scene.BIMModelProperties.boundary_class)
# Is this right? Or should I use loop?
target_face_verts = [v.co.copy() for v in target_face.verts]
target_face_matrix = self.get_face_matrix(*[v.copy() for v in target_face_verts[0:3]])
@@ -630,6 +906,18 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
gross_boundary_polygon = geom
break
if (
not (isinstance(gross_boundary_polygon, shapely.Polygon) and gross_boundary_polygon.is_valid)
or gross_boundary_polygon.is_empty
):
return
parent_boundary = tool.Ifc.run("root.create_entity", ifc_class=context.scene.BIMModelProperties.boundary_class)
parent_boundary.PhysicalOrVirtualBoundary = "PHYSICAL"
# Set to EXTERNAL by default and turn later to internal if there is a corresponding boundary relating to an
# internal space
parent_boundary.InternalOrExternalBoundary = "EXTERNAL"
# The gross boundary polygon may not be a true gross boundary since it
# may have openings already removed, such as in IFC4 Reference View. So
# we cheat by using the exterior boundary to mean "gross". Later, we
@@ -637,19 +925,25 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
# space.
exterior_boundary_polygon = shapely.Polygon(gross_boundary_polygon.exterior.coords)
net_boundary_polygon = shapely.Polygon(gross_boundary_polygon)
inner_boundaries = []
for rel in getattr(related_building_element, "HasOpenings", []):
opening = rel.RelatedOpeningElement
filling = None
if opening.HasFillings:
filling = opening.HasFillings[0].RelatedBuildingElement
if not opening.HasFillings:
continue
# TODO: HasFilling is a zero to many relationship. How to handle many ?
filling = opening.HasFillings[0].RelatedBuildingElement
opening_polygon = self.get_flattened_polygon(opening, relating_space_obj, target_face_matrix_i)
net_boundary_polygon = net_boundary_polygon.difference(opening_polygon)
# An inner boundary is supposed to overlap its parent boundary according to IFC4 documentation
# so we extend our exterior boundary with all opening which has a filling
# Also see Implementation aggreement SB 1.1 for IFC 2x3 TC1 Space Boundary Addon View
# https://standards.buildingsmart.org/MVD/RELEASE/IFC2x3/TC1/SB1_1/IFC%20Space%20Boundary%20Implementation%20Agreement%20Addendum%202010-03-22.pdf
unionised_object = exterior_boundary_polygon.union(opening_polygon)
if isinstance(unionised_object, shapely.Polygon):
exterior_boundary_polygon = unionised_object
# Only openings that are projected onto our exterior boundary are relevant.
if opening_polygon.intersection(exterior_boundary_polygon).area == 0:
continue
@@ -657,24 +951,25 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
connection_geometry = self.create_connection_geometry_from_polygon(opening_polygon, target_face_matrix)
boundary = tool.Ifc.run("root.create_entity", ifc_class=context.scene.BIMModelProperties.boundary_class)
boundary.RelatingSpace = relating_space
boundary.RelatedBuildingElement = filling or related_building_element
boundary.RelatedBuildingElement = filling
boundary.ConnectionGeometry = connection_geometry
boundary.PhysicalOrVirtualBoundary = "PHYSICAL" if filling else "VIRTUAL"
boundary.InternalOrExternalBoundary = "INTERNAL"
if related_building_element.is_a("IfcVirtualElement"):
boundary.PhysicalOrVirtualBoundary = "VIRTUAL"
else:
boundary.PhysicalOrVirtualBoundary = "PHYSICAL"
boundary.InternalOrExternalBoundary = parent_boundary.InternalOrExternalBoundary
self.set_boundary_name(boundary)
if boundary.is_a("IfcRelSpaceBoundary2ndLevel"):
boundary.ParentBoundary = parent_boundary
connection_geometry = self.create_connection_geometry_from_polygon(net_boundary_polygon, target_face_matrix)
connection_geometry = self.create_connection_geometry_from_polygon(
exterior_boundary_polygon, target_face_matrix
)
parent_boundary.RelatingSpace = relating_space
parent_boundary.RelatedBuildingElement = related_building_element
parent_boundary.ConnectionGeometry = connection_geometry
parent_boundary.PhysicalOrVirtualBoundary = "PHYSICAL"
parent_boundary.InternalOrExternalBoundary = "INTERNAL"
bpy.ops.bim.show_boundaries()
obj = tool.Ifc.get_object(parent_boundary)
obj.select_set(True)
self.set_boundary_name(parent_boundary)
return parent_boundary
def get_face_matrix(self, p1, p2, p3):
edge1 = p2 - p1
@@ -729,11 +1024,13 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
tool.Model.unit_scale = self.unit_scale
surface = tool.Ifc.get().createIfcCurveBoundedPlane()
surface.BasisSurface = tool.Ifc.get().createIfcPlane(tool.Ifc.get().createIfcAxis2Placement3D(
tool.Ifc.get().createIfcCartesianPoint([o / self.unit_scale for o in p1]),
tool.Ifc.get().createIfcDirection([float(o) for o in z_axis]),
tool.Ifc.get().createIfcDirection([float(o) for o in x_axis]),
))
surface.BasisSurface = tool.Ifc.get().createIfcPlane(
tool.Ifc.get().createIfcAxis2Placement3D(
tool.Ifc.get().createIfcCartesianPoint([o / self.unit_scale for o in p1]),
tool.Ifc.get().createIfcDirection([float(o) for o in z_axis]),
tool.Ifc.get().createIfcDirection([float(o) for o in x_axis]),
)
)
if tool.Ifc.get().schema != "IFC2X3":
points = [tool.Model.convert_si_to_unit(list(co)) for co in polygon.exterior.coords]
@@ -746,9 +1043,24 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
point_list = tool.Ifc.get().createIfcCartesianPointList2D(points)
inner_boundaries.append(tool.Ifc.get().createIfcIndexedPolyCurve(point_list, None, False))
else:
pass # TODO
pass # TODO
surface.OuterBoundary = outer_boundary
surface.InnerBoundaries = inner_boundaries
return surface
def set_boundary_name(self, boundary):
"""
By convention 1stLevel and 2ndLevel boundary have specific name and description
See https://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcRelSpaceBoundary.htm
Warning: IfcRelSpaceBoundary2ndLevel is a subclass of IfcRelSpaceBoundary1stLevel test order matter
"""
if boundary.is_a("IfcRelSpaceBoundary2ndLevel"):
boundary.Name = "2ndLevel"
if boundary.CorrespondingBoundary:
boundary.Description = "2a"
else:
boundary.Description = "2b"
elif boundary.is_a("IfcRelSpaceBoundary1stLevel"):
boundary.Name = "1stLevel"
@@ -22,8 +22,8 @@ from . import ui, prop, operator
classes = (
operator.GetBSDDClassificationProperties,
operator.LoadBSDDDomains,
operator.SearchBSDDClassifications,
operator.SetActiveBSDDDomain,
operator.SearchBSDDClass,
operator.SetActiveBSDDDictionary,
prop.BSDDDomain,
prop.BSDDClassification,
prop.BSDDPset,
@@ -16,73 +16,42 @@
# You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import os
import bpy
import bsdd
import json
import ifcopenshell
import blenderbim.tool as tool
from blenderbim.core import bsdd as core
class LoadBSDDDomains(bpy.types.Operator):
bl_idname = "bim.load_bsdd_domains"
bl_label = "Load bSDD Domains"
bl_label = "Load bSDD Dictionaries"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
props = context.scene.BIMBSDDProperties
props.domains.clear()
client = bsdd.Client()
for domain in sorted(client.Domain(), key=lambda x: x["name"]):
new = props.domains.add()
new.name = domain["name"]
new.namespace_uri = domain["namespaceUri"]
new.default_language_code = domain["defaultLanguageCode"]
new.organization_name_owner = domain["organizationNameOwner"]
new.status = domain["status"]
new.version = domain["version"]
core.load_bsdd(bsdd.Client(), tool.Bsdd)
return {"FINISHED"}
class SetActiveBSDDDomain(bpy.types.Operator):
class SetActiveBSDDDictionary(bpy.types.Operator):
bl_idname = "bim.set_active_bsdd_domain"
bl_label = "Load bSDD Domains"
bl_label = "Load bSDD Dictionary"
bl_options = {"REGISTER", "UNDO"}
name: bpy.props.StringProperty()
uri: bpy.props.StringProperty()
def execute(self, context):
props = context.scene.BIMBSDDProperties
props.active_domain = self.name
props.active_uri = self.uri
core.set_active_bsdd_dictionary(self.name, self.uri, tool.Bsdd)
return {"FINISHED"}
class SearchBSDDClassifications(bpy.types.Operator):
class SearchBSDDClass(bpy.types.Operator):
bl_idname = "bim.search_bsdd_classifications"
bl_label = "Search bSDD Classifications"
bl_label = "Search bSDD Class"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
props = context.scene.BIMBSDDProperties
props.classifications.clear()
client = bsdd.Client()
related_ifc_entities = []
if len(props.keyword) < 3:
return {"FINISHED"}
if props.should_filter_ifc_class and context.active_object:
element = tool.Ifc.get_entity(context.active_object)
if element:
related_ifc_entities = [element.is_a()]
results = client.ClassificationSearchOpen(props.keyword, DomainNamespaceUris=[props.active_uri], RelatedIfcEntities=related_ifc_entities)
for result in sorted(results["classifications"], key=lambda x: x["referenceCode"]):
new = props.classifications.add()
new.name = result["name"]
new.reference_code = result["referenceCode"]
new.description = result.get("description", "")
new.namespace_uri = result["namespaceUri"]
new.domain_name = result["domainName"]
new.domain_namespace_uri = result["domainNamespaceUri"]
keyword = context.scene.BIMBSDDProperties.keyword
core.search_class(keyword, bsdd.Client(), tool.Bsdd)
return {"FINISHED"}
@@ -92,41 +61,5 @@ class GetBSDDClassificationProperties(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
bprops = context.scene.BIMBSDDProperties
bprops.classification_psets.clear()
bsdd_classification = bprops.classifications[bprops.active_classification_index]
client = bsdd.Client()
data = client.Classification(bsdd_classification.namespace_uri)
properties = data.get("classificationProperties", None)
if not properties:
return {"FINISHED"}
psets = {}
for prop in properties:
if prop.get("propertyDomainName") != "IFC":
continue
pset = prop.get("propertySet", None)
if not pset:
continue
psets.setdefault(pset, {})
predefined_value = prop.get("predefinedValue")
if predefined_value:
possible_values = [predefined_value]
else:
possible_values = prop.get("possibleValues", []) or []
possible_values = [v["value"] for v in possible_values]
psets[pset][prop["name"]] = possible_values
for pset_name, pset in psets.items():
new = bprops.classification_psets.add()
new.name = pset_name
for name, values in pset.items():
new2 = new.properties.add()
new2.name = name
new2.enum_items = json.dumps(values)
new2.data_type = "enum"
core.get_class_properties(bsdd.Client(), tool.Bsdd)
return {"FINISHED"}
@@ -33,7 +33,7 @@ from bpy.props import (
class BSDDDomain(PropertyGroup):
name: StringProperty(name="Name")
namespace_uri: StringProperty(name="URI")
uri: StringProperty(name="URI")
default_language_code: StringProperty(name="Language")
organization_name_owner: StringProperty(name="Organization")
status: StringProperty(name="Status")
@@ -44,7 +44,7 @@ class BSDDClassification(PropertyGroup):
name: StringProperty(name="Name")
reference_code: StringProperty(name="Reference Code")
description: StringProperty(name="Description")
namespace_uri: StringProperty(name="Namespace URI")
uri: StringProperty(name="Namespace URI")
domain_name: StringProperty(name="Domain Name")
domain_namespace_uri: StringProperty(name="Domain Namespace URI")
@@ -63,4 +63,5 @@ class BIMBSDDProperties(PropertyGroup):
active_classification_index: IntProperty(name="Active Classification Index")
keyword: StringProperty(name="Keyword")
should_filter_ifc_class: BoolProperty(name="Filter Active IFC Class", default=True)
load_preview_domains: BoolProperty(name="Load Preview Domains", default=False)
classification_psets: CollectionProperty(name="Classification Psets", type=BSDDPset)
@@ -32,6 +32,11 @@ class BIM_PT_bsdd(Panel):
def draw(self, context):
props = context.scene.BIMBSDDProperties
row = self.layout.row(align=True)
row.prop(props, "load_preview_domains")
if len(props.domains):
row.operator("bim.load_bsdd_domains", text="", icon="FILE_REFRESH")
if props.active_domain:
row = self.layout.row()
row.label(text="Active: " + props.active_domain, icon="URL")
@@ -57,10 +62,13 @@ class BIM_UL_bsdd_domains(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
if item:
row = layout.row(align=True)
row.label(text=f"{item.name} ({item.organization_name_owner})")
if item.status != "Active":
row.label(text=f"{item.name} ({item.organization_name_owner}) - {item.status}", icon="ERROR")
else:
row.label(text=f"{item.name} ({item.organization_name_owner})")
op = row.operator("bim.set_active_bsdd_domain", text="", icon="RESTRICT_SELECT_OFF")
op.name = item.name
op.uri = item.namespace_uri
op.uri = item.uri
class BIM_UL_bsdd_classifications(UIList):
@@ -20,26 +20,33 @@ import bpy
from . import ui, prop, operator
classes = (
operator.AddClashSet,
operator.AddClashSource,
operator.ExecuteIfcClash,
operator.ExportClashSets,
operator.ImportClashSets,
operator.AddClashSet,
operator.LoadIfcClashes,
operator.SaveIfcClashes,
operator.LoadSmartGroupsForActiveClashSet,
operator.RemoveClashSet,
operator.AddClashSource,
operator.RemoveClashSource,
operator.SelectClashSource,
operator.ExecuteIfcClash,
operator.SelectIfcClashResults,
operator.SelectClash,
operator.SelectClashResults,
operator.SelectClashSource,
operator.SelectIfcClashResults,
operator.SelectSmartGroup,
operator.SelectSmartGroupedClashesPath,
operator.SmartClashGroup,
operator.SelectSmartGroup,
operator.LoadSmartGroupsForActiveClashSet,
prop.Clash,
prop.ClashSource,
prop.ClashSet,
prop.SmartClashGroup,
prop.BIMClashProperties,
ui.BIM_PT_ifcclash,
ui.BIM_PT_clash_manager,
ui.BIM_PT_dumb_clash_manager,
ui.BIM_PT_smart_clash_manager,
ui.BIM_UL_clashes,
ui.BIM_UL_clash_sets,
ui.BIM_UL_smart_groups,
)
@@ -26,6 +26,7 @@ import ifcopenshell
from mathutils import Matrix, Vector
from math import radians
from blenderbim.bim.ifc import IfcStore
import blenderbim.tool as tool
class ExportClashSets(bpy.types.Operator):
@@ -245,12 +246,14 @@ class ExecuteIfcClash(bpy.types.Operator):
y = Vector([y.x, y.y, y.z])
x = y.cross(z)
mat = Matrix([
[x[0], y[0], z[0], p.x],
[x[1], y[1], z[1], p.y],
[x[2], y[2], z[2], p.z],
[0, 0, 0, 0],
])
mat = Matrix(
[
[x[0], y[0], z[0], p.x],
[x[1], y[1], z[1], p.y],
[x[2], y[2], z[2], p.z],
[0, 0, 0, 0],
]
)
camera.matrix_world = mat
context.scene.camera = camera
@@ -349,6 +352,119 @@ class SelectIfcClashResults(bpy.types.Operator):
return {"FINISHED"}
class LoadIfcClashes(bpy.types.Operator):
bl_idname = "bim.load_ifc_clashes"
bl_label = "Load IFC Clashes"
bl_options = {"REGISTER", "UNDO"}
bl_description = "Load the clashing IFC geometry stored in a file"
filename_ext = ".json"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def invoke(self, context, event):
self.filepath = bpy.path.ensure_ext(bpy.data.filepath, ".json")
WindowManager = context.window_manager
WindowManager.fileselect_add(self)
return {"RUNNING_MODAL"}
def execute(self, context):
self.file = IfcStore.get_file()
self.filepath = bpy.path.ensure_ext(self.filepath, ".json")
with open(self.filepath) as f:
clash_sets_json = json.load(f)
active_clash_set = context.scene.BIMClashProperties.active_clash_set
for clash_set in clash_sets_json:
if not "clashes" in clash_set.keys():
self.report({"WARNING"}, "No clashes found for the selected Clash Set.")
return {"CANCELLED"}
active_clash_set.clashes.clear()
for clash in clash_set["clashes"].values():
blender_clash = active_clash_set.clashes.add()
blender_clash.a_global_id = clash["a_global_id"]
blender_clash.b_global_id = clash["b_global_id"]
blender_clash.a_name = "{}/{}".format(clash["a_ifc_class"], clash["a_name"])
blender_clash.b_name = "{}/{}".format(clash["b_ifc_class"], clash["b_name"])
blender_clash.status = False if not "status" in clash.keys() else clash["status"]
return {"FINISHED"}
class SaveIfcClashes(bpy.types.Operator):
bl_idname = "bim.save_ifc_clashes"
bl_label = "Save IFC Clashes"
bl_options = {"REGISTER", "UNDO"}
bl_description = "Save the clashing IFC geometry stored in a file"
filename_ext = ".json"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def invoke(self, context, event):
self.filepath = bpy.path.ensure_ext(bpy.data.filepath, ".json")
WindowManager = context.window_manager
WindowManager.fileselect_add(self)
return {"RUNNING_MODAL"}
def execute(self, context):
self.file = IfcStore.get_file()
self.filepath = bpy.path.ensure_ext(self.filepath, ".json")
clash_sets_json = self.load_json()
active_clash_set = context.scene.BIMClashProperties.active_clash_set
self.update_clash_sets(clash_sets_json, active_clash_set)
self.save_json(clash_sets_json)
return {"FINISHED"}
def load_json(self):
with open(self.filepath) as f:
return json.load(f)
def update_clash_sets(self, clash_sets_json, active_clash_set):
for clash_set in clash_sets_json:
if clash_set["name"] != active_clash_set.name or "clashes" not in clash_set.keys():
continue
for clash in active_clash_set.clashes:
clash_key = clash.a_global_id + "-" + clash.b_global_id
if clash_set.get("clashes", {}).get(clash_key, None) is not None:
clash_set["clashes"][clash_key]["status"] = clash.status
def save_json(self, clash_sets_json):
with open(self.filepath, "w") as destination:
json.dump(clash_sets_json, destination, indent=4)
class SelectClash(bpy.types.Operator):
bl_idname = "bim.select_clash"
bl_label = "Select Clash"
bl_options = {"REGISTER", "UNDO"}
bl_description = "Select the clashing IFC geometry stored in a file"
index: bpy.props.IntProperty()
def execute(self, context):
clash = context.scene.BIMClashProperties.active_clash
products = []
linked_objects = []
try:
products.append(tool.Ifc.get().by_guid(clash.a_global_id))
except:
linked_objects.append(clash.a_name)
try:
products.append(tool.Ifc.get().by_guid(clash.b_global_id))
except:
linked_objects.append(clash.b_name)
tool.Spatial.select_products(products, unhide=True)
print(linked_objects)
for obj_name in linked_objects:
obj = bpy.data.objects.get(obj_name)
if obj:
obj.select_set(True)
else:
print("Object not found:", obj_name)
if context.scene.BIMClashProperties.sould_focus_on_clash:
context_override = tool.Blender.get_viewport_context()
with bpy.context.temp_override(**context_override):
bpy.ops.view3d.view_selected()
return {"FINISHED"}
class SmartClashGroup(bpy.types.Operator):
bl_idname = "bim.smart_clash_group"
bl_label = "Smart Group Clashes"
@@ -433,9 +549,9 @@ class LoadSmartGroupsForActiveClashSet(bpy.types.Operator):
new_group = context.scene.BIMClashProperties.smart_clash_groups.add()
new_group.number = f"{smart_group}"
for pair in global_id_pairs:
for id in pair:
for guid in pair:
new_global_id = new_group.global_ids.add()
new_global_id.name = id
new_global_id.guid = guid
return {"FINISHED"}
@@ -450,20 +566,16 @@ class SelectSmartGroup(bpy.types.Operator):
return IfcStore.get_file() and context.visible_objects and context.scene.BIMClashProperties.active_smart_group
def execute(self, context):
self.file = IfcStore.get_file()
# Select smart group in view
selected_smart_group = context.scene.BIMClashProperties.active_smart_group
# print(selected_smart_group.number)
for obj in context.visible_objects:
if not obj.BIMObjectProperties.ifc_definition_id:
products = []
for global_id in selected_smart_group.global_ids:
try:
products.append(tool.Ifc.get().by_guid(global_id.guid))
except:
continue
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
for id in selected_smart_group.global_ids:
# print("Id: ", id)
# print("Global id: ", element.GlobalId)
if element.GlobalId in id.name:
# print("object match: ", global_id)
obj.select_set(True)
tool.Spatial.select_products(products, unhide=True)
context_override = tool.Blender.get_viewport_context()
with bpy.context.temp_override(**context_override):
bpy.ops.view3d.view_selected()
return {"FINISHED"}
@@ -43,11 +43,20 @@ class ClashSource(PropertyGroup):
)
class Clash(PropertyGroup):
a_global_id: StringProperty(name="A")
b_global_id: StringProperty(name="B")
a_name: StringProperty(name="A Name")
b_name: StringProperty(name="B Name")
status: BoolProperty(name="Status", default=False)
class ClashSet(PropertyGroup):
name: StringProperty(name="Name")
tolerance: FloatProperty(name="Tolerance")
a: CollectionProperty(name="Group A", type=ClashSource)
b: CollectionProperty(name="Group B", type=ClashSource)
clashes: CollectionProperty(name="Clashes", type=Clash)
class SmartClashGroup(PropertyGroup):
@@ -63,11 +72,13 @@ class BIMClashProperties(PropertyGroup):
clash_results_path: StringProperty(name="Clash Results Path")
smart_grouped_clashes_path: StringProperty(name="Smart Grouped Clashes Path")
active_clash_set_index: IntProperty(name="Active Clash Set Index")
active_clash_index: IntProperty(name="Active Clash Index")
smart_clash_groups: CollectionProperty(name="Smart Clash Groups", type=SmartClashGroup)
active_smart_group_index: IntProperty(name="Active Smart Group Index")
smart_clash_grouping_max_distance: IntProperty(
name="Smart Clash Grouping Max Distance", default=3, soft_min=1, soft_max=10
)
sould_focus_on_clash: BoolProperty(name="Show Focus Clash", default=False)
@property
def active_clash_set(self):
@@ -80,3 +91,9 @@ class BIMClashProperties(PropertyGroup):
if not self.smart_clash_groups:
return None
return self.smart_clash_groups[self.active_smart_group_index]
@property
def active_clash(self):
if not self.active_clash_set:
return None
return self.active_clash_set.clashes[self.active_clash_index]
@@ -107,6 +107,42 @@ class BIM_PT_clash_manager(Panel):
bl_context = "scene"
bl_parent_id = "BIM_PT_tab_clash_detection"
def draw(self, context):
pass
class BIM_PT_dumb_clash_manager(Panel):
bl_idname = "BIM_PT_dumb_clash_manager"
bl_label = "Dumb Manager"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
bl_parent_id = "BIM_PT_clash_manager"
def draw(self, context):
layout = self.layout
props = context.scene.BIMClashProperties
row = layout.row(align=True)
row.operator("bim.load_ifc_clashes", text="LOAD IFC CLASHES", icon="IMPORT")
row.operator("bim.save_ifc_clashes", text="SAVE CLASH RESULTS", icon="EXPORT")
layout.template_list("BIM_UL_clashes", "", props.active_clash_set, "clashes", props, "active_clash_index")
# bim.select_clash
row = layout.row(align=True)
row.operator("bim.select_clash", text="SELECT CLASH", icon="IMPORT")
row.prop(props, "sould_focus_on_clash", icon="RESTRICT_VIEW_OFF")
class BIM_PT_smart_clash_manager(Panel):
bl_idname = "BIM_PT_smart_clash_manager"
bl_label = "Smart Clash Manager"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
bl_parent_id = "BIM_PT_clash_manager"
def draw(self, context):
layout = self.layout
props = context.scene.BIMClashProperties
@@ -156,3 +192,14 @@ class BIM_UL_smart_groups(bpy.types.UIList):
layout.label(text=str(item.number), translate=False, icon="NONE", icon_value=0)
else:
layout.label(text="", translate=False)
class BIM_UL_clashes(bpy.types.UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
if item:
row = layout.row(align=True)
row.label(text=str(item.a_name), translate=False, icon="NONE", icon_value=0)
row.label(text=str(item.b_name), translate=False, icon="NONE", icon_value=0)
row.prop(item, "status", text="")
else:
layout.label(text="", translate=False)
@@ -24,12 +24,18 @@ classes = (
operator.AddClassificationFromBSDD,
operator.AddClassificationReference,
operator.AddClassificationReferenceFromBSDD,
operator.AddManualClassification,
operator.AddManualClassificationReference,
operator.ChangeClassificationLevel,
operator.DisableAddingManualClassification,
operator.DisableAddingManualClassificationReference,
operator.DisableEditingClassification,
operator.DisableEditingClassificationReference,
operator.DisableEditingClassificationReferences,
operator.EditClassification,
operator.EditClassificationReference,
operator.EnableAddingManualClassification,
operator.EnableAddingManualClassificationReference,
operator.EnableEditingClassification,
operator.EnableEditingClassificationReference,
operator.LoadClassificationLibrary,
@@ -73,6 +73,10 @@ class ReferencesData:
if name in [e.Name for e in tool.Ifc.get().by_type("IfcClassification")]:
return name
@classmethod
def classifications(cls):
return [(str(e.id()), e.Name, "") for e in tool.Ifc.get().by_type("IfcClassification")]
class ClassificationReferencesData(ReferencesData):
data = {}
@@ -83,6 +87,8 @@ class ClassificationReferencesData(ReferencesData):
cls.is_loaded = True
cls.data["references"] = cls.references()
cls.data["active_classification_library"] = cls.active_classification_library()
cls.data["classifications"] = cls.classifications()
cls.data["object_type"] = "OBJECT"
@classmethod
def references(cls):
@@ -105,6 +111,8 @@ class MaterialClassificationsData(ReferencesData):
cls.is_loaded = True
cls.data["references"] = cls.references()
cls.data["active_classification_library"] = cls.active_classification_library()
cls.data["classifications"] = cls.classifications()
cls.data["object_type"] = "MATERIAL"
@classmethod
def references(cls):
@@ -127,6 +135,8 @@ class CostClassificationsData(ReferencesData):
cls.is_loaded = True
cls.data["references"] = cls.references()
cls.data["active_classification_library"] = cls.active_classification_library()
cls.data["classifications"] = cls.classifications()
cls.data["object_type"] = "COST"
@classmethod
def references(cls):
@@ -54,6 +54,50 @@ class AddClassification(bpy.types.Operator, tool.Ifc.Operator):
)
class AddManualClassification(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.add_manual_classification"
bl_label = "Add Manual Classification"
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
props = context.scene.BIMClassificationProperties
attributes = blenderbim.bim.helper.export_attributes(props.classification_attributes)
classification = ifcopenshell.api.run(
"classification.add_classification", tool.Ifc.get(), classification="Unnamed"
)
ifcopenshell.api.run(
"classification.edit_classification", tool.Ifc.get(), classification=classification, attributes=attributes
)
props.is_adding = False
class AddManualClassificationReference(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.add_manual_classification_reference"
bl_label = "Add Manual Classification Reference"
bl_options = {"REGISTER", "UNDO"}
obj_type: bpy.props.StringProperty()
def _execute(self, context):
obj = context.active_object
props = obj.BIMClassificationReferenceProperties
attributes = blenderbim.bim.helper.export_attributes(props.reference_attributes)
product = tool.Ifc.get_entity(obj)
# TODO: refactor and support material and cost item classifications
classification = tool.Ifc.get().by_id(int(props.classifications))
reference = ifcopenshell.api.run(
"classification.add_reference",
tool.Ifc.get(),
product=product,
classification=classification,
identification="X",
name="Unnamed",
)
ifcopenshell.api.run(
"classification.edit_reference", tool.Ifc.get(), reference=reference, attributes=attributes
)
props.is_adding = False
class AddClassificationFromBSDD(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.add_classification_from_bsdd"
bl_label = "Add Classification From bSDD"
@@ -63,16 +107,67 @@ class AddClassificationFromBSDD(bpy.types.Operator, tool.Ifc.Operator):
props = context.scene.BIMBSDDProperties
domain = [d for d in props.domains if d.name == props.active_domain][0]
for element in tool.Ifc.get().by_type("IfcClassification"):
if element.Name == props.active_domain or element.Location == domain.namespace_uri:
if element.Name == props.active_domain or element.Location == domain.uri:
return
classification = ifcopenshell.api.run(
"classification.add_classification", tool.Ifc.get(), classification=props.active_domain
)
classification.Source = domain.organization_name_owner
classification.Location = domain.namespace_uri
classification.Location = domain.uri
classification.Edition = domain.version
class EnableAddingManualClassification(bpy.types.Operator):
bl_idname = "bim.enable_adding_manual_classification"
bl_label = "Enable Adding Manual Classification"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
props = context.scene.BIMClassificationProperties
props.is_adding = True
props.active_classification_id = 0
props.classification_attributes.clear()
blenderbim.bim.helper.import_attributes2("IfcClassification", props.classification_attributes)
return {"FINISHED"}
class DisableAddingManualClassification(bpy.types.Operator):
bl_idname = "bim.disable_adding_manual_classification"
bl_label = "Disable Adding Manual Classification"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
props = context.scene.BIMClassificationProperties
props.is_adding = False
return {"FINISHED"}
class EnableAddingManualClassificationReference(bpy.types.Operator):
bl_idname = "bim.enable_adding_manual_classification_reference"
bl_label = "Enable Adding Manual Classification Reference"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
obj = context.active_object
props = obj.BIMClassificationReferenceProperties
props.is_adding = True
props.reference_attributes.clear()
blenderbim.bim.helper.import_attributes2("IfcClassificationReference", props.reference_attributes)
return {"FINISHED"}
class DisableAddingManualClassificationReference(bpy.types.Operator):
bl_idname = "bim.disable_adding_manual_classification_reference"
bl_label = "Disable Adding Manual Classification Reference"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
obj = context.active_object
props = obj.BIMClassificationReferenceProperties
props.is_adding = False
return {"FINISHED"}
class EnableEditingClassification(bpy.types.Operator):
bl_idname = "bim.enable_editing_classification"
bl_label = "Enable Editing Classification"
@@ -194,21 +289,23 @@ class RemoveClassificationReference(bpy.types.Operator, tool.Ifc.Operator):
else:
objects = [self.obj]
identification = tool.Ifc.get().by_id(self.reference)[0]
active_reference = tool.Ifc.get().by_id(self.reference)
identification = active_reference[1]
for obj in objects:
ifc_definition_id = blenderbim.bim.helper.get_obj_ifc_definition_id(context, obj, self.obj_type)
element = tool.Ifc.get().by_id(ifc_definition_id)
references = ifcopenshell.util.classification.get_references(element, should_inherit=False)
for reference in references:
if reference[0] == identification:
if (identification and reference[1] == identification) or (
not identification and reference == active_reference
):
ifcopenshell.api.run(
"classification.remove_reference",
tool.Ifc.get(),
reference=reference,
product=element,
)
break
class EditClassificationReference(bpy.types.Operator, tool.Ifc.Operator):
@@ -321,20 +418,35 @@ class AddClassificationReferenceFromBSDD(bpy.types.Operator, tool.Ifc.Operator):
identification=bsdd_classification.reference_code,
name=bsdd_classification.name,
)
reference.Location = bsdd_classification.namespace_uri
reference.Location = bsdd_classification.uri
for classification_pset in bprops.classification_psets:
pset = ifcopenshell.util.element.get_pset(element, classification_pset.name)
is_pset = not classification_pset.name.startswith("Qto_")
if is_pset:
pset = ifcopenshell.util.element.get_pset(element, classification_pset.name, psets_only=True)
else:
pset = ifcopenshell.util.element.get_pset(element, classification_pset.name, qtos_only=True)
if pset:
pset = tool.Ifc.get().by_id(pset["id"])
else:
elif is_pset:
pset = ifcopenshell.api.run(
"pset.add_pset", tool.Ifc.get(), product=element, name=classification_pset.name
)
else:
pset = ifcopenshell.api.run(
"pset.add_qto", tool.Ifc.get(), product=element, name=classification_pset.name
)
properties = {}
for prop in classification_pset.properties:
properties[prop.name] = prop.get_value()
ifcopenshell.api.run("pset.edit_pset", tool.Ifc.get(), pset=pset, properties=properties)
if is_pset:
ifcopenshell.api.run("pset.edit_pset", tool.Ifc.get(), pset=pset, properties=properties)
else:
ifcopenshell.api.run("pset.edit_qto", tool.Ifc.get(), qto=pset, properties=properties)
class ChangeClassificationLevel(bpy.types.Operator):
@@ -17,9 +17,10 @@
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import bpy
import blenderbim.bim.helper
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.prop import StrProperty, Attribute
from blenderbim.bim.module.classification.data import ClassificationsData
from blenderbim.bim.module.classification.data import ClassificationsData, ClassificationReferencesData
from bpy.types import PropertyGroup
from bpy.props import (
PointerProperty,
@@ -39,6 +40,12 @@ def get_available_classifications(self, context):
return ClassificationsData.data["available_classifications"]
def get_classifications(self, context):
if not ClassificationReferencesData.is_loaded:
ClassificationReferencesData.load()
return ClassificationReferencesData.data["classifications"]
class ClassificationReference(PropertyGroup):
name: StringProperty(name="Name")
identification: StringProperty(name="Identification")
@@ -48,6 +55,7 @@ class ClassificationReference(PropertyGroup):
class BIMClassificationProperties(PropertyGroup):
is_adding: BoolProperty(name="Is Adding", default=False)
classification_source: EnumProperty(
items=[
("FILE", "IFC File", ""),
@@ -66,5 +74,7 @@ class BIMClassificationProperties(PropertyGroup):
class BIMClassificationReferenceProperties(PropertyGroup):
is_adding: BoolProperty(name="Is Adding", default=False)
classifications: EnumProperty(items=get_classifications, name="Classifications")
reference_attributes: CollectionProperty(name="Reference Attributes", type=Attribute)
active_reference_id: IntProperty(name="Active Reference Id")
@@ -66,8 +66,14 @@ class BIM_PT_classifications(Panel):
self.draw_ui(classification)
def draw_add_manual_ui(self, context):
row = self.layout.row()
row.label(text="TODO", icon="ERROR")
if self.props.is_adding:
blenderbim.bim.helper.draw_attributes(self.props.classification_attributes, self.layout)
row = self.layout.row(align=True)
row.operator("bim.add_manual_classification", text="Save", icon="CHECKMARK")
row.operator("bim.disable_adding_manual_classification", text="", icon="CANCEL")
else:
row = self.layout.row()
row.operator("bim.enable_adding_manual_classification", text="Add Classification", icon="ADD")
def draw_add_bsdd_ui(self, context):
self.bprops = context.scene.BIMBSDDProperties
@@ -144,7 +150,16 @@ class ReferenceUI:
def draw_add_manual_ui(self, context):
row = self.layout.row()
row.label(text="TODO", icon="ERROR")
row.prop(self.props, "classifications", text="")
if self.props.is_adding:
blenderbim.bim.helper.draw_attributes(self.props.reference_attributes, self.layout)
row = self.layout.row(align=True)
op = row.operator("bim.add_manual_classification_reference", text="Save", icon="CHECKMARK")
op.type = self.data.data["object_type"]
row.operator("bim.disable_adding_manual_classification_reference", text="", icon="CANCEL")
else:
row = self.layout.row()
row.operator("bim.enable_adding_manual_classification_reference", text="Add Reference", icon="ADD")
def draw_add_bsdd_ui(self, context):
if not self.bprops.active_domain:
@@ -239,6 +254,8 @@ class ReferenceUI:
name = reference["Identification"] or "No Identification"
row.label(text=name, icon="ASSET_MANAGER")
row.label(text=reference["Name"] or "")
if reference["Location"]:
row.operator("bim.open_uri", icon="URL", text="").uri = reference["Location"]
if not self.props.active_reference_id:
op = row.operator("bim.enable_editing_classification_reference", text="", icon="GREASEPENCIL")
op.reference = reference["id"]
@@ -256,7 +256,7 @@ class CostSchedulesData:
for cost_value in cost_item.CostValues or []:
label = "{0:.2f}".format(ifcopenshell.util.cost.calculate_applied_value(cost_item, cost_value))
label += " = {}".format(ifcopenshell.util.cost.serialise_cost_value(cost_value))
results.append({"id": cost_value.id(), "label": label})
results.append({"id": cost_value.id(), "label": label, "name": cost_value.Name})
return results
@classmethod
@@ -37,12 +37,10 @@ class AddCostSchedule(bpy.types.Operator, tool.Ifc.Operator):
object_type: bpy.props.StringProperty()
def _execute(self, context):
core.add_cost_schedule(
tool.Ifc,
name=self.name,
predefined_type=context.scene.BIMCostProperties.cost_schedule_predefined_types,
object_type=self.object_type,
)
predefined_type = context.scene.BIMCostProperties.cost_schedule_predefined_types
if predefined_type == "USERDEFINED":
predefined_type = self.object_type
core.add_cost_schedule(tool.Ifc, name=self.name, predefined_type=predefined_type)
def draw(self, context):
layout = self.layout
@@ -42,19 +42,21 @@ class BIM_PT_cost_schedules(Panel):
CostSchedulesData.load()
self.props = context.scene.BIMCostProperties
row = self.layout.row(align=True)
if not self.props.active_cost_schedule_id:
row = self.layout.row(align=True)
if CostSchedulesData.data["total_cost_schedules"]:
row.alignment = "RIGHT"
row.operator("bim.export_cost_schedules",icon="EXPORT",text="Export All Schedules")
row = self.layout.row(align=True)
row.label(text=f"{CostSchedulesData.data['total_cost_schedules']} Cost Schedules Found", icon="TEXT")
else:
row.label(text="No Cost Schedules found.", icon="TEXT")
row.operator("bim.add_cost_schedule", icon="ADD", text="")
row.operator("import_cost_schedule_csv.bim",icon="IMPORT",text="")
for schedule in CostSchedulesData.data["schedules"]:
self.draw_cost_schedule_ui(schedule)
row2 = self.layout.row()
row2.alignment = "RIGHT"
row2.operator("bim.export_cost_schedules", text="Export all", icon="EXPORT")
def draw_cost_schedule_ui(self, cost_schedule):
row = self.layout.row(align=True)
@@ -236,6 +238,8 @@ class BIM_PT_cost_schedules(Panel):
blenderbim.bim.helper.draw_attributes(self.props.cost_value_attributes, self.layout.box())
def draw_readonly_cost_value_ui(self, layout, cost_value):
if cost_value["name"]:
layout.label(text=f"{cost_value['name']}: ")
if self.props.active_cost_value_id == cost_value["id"] and self.props.cost_value_editing_type == "FORMULA":
layout.prop(self.props, "cost_value_formula", text="")
else:
@@ -24,6 +24,7 @@ import logging
import tempfile
import ifcopenshell
import blenderbim.tool as tool
import blenderbim.bim.module.drawing.scheduler as scheduler
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.handler import refresh_ui_data
@@ -92,15 +93,26 @@ class ImportCsvAttributes(bpy.types.Operator):
data = json.load(open(self.filepath))
tool.Search.import_filter_query(data["query"], props.filter_groups)
for prop in [
"should_generate_svg",
"should_preserve_existing",
"include_global_id",
"null_value",
"empty_value",
"true_value",
"false_value",
"concat_value",
"csv_delimiter",
"format",
"csv_custom_delimiter",
]:
setattr(props, prop, data["settings"][prop])
props.csv_attributes.clear()
for attribute in data["attributes"]:
new = props.csv_attributes.add()
new.name = attribute["name"]
new.header = attribute["header"]
new.sort = attribute["sort"]
new.group = attribute["group"]
new.summary = attribute["summary"]
new.formatting = attribute["formatting"]
for prop in ["name", "header", "sort", "group", "summary", "formatting"]:
setattr(new, prop, attribute[prop])
return {"FINISHED"}
def invoke(self, context, event):
@@ -120,6 +132,22 @@ class ExportCsvAttributes(bpy.types.Operator):
def execute(self, context):
props = context.scene.CsvProperties
settings = {}
for prop in [
"should_generate_svg",
"should_preserve_existing",
"include_global_id",
"null_value",
"empty_value",
"true_value",
"false_value",
"concat_value",
"csv_delimiter",
"format",
"csv_custom_delimiter",
]:
settings[prop] = getattr(props, prop)
data = {
"query": tool.Search.export_filter_query(props.filter_groups),
"attributes": [
@@ -133,6 +161,7 @@ class ExportCsvAttributes(bpy.types.Operator):
}
for a in props.csv_attributes
],
"settings": settings,
}
with open(self.filepath, "w") as outfile:
@@ -213,6 +242,10 @@ class ExportIfcCsv(bpy.types.Operator):
summaries=summaries,
formatting=formatting,
)
if props.format != "csv" and props.should_generate_svg:
schedule_creator = scheduler.Scheduler()
schedule_creator.schedule(self.filepath, tool.Drawing.get_path_with_ext(self.filepath, "svg"))
return {"FINISHED"}
@@ -65,6 +65,7 @@ class CsvProperties(PropertyGroup):
ifc_selector: StringProperty(default="", name="IFC Selector")
filter_groups: CollectionProperty(type=BIMFilterGroup, name="Filter Groups")
csv_attributes: CollectionProperty(name="CSV Attributes", type=CsvAttribute)
should_generate_svg: BoolProperty(default=False, name="Generate SVG")
should_preserve_existing: BoolProperty(default=False, name="Preserve Existing")
include_global_id: BoolProperty(default=True, name="Include GlobalId")
null_value: StringProperty(default="N/A", name="Null Value")
@@ -67,6 +67,8 @@ class BIM_PT_ifccsv(Panel):
row = layout.row(align=True)
row.prop(props, "csv_custom_delimiter")
row = layout.row()
row.prop(props, "should_generate_svg")
row = layout.row()
row.prop(props, "should_preserve_existing")
row = layout.row()
@@ -213,7 +213,7 @@ class CreateAllShapes(bpy.types.Operator):
settings_2d.set(settings_2d.INCLUDE_CURVES, True)
failures = []
excludes = () # For the developer to debug with
for i, element in enumerate(elements):
for i, element in enumerate(elements, 1):
if element.GlobalId in excludes:
continue
print(f"{i}/{total}:", element)
@@ -475,7 +475,7 @@ class OverrideDisplayType(bpy.types.Operator):
class PrintUnusedElementStats(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.print_unused_elements_stats"
bl_label = "Purge Unused Elements Stats"
bl_label = "Print Unused Elements Stats"
bl_options = {"REGISTER", "UNDO"}
bl_description = (
"Print all unused elements in current IFC project in system console, not limited to the selected class"
@@ -484,6 +484,7 @@ class PrintUnusedElementStats(bpy.types.Operator, tool.Ifc.Operator):
ignore_contexts: bpy.props.BoolProperty(name="Ignore Contexts", default=True)
ignore_relationships: bpy.props.BoolProperty(name="Ignore Relationships", default=True)
ignore_types: bpy.props.BoolProperty(name="Ignore Types", default=True)
ignore_styled_items: bpy.props.BoolProperty(name="Ignoer Styled Items", default=True)
def _execute(self, context):
props = context.scene.BIMDebugProperties
@@ -495,6 +496,8 @@ class PrintUnusedElementStats(bpy.types.Operator, tool.Ifc.Operator):
ignore_classes += ["IfcRelationship"]
if self.ignore_types:
ignore_classes += ["IfcTypeProduct"]
if self.ignore_styled_items:
ignore_classes += ["IfcStyledItem"]
unused_elements = tool.Debug.print_unused_elements_stats(props.ifc_class_purge, ignore_classes)
self.report({"INFO"}, f"{unused_elements} unused elements found, check the system console for the details.")
@@ -504,11 +507,119 @@ class PurgeUnusedElementsByClass(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.purge_unused_elements_by_class"
bl_label = "Purge Unused Elements By Class"
bl_description = (
"Will find all elements of class that have no inverse refernces and will remove them, use very carefully."
"Will find all elements of class that have no inverse refernces and will remove them, use very carefully"
)
bl_options = {"REGISTER", "UNDO"}
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
filter_glob: bpy.props.StringProperty(default="*.ifc", options={"HIDDEN"})
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
return {"RUNNING_MODAL"}
def _execute(self, context):
props = context.scene.BIMDebugProperties
purged_elements = core.purge_unused_elements(tool.Ifc, tool.Debug, props.ifc_class_purge)
self.report({"INFO"}, f"{purged_elements} unused elements found and removed.")
if props.ifc_class_purge:
purged_elements = core.purge_unused_elements(tool.Ifc, tool.Debug, props.ifc_class_purge)
self.report({"INFO"}, f"{purged_elements} unused elements found and removed.")
tool.Ifc.get().write(self.filepath)
return
# A whitelisted class is a class that only contains simple data that
# has no meaning by itself (only has meaning when combined with other
# data). E.g. a colour by itself has no meaning so is whitelisted, but
# a material by itself may be part of your materials library.
# There are classes I don't know enough about to decide. Not whitelisting in case.
# IfcAlignmentParameterSegment
# IfcBoundaryCondition
# IfcStructuralConnectionCondition
# IfcStructuralLoad
whitelisted_classes = [
"IfcActorRole",
"IfcAddress",
"IfcApplication",
"IfcAppliedValue",
"IfcConnectionGeometry",
"IfcCoordinateReferenceSystem",
"IfcDerivedUnit",
"IfcDerivedUnitElement",
"IfcDimensionalExponents",
"IfcExternalReference",
"IfcGridAxis",
"IfcIrregularTimeSeriesValue",
"IfcLightDistributionData",
"IfcLightIntensityDistribution",
"IfcMeasureWithUnit",
"IfcMonetaryUnit",
"IfcNamedUnit",
"IfcObjectPlacement",
"IfcOrganization", # Should be referenced as part of an actor
"IfcOwnerHistory",
"IfcPerson", # Should be referenced as part of an actor
"IfcPersonAndOrganization", # Should be referenced as part of an actor
"IfcPhysicalQuantity",
"IfcPresentationItem",
"IfcProductDefinitionShape",
"IfcPropertyAbstraction",
"IfcRecurrencePattern",
"IfcReference",
"IfcRepresentation",
"IfcRepresentationContext",
"IfcRepresentationItem",
"IfcRepresentationMap",
"IfcPropertyDefinition", # A bit of a questionable one, and the odd one out from IfcRoot.
"IfcSchedulingTime",
"IfcShapeAspect",
"IfcTable",
"IfcTableColumn",
"IfcTableRow",
"IfcTextureCoordinateIndices",
"IfcTimePeriod",
"IfcTimeSeries",
"IfcTimeSeriesValue",
"IfcUnitAssignment",
"IfcVirtualGridIntersection",
]
total_purged = 0
while True:
total_batches = 0
print("*" * 100)
total_batch_purged = 0
for ifc_class in whitelisted_classes:
total_class_purged = 0
try:
elements = tool.Ifc.get().by_type(ifc_class)
except:
continue # Probably not in this schema?
to_purge = set()
for element in elements:
try:
if ifc_class == "IfcRepresentationItem" and element.is_a("IfcStyledItem") and element.Item:
continue
except:
to_purge.add(element.id()) # It's invalid, definitely purge it.
if tool.Ifc.get().get_total_inverses(element) == 0:
to_purge.add(element.id())
for element_id in to_purge:
try:
element = tool.Ifc.get().by_id(element_id)
ifcopenshell.util.element.remove_deep2(tool.Ifc.get(), element)
total_class_purged += 1
except:
continue
if total_class_purged > 0:
total_batch_purged += total_class_purged
print(f"Auto purged {total_class_purged} {ifc_class}")
if total_batch_purged > 0:
total_purged += total_batch_purged
print(f"Auto purged in batch: {total_batch_purged}")
total_batches += 1
if total_batch_purged == 0:
break
elif total_batches > 20:
print("Finished 20 batches. Manually stopping in case of infinite loop.")
self.report({"INFO"}, f"Auto purged {total_purged} orphaned elements")
tool.Ifc.get().write(self.filepath)
@@ -37,7 +37,6 @@ class BIM_PT_debug(Panel):
row = self.layout.row(align=True)
row.prop(context.scene.BIMProperties, "ifc_file", text="")
row.operator("bim.validate_ifc_file", icon="CHECKMARK", text="")
row.operator("bim.reload_selected_ifc_file", icon="FILE_REFRESH", text="")
row.operator("bim.select_ifc_file", icon="FILE_FOLDER", text="")
row = self.layout.row(align=True)
@@ -45,6 +44,9 @@ class BIM_PT_debug(Panel):
row.operator("bim.parse_express", icon="IMPORT", text="")
row.operator("bim.select_express_file", icon="FILE_FOLDER", text="")
row = layout.row()
row.operator("bim.reload_ifc_file", text="Incrementally Reload Changes")
row = layout.row()
row.operator("bim.print_ifc_file")
@@ -87,6 +89,13 @@ class BIM_PT_debug(Panel):
row.prop(props, "display_type", text="")
row.operator("bim.override_display_type").display = context.scene.BIMDebugProperties.display_type
layout.operator("bim.purge_unused_representations")
row = layout.row(align=True)
row.prop(context.scene.BIMDebugProperties, "ifc_class_purge", text="")
row.operator("bim.purge_unused_elements_by_class", text="Purge Orphaned", icon="TRASH")
row.operator("bim.print_unused_elements_stats", text="", icon="INFO")
if context.active_object and context.active_object.data:
mprops = context.active_object.data.BIMMeshProperties
row = layout.row()
@@ -18,6 +18,7 @@
import os
import bpy
import json
import ifcopenshell.util.element
import ifcopenshell.util.representation
import blenderbim.tool as tool
@@ -801,7 +801,7 @@ class AngleDecorator(BaseDecorator):
angle_rad = acos(cos_a)
angle = angle_rad / pi * 180
except ZeroDivisionError:
angle = 0
angle_rad, angle = 0, 0
# calculate angle position
p0, p1, p2 = [location_3d_to_region_2d(region, region3d, p) for p in vertices[i0 : i1 + 2]]
@@ -1949,9 +1949,10 @@ class DecorationsHandler:
def get_objects_and_decorators(self, collection):
# TODO: do it in data instead of the handler for performance?
results = []
viewport = bpy.context.space_data
for obj in collection.all_objects:
if obj.hide_get():
if not obj.visible_get(viewport=viewport):
continue
element = tool.Ifc.get_entity(obj)
@@ -44,6 +44,7 @@ import blenderbim.bim.module.drawing.helper as helper
import blenderbim.bim.export_ifc
from blenderbim.bim.module.drawing.decoration import CutDecorator
from blenderbim.bim.module.drawing.data import DecoratorData, DrawingsData
from typing import NamedTuple, List
from lxml import etree
from mathutils import Vector, Color, Matrix
from timeit import default_timer as timer
@@ -77,6 +78,11 @@ class Operator:
return {"FINISHED"}
class LineworkContexts(NamedTuple):
body: List[List[int]]
annotation: List[List[int]]
class AddAnnotationType(bpy.types.Operator, Operator):
bl_idname = "bim.add_annotation_type"
bl_label = "Add Annotation Type"
@@ -367,6 +373,105 @@ class CreateDrawing(bpy.types.Operator):
self.svg_writer.create_blank_svg(svg_path).draw_underlay(context.scene.render.filepath).save()
return svg_path
def get_linework_contexts(self, ifc, target_view) -> LineworkContexts:
plan_body_target_contexts = []
plan_body_model_contexts = []
model_body_target_contexts = []
model_body_model_contexts = []
plan_annotation_target_contexts = []
plan_annotation_model_contexts = []
model_annotation_target_contexts = []
model_annotation_model_contexts = []
for rep_context in ifc.by_type("IfcGeometricRepresentationContext"):
if rep_context.is_a("IfcGeometricRepresentationSubContext"):
if rep_context.ContextType == "Plan":
if rep_context.ContextIdentifier in ["Body", "Facetation"]:
if rep_context.TargetView == target_view:
plan_body_target_contexts.append(rep_context.id())
elif rep_context.TargetView == "MODEL_VIEW":
plan_body_model_contexts.append(rep_context.id())
elif rep_context.ContextIdentifier == "Annotation":
if rep_context.TargetView == target_view:
plan_annotation_target_contexts.append(rep_context.id())
elif rep_context.TargetView == "MODEL_VIEW":
plan_annotation_model_contexts.append(rep_context.id())
elif rep_context.ContextType == "Model":
if rep_context.ContextIdentifier in ["Body", "Facetation"]:
if rep_context.TargetView == target_view:
model_body_target_contexts.append(rep_context.id())
elif rep_context.TargetView == "MODEL_VIEW":
model_body_model_contexts.append(rep_context.id())
elif rep_context.ContextIdentifier == "Annotation":
if rep_context.TargetView == target_view:
model_annotation_target_contexts.append(rep_context.id())
elif rep_context.TargetView == "MODEL_VIEW":
model_annotation_model_contexts.append(rep_context.id())
elif rep_context.ContextType == "Model":
# You should never purely assign to a "Model" context, but
# if you do, this is what we assume your intention is.
model_body_model_contexts.append(rep_context.id())
continue
body_contexts = (
[
plan_body_target_contexts,
plan_body_model_contexts,
model_body_target_contexts,
model_body_model_contexts,
]
if target_view in ["PLAN_VIEW", "REFLECTED_PLAN_VIEW"]
else [
model_body_target_contexts,
model_body_model_contexts,
]
)
annotation_contexts = (
[
plan_annotation_target_contexts,
plan_annotation_model_contexts,
model_annotation_target_contexts,
model_annotation_model_contexts,
]
if target_view in ["PLAN_VIEW", "REFLECTED_PLAN_VIEW"]
else [
model_annotation_target_contexts,
model_annotation_model_contexts,
]
)
return LineworkContexts(body_contexts, annotation_contexts)
def serialize_contexts_elements(
self, ifc, tree: ifcopenshell.geom.tree, contexts: LineworkContexts, context_type, drawing_elements, target_view
):
drawing_elements = drawing_elements.copy()
contexts = getattr(contexts, context_type)
for context in contexts:
with profile(f"Processing {context_type} context"):
if not context or not drawing_elements:
continue
geom_settings = ifcopenshell.geom.settings(
DISABLE_TRIANGULATION=True, STRICT_TOLERANCE=True, INCLUDE_CURVES=True
)
if ifc.by_id(context[0]).ContextType == "Plan" and "PLAN_VIEW" in target_view:
offset = ifcopenshell.ifcopenshell_wrapper.float_array_3()
# A 2mm Z offset to combat Z-fighting in plan or RCPs
offset[2] = 0.002 if target_view == "PLAN_VIEW" else -0.002
geom_settings.offset = offset
geom_settings.set_context_ids(context)
it = ifcopenshell.geom.iterator(
geom_settings, ifc, multiprocessing.cpu_count(), include=drawing_elements
)
processed = set()
for elem in it:
processed.add(ifc.by_id(elem.id))
self.serialiser.write(elem)
tree.add_element(elem)
drawing_elements -= processed
def generate_linework(self, context):
if not ifcopenshell.util.element.get_pset(self.drawing, "EPset_Drawing", "HasLinework"):
return
@@ -408,77 +513,8 @@ class CreateDrawing(bpy.types.Operator):
# Drawings only draw bodies and annotations (and facetation, due to a Revit bug).
# A drawing prioritises a target view context first, followed by a model view context as a fallback.
# Specifically for PLAN_VIEW and REFLECTED_PLAN_VIEW, any Plan context is also prioritised.
plan_body_target_contexts = []
plan_body_model_contexts = []
model_body_target_contexts = []
model_body_model_contexts = []
plan_annotation_target_contexts = []
plan_annotation_model_contexts = []
model_annotation_target_contexts = []
model_annotation_model_contexts = []
target_view = ifcopenshell.util.element.get_psets(self.camera_element)["EPset_Drawing"]["TargetView"]
for rep_context in ifc.by_type("IfcGeometricRepresentationContext"):
if rep_context.is_a("IfcGeometricRepresentationSubContext"):
if rep_context.ContextType == "Plan":
if rep_context.ContextIdentifier in ["Body", "Facetation"]:
if rep_context.TargetView == target_view:
plan_body_target_contexts.append(rep_context.id())
elif rep_context.TargetView == "MODEL_VIEW":
plan_body_model_contexts.append(rep_context.id())
elif rep_context.ContextIdentifier == "Annotation":
if rep_context.TargetView == target_view:
plan_annotation_target_contexts.append(rep_context.id())
elif rep_context.TargetView == "MODEL_VIEW":
plan_annotation_model_contexts.append(rep_context.id())
elif rep_context.ContextType == "Model":
if rep_context.ContextIdentifier in ["Body", "Facetation"]:
if rep_context.TargetView == target_view:
model_body_target_contexts.append(rep_context.id())
elif rep_context.TargetView == "MODEL_VIEW":
model_body_model_contexts.append(rep_context.id())
elif rep_context.ContextIdentifier == "Annotation":
if rep_context.TargetView == target_view:
model_annotation_target_contexts.append(rep_context.id())
elif rep_context.TargetView == "MODEL_VIEW":
model_annotation_model_contexts.append(rep_context.id())
elif rep_context.ContextType == "Model":
# You should never purely assign to a "Model" context, but
# if you do, this is what we assume your intention is.
model_body_model_contexts.append(rep_context.id())
continue
body_contexts = (
[
plan_body_target_contexts,
plan_body_model_contexts,
model_body_target_contexts,
model_body_model_contexts,
]
if target_view in ["PLAN_VIEW", "REFLECTED_PLAN_VIEW"]
else [
model_body_target_contexts,
model_body_model_contexts,
]
)
annotation_contexts = (
[
plan_annotation_target_contexts,
plan_annotation_model_contexts,
model_annotation_target_contexts,
model_annotation_model_contexts,
]
if target_view in ["PLAN_VIEW", "REFLECTED_PLAN_VIEW"]
else [
model_annotation_target_contexts,
model_annotation_model_contexts,
]
)
contexts = self.get_linework_contexts(ifc, target_view)
drawing_elements = tool.Drawing.get_drawing_elements(self.camera_element)
self.setup_serialiser(ifc, target_view)
@@ -487,50 +523,15 @@ class CreateDrawing(bpy.types.Operator):
tree = ifcopenshell.geom.tree()
tree.enable_face_styles(True)
elements = drawing_elements.copy()
for body_context in body_contexts:
with profile("Processing body context"):
if body_context and elements:
geom_settings = ifcopenshell.geom.settings(
DISABLE_TRIANGULATION=True, STRICT_TOLERANCE=True, INCLUDE_CURVES=True
)
geom_settings.set_context_ids(body_context)
it = ifcopenshell.geom.iterator(
geom_settings, ifc, multiprocessing.cpu_count(), include=elements
)
it.set_cache(cache)
processed = set()
for elem in self.yield_from_iterator(it):
processed.add(ifc.by_id(elem.id))
self.serialiser.write(elem)
tree.add_element(elem)
elements -= processed
elements = drawing_elements.copy()
for annotation_context in annotation_contexts:
with profile("Processing annotation context"):
if annotation_context and elements:
geom_settings = ifcopenshell.geom.settings(
DISABLE_TRIANGULATION=True, STRICT_TOLERANCE=True, INCLUDE_CURVES=True
)
geom_settings.set_context_ids(annotation_context)
it = ifcopenshell.geom.iterator(
geom_settings, ifc, multiprocessing.cpu_count(), include=elements
)
it.set_cache(cache)
processed = set()
for elem in self.yield_from_iterator(it):
processed.add(ifc.by_id(elem.id))
self.serialiser.write(elem)
tree.add_element(elem)
elements -= processed
self.serialize_contexts_elements(ifc, tree, contexts, "body", drawing_elements, target_view)
self.serialize_contexts_elements(ifc, tree, contexts, "annotation", drawing_elements, target_view)
if self.camera_element not in drawing_elements:
with profile("Camera element"):
# The camera must always be included, regardless of any include/exclude filters.
geom_settings = ifcopenshell.geom.settings(DISABLE_TRIANGULATION=True, STRICT_TOLERANCE=True)
it = ifcopenshell.geom.iterator(geom_settings, ifc, include=[self.camera_element])
for elem in self.yield_from_iterator(it):
for elem in it:
self.serialiser.write(elem)
with profile("Finalizing"):
@@ -874,13 +875,6 @@ class CreateDrawing(bpy.types.Operator):
# the style of the face when running tree.select_ray()
# tree.enable_face_styles(True)
def yield_from_iterator(self, it):
if it.initialize():
while True:
yield it.get()
if not it.next():
break
def get_svg_classes(self, element):
classes = [element.is_a()]
material = ifcopenshell.util.element.get_material(element, should_skip_usage=True)
@@ -956,17 +950,21 @@ class CreateDrawing(bpy.types.Operator):
# alone (e.g. dangles, cuts, invalids). See #3421.
line_strings = []
old_paths = []
has_open_paths = False
for path in el.findall("{http://www.w3.org/2000/svg}path"):
for subpath in path.attrib["d"].split("M")[1:]:
subpath = "M" + subpath.strip()
coords = [[round(float(o), 1) for o in co[1:].split(",")] for co in subpath.split()]
if coords[0] != coords[-1]:
has_open_paths = True
line_strings.append(shapely.LineString(coords))
old_paths.append(path)
unioned_line_strings = shapely.union_all(shapely.GeometryCollection(line_strings))
if hasattr(unioned_line_strings, "geoms"):
results = shapely.polygonize_full(unioned_line_strings.geoms)
else:
results = []
results = []
if has_open_paths:
unioned_line_strings = shapely.union_all(shapely.GeometryCollection(line_strings))
if hasattr(unioned_line_strings, "geoms"):
results = shapely.polygonize_full(unioned_line_strings.geoms)
# If we succeeded in generating new path geometry, remove all the
# old paths and add new ones.
@@ -1996,11 +1994,11 @@ class AddReferenceToSheet(bpy.types.Operator, Operator):
props = context.scene.DocProperties
active_reference = props.references[props.active_reference_index]
active_sheet = tool.Drawing.get_active_sheet(context)
reference = tool.Ifc.get().by_id(active_reference.ifc_definition_id)
extref = tool.Ifc.get().by_id(active_reference.ifc_definition_id)
if tool.Ifc.get_schema() == "IFC2X3":
reference_location = tool.Drawing.get_path_with_ext(reference.DocumentReferences[0].Location, "svg")
extref_location = tool.Drawing.get_path_with_ext(extref.DocumentReferences[0].Location, "svg")
else:
reference_location = tool.Drawing.get_path_with_ext(reference.HasDocumentReferences[0].Location, "svg")
extref_location = tool.Drawing.get_path_with_ext(extref.HasDocumentReferences[0].Location, "svg")
sheet = tool.Ifc.get().by_id(active_sheet.ifc_definition_id)
if not sheet.is_a("IfcDocumentInformation"):
@@ -2008,30 +2006,30 @@ class AddReferenceToSheet(bpy.types.Operator, Operator):
references = tool.Drawing.get_document_references(sheet)
has_reference = False
has_extref = False
for reference in references:
if reference.Location == reference_location:
has_reference = True
if reference.Location == extref_location:
has_extref = True
break
if has_reference:
if has_extref:
return
if not tool.Drawing.does_file_exist(tool.Ifc.resolve_uri(reference_location)):
self.report({"ERROR"}, f"Cannot find reference svg by path {reference_location}.")
if not tool.Drawing.does_file_exist(tool.Ifc.resolve_uri(extref_location)):
self.report({"ERROR"}, f"Cannot find reference svg by path {extref_location}.")
return
reference = tool.Ifc.run("document.add_reference", information=sheet)
id_attr = "ItemReference" if tool.Ifc.get_schema() == "IFC2X3" else "Identification"
attributes = {
id_attr: str(len([r for r in references if r.Description in ("DRAWING", "REFERENCE")]) + 1),
"Location": reference_location,
"Location": extref_location,
"Description": "REFERENCE",
}
tool.Ifc.run("document.edit_reference", reference=reference, attributes=attributes)
sheet_builder = sheeter.SheetBuilder()
sheet_builder.data_dir = context.scene.BIMProperties.data_dir
sheet_builder.add_document(reference, reference, sheet)
sheet_builder.add_document(reference, extref, sheet)
tool.Drawing.import_sheets()
@@ -2571,6 +2569,7 @@ class AddReferenceImage(bpy.types.Operator, Operator):
bl_label = "Add Reference Image"
bl_options = {"REGISTER", "UNDO"}
use_relative_path: bpy.props.BoolProperty(name="Use Relative Path", default=True)
filepath: bpy.props.StringProperty(
name="File Path", description="Filepath used to import from", maxlen=1024, default="", subtype="FILE_PATH"
)
@@ -2590,19 +2589,34 @@ class AddReferenceImage(bpy.types.Operator, Operator):
options={"SKIP_SAVE"},
)
def draw(self, context):
layout = self.layout
if Path(tool.Ifc.get_path()).is_file():
layout.prop(self, "use_relative_path")
else:
self.use_relative_path = False
layout.label(text="Save the .ifc file first ")
layout.label(text="to use relative paths.")
layout.prop(self, "override_existing_image")
layout.prop(self, "use_existing_object_by_name")
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
return {"RUNNING_MODAL"}
def _execute(self, context):
abs_path = Path(self.filepath)
if self.use_relative_path:
image_filepath = abs_path.relative_to(Path(tool.Ifc.get_path()).parent)
else:
image_filepath = abs_path
ifc_file = tool.Ifc.get()
image_filepath = Path(self.filepath)
if self.override_existing_image:
params = {"check_existing": True, "force_reload": True}
else:
params = {"check_existing": False}
image = load_image(image_filepath.name, image_filepath.parent, **params)
image = load_image(abs_path.name, abs_path.parent, **params)
def bm_add_image_plane(mesh):
bm = tool.Blender.get_bmesh_for_mesh(mesh, clean=True)
@@ -2638,8 +2652,33 @@ class AddReferenceImage(bpy.types.Operator, Operator):
style = ifc_file.by_id(material.BIMMaterialProperties.ifc_style_id)
tool.Style.assign_style_to_object(style, obj)
tool.Geometry.record_object_materials(obj)
material.BIMStyleProperties.diffuse_path = self.filepath
material.BIMStyleProperties.uv_mode = "Generated"
bpy.ops.bim.update_style_colours()
# TODO: IfcSurfaceStyleRendering is unnecessary here, added it only because
# we don't support IfcSurfaceStyleWithTextures without Rendering yet
shading_attributes = {
"SurfaceColour": {
"Red": 1.0,
"Green": 1.0,
"Blue": 1.0,
},
"Transparency": 0.0,
"ReflectanceMethod": "NOTDEFINED",
}
ifcopenshell.api.run(
"style.add_surface_style",
tool.Ifc.get(),
style=style,
ifc_class="IfcSurfaceStyleRendering",
attributes=shading_attributes,
)
texture = ifc_file.create_entity("IfcImageTexture", Mode="DIFFUSE", URLReference=image_filepath.as_posix())
textures = [texture]
ifc_file.create_entity("IfcTextureCoordinateGenerator", Maps=textures, Mode="COORD") # UV map
tool.Ifc.run(
"style.add_surface_style",
style=style,
ifc_class="IfcSurfaceStyleWithTextures",
attributes={"Textures": textures},
)
tool.Style.reload_material_from_ifc(material)
tool.Geometry.record_object_materials(obj)
@@ -18,6 +18,7 @@
from blenderbim.bim.module.drawing.svgwriter import SvgWriter
import svgwrite
import openpyxl
from odf.opendocument import load as load_ods
from odf.table import Table, TableRow, TableColumn, TableCell
@@ -62,6 +63,156 @@ class Scheduler:
)
self.padding = 1
self.margin = 1
if infile.endswith("ods"):
self.schedule_ods(infile, outfile)
elif infile.endswith("xlsx"):
self.schedule_xlsx(infile, outfile)
def schedule_xlsx(self, infile, outfile):
workbook = openpyxl.open(infile, data_only=True)
sheet = workbook.active
column_dimensions = {k: v.width for k, v in sheet.column_dimensions.items()}
row_dimensions = {k: v.height for k, v in sheet.row_dimensions.items()}
default_width = 8.43 # 8.43 characters wide
default_height = 15 # 15pt seems to be the default height
merged_cells = []
for cell_range in sheet.merged_cells.ranges:
pass
y = self.margin
rows = list(sheet.iter_rows())
total_rows = len(rows)
for i, row in enumerate(rows):
# The last row may contain only null values
if i == (total_rows - 1) and not [c for c in row if c.value is not None]:
continue
x = self.margin
for cell in row:
if isinstance(cell, openpyxl.cell.cell.MergedCell):
column_letter = openpyxl.utils.get_column_letter(cell.column)
width = column_dimensions.get(column_letter, default_width)
else:
width = column_dimensions.get(cell.column_letter, default_width)
width = self.convert_character_width_to_mm(width)
height = row_dimensions.get(cell.row, default_height)
height = self.convert_to_mm(f"{height}pt")
if isinstance(cell, openpyxl.cell.cell.MergedCell):
x += width
continue
unmerged_width = width
unmerged_height = height
for cell_range in sheet.merged_cells.ranges:
if cell.coordinate not in cell_range:
continue
merged_width = 0
merged_height = 0
for merged_column in cell_range.cols:
merged_cell = sheet.cell(*merged_column[0])
column_letter = openpyxl.utils.get_column_letter(merged_cell.column)
width = column_dimensions.get(cell.column_letter, default_width)
width = self.convert_character_width_to_mm(width)
merged_width += width
for merged_row in cell_range.rows:
merged_cell = sheet.cell(*merged_row[0])
height = row_dimensions.get(merged_cell.row, default_height)
height = self.convert_to_mm(f"{height}pt")
merged_height += height
width = merged_width
height = merged_height
break
if cell.fill.patternType:
background_color = "#" + str(cell.fill.bgColor.rgb).lower()[2:]
else:
background_color = "#ffffff"
self.svg.add(
self.svg.rect(
insert=(x, y),
size=(width, height),
style=f"fill: {background_color}; stroke-width:.125; stroke: #000000;",
)
)
if not cell.value:
x += unmerged_width
continue
font_size = cell.font.size or 11 # 11pt default
font_size = font_size / FONT_SIZE_PT * FONT_SIZE # Magic?
text_position = [0.0, 0.0]
if cell.alignment.horizontal == "left":
text_position[0] = x + self.padding
horizontal_align = "left"
elif cell.alignment.horizontal == "general" and isinstance(cell.value, str):
text_position[0] = x + self.padding
horizontal_align = "left"
elif cell.alignment.horizontal in ("center", "justify", "centerContinuous", "distributed"):
text_position[0] = x + width / 2
horizontal_align = "middle"
elif cell.alignment.horizontal == "right":
text_position[0] = x + width - self.padding
horizontal_align = "right"
elif cell.alignment.horizontal == "general" and not isinstance(cell.value, str):
text_position[0] = x + width - self.padding
horizontal_align = "right"
else:
text_position[0] = x + self.padding
horizontal_align = "left"
if cell.alignment.vertical == "top":
text_position[1] = y + self.padding
vertical_align = "top"
elif cell.alignment.vertical in ("center", "justify", "distributed"):
text_position[1] = y + height / 2
vertical_align = "middle"
elif cell.alignment.vertical == "bottom":
text_position[1] = y + height - self.padding
vertical_align = "bottom"
else:
text_position[1] = y + height - self.padding
vertical_align = "bottom"
if vertical_align == "middle" and horizontal_align == "middle":
box_alignment = "center"
else:
box_alignment = f"{vertical_align}-{horizontal_align}"
if cell.font.color:
text_color = "#" + str(cell.font.color.rgb).lower()[2:]
else:
text_color = "#000000"
self.add_text(
[cell.value],
*text_position,
font_size=font_size,
box_alignment=box_alignment,
wrap_text=cell.alignment.wrapText or False,
cell_width=width,
bold=cell.font.b,
italic=cell.font.i,
text_color=text_color,
)
x += unmerged_width
y += unmerged_height
total_width = x + self.margin
total_height = y + self.margin
self.svg["width"] = "{}mm".format(total_width)
self.svg["height"] = "{}mm".format(total_height)
self.svg["viewBox"] = "0 0 {} {}".format(total_width, total_height)
self.svg.save(pretty=True)
def schedule_ods(self, infile, outfile):
doc = load_ods(infile)
# useful for debugging ods
@@ -386,6 +537,10 @@ class Scheduler:
if len(text_lines) == 1 and not wrap_text:
text_params.update(box_alignment_params)
text_tag = self.svg.text(text_lines[0], insert=(x, y), **(text_params))
if self.is_url(text_lines[0]):
anchor = self.svg.a(text_lines[0].strip())
anchor.add(text_tag)
text_tag = anchor
self.svg.add(text_tag)
return
@@ -420,11 +575,26 @@ class Scheduler:
tspan = self.svg.tspan(text_line, insert=(x, y), **text_params)
# doing it here and not in tspan constructor because constructor adds unnecessary spaces
tspan.update({"dy": f"{line_number}em"})
if self.is_url(text_line):
anchor = self.svg.a(text_line.strip())
anchor.add(tspan)
tspan = anchor
text_tag.add(tspan)
line_number += dy_dir
self.svg.add(text_tag)
def is_url(self, value):
return value.strip().startswith("http") and " " not in value.strip() and "://" in value # Wrong but fast
def convert_character_width_to_mm(self, value):
# Excel measures widths in terms of "number of characters of the
# default font", which is most commonly 11pt Calibri. The width of the
# "0" character in Calibri is approximately 7px at 100% zoom.
px_per_character = 7
px_to_pt = 0.75
return self.convert_to_mm(f"{value * px_per_character * px_to_pt}pt")
def convert_to_mm(self, value):
# XSL is what defines the units of measurements in ODF
# https://docs.oasis-open.org/office/v1.2/os/OpenDocument-v1.2-os-part1.html#datatype-positiveLength
@@ -187,11 +187,11 @@ class SheetBuilder:
layout_tree.write(layout_path)
def add_document(self, reference, schedule, sheet):
view_path = tool.Drawing.get_path_with_ext(tool.Drawing.get_document_uri(schedule), "svg")
def add_document(self, reference, document, sheet):
view_path = tool.Drawing.get_path_with_ext(tool.Drawing.get_document_uri(document), "svg")
if not os.path.exists(view_path):
tool.Drawing.create_svg_schedule(schedule)
schedule_name = os.path.splitext(os.path.basename(view_path))[0]
tool.Drawing.create_svg_document(document)
document_name = os.path.splitext(os.path.basename(view_path))[0]
layout_path = tool.Drawing.get_document_uri(sheet, "LAYOUT")
layout_dir = os.path.dirname(layout_path)
@@ -207,12 +207,12 @@ class SheetBuilder:
view_height = self.convert_to_mm(view_root.attrib.get("height"))
view = ET.SubElement(layout_root, "g")
view.attrib["data-type"] = "schedule"
view.attrib["data-id"] = str(reference.id())
view.attrib["data-schedule"] = str(schedule.id())
view.attrib["data-type"] = document.Scope.lower()
view.attrib["data-document"] = str(document.id())
foreground = ET.SubElement(view, "image")
foreground.attrib["data-type"] = "table"
foreground.attrib["data-type"] = "content"
foreground.attrib["xlink:href"] = os.path.relpath(view_path, layout_dir)
foreground.attrib["x"] = str(DEFAULT_POSITION.x)
foreground.attrib["y"] = str(DEFAULT_POSITION.y)
@@ -263,7 +263,7 @@ class SheetBuilder:
self.build_titleblock(root, sheet)
self.build_drawings(root, sheet)
self.build_schedules(root, sheet)
self.build_documents(root, sheet)
with open(sheet_path, "wb") as output:
tree.write(output)
@@ -390,11 +390,14 @@ class SheetBuilder:
for image in images:
view.remove(image)
def build_schedules(self, root, sheet):
for view in root.findall('{http://www.w3.org/2000/svg}g[@data-type="schedule"]'):
def build_documents(self, root, sheet):
schedules = root.findall('{http://www.w3.org/2000/svg}g[@data-type="schedule"]')
references = root.findall('{http://www.w3.org/2000/svg}g[@data-type="reference"]')
documents = schedules + references
for view in documents:
try:
reference = tool.Ifc.get().by_id(int(view.attrib["data-id"]))
schedule = tool.Ifc.get().by_id(int(view.attrib["data-schedule"]))
document = tool.Ifc.get().by_id(int(view.attrib["data-document"]))
except:
# Perhaps the SVG has outdated content or is edited externally which we cannot control.
continue
@@ -405,7 +408,7 @@ class SheetBuilder:
view_title = None
for image in images:
if image.attrib["data-type"] == "table":
if image.attrib["data-type"] == "content":
table = image
elif image.attrib["data-type"] == "view-title":
view_title = image
@@ -418,7 +421,7 @@ class SheetBuilder:
data = reference.get_info()
data.update({"Sheet" + k: v for k, v in sheet.get_info().items()})
if not data["Name"]:
data["Name"] = schedule.Name or "Unnamed"
data["Name"] = document.Name or "Unnamed"
view.append(self.parse_embedded_svg(view_title, data))
for image in images:
@@ -27,10 +27,10 @@ classes = (
operator.EnableEditingRepresentationItems,
operator.FlipObject,
operator.GetRepresentationIfcParameters,
operator.DuplicateMoveLinkedAggregate,
operator.DuplicateMoveLinkedAggregateMacro,
operator.OverrideDelete,
operator.OverrideDuplicateMove,
operator.OverrideDuplicateMoveAggregate,
operator.OverrideDuplicateMoveAggregateMacro,
operator.OverrideDuplicateMoveLinked,
operator.OverrideDuplicateMoveLinkedMacro,
operator.OverrideDuplicateMoveMacro,
@@ -42,14 +42,24 @@ classes = (
operator.OverrideOutlinerDelete,
operator.OverridePasteBuffer,
operator.PurgeUnusedRepresentations,
operator.RefreshAggregate,
operator.RefreshLinkedAggregate,
operator.RemoveConnection,
operator.RemoveRepresentation,
operator.SelectConnection,
operator.SwitchRepresentation,
operator.UpdateParametricRepresentation,
operator.UpdateRepresentation,
operator.RemoveRepresentationItem,
operator.EnableEditingRepresentationItemStyle,
operator.EditRepresentationItemStyle,
operator.DisableEditingRepresentationItemStyle,
operator.UnassignRepresentationItemStyle,
operator.EnableEditingRepresentationItemShapeAspect,
operator.EditRepresentationItemShapeAspect,
operator.DisableEditingRepresentationItemShapeAspect,
operator.RemoveRepresentationItemFromShapeAspect,
prop.RepresentationItem,
prop.ShapeAspect,
prop.BIMObjectGeometryProperties,
prop.BIMGeometryProperties,
ui.BIM_PT_placement,
@@ -74,8 +84,8 @@ def register():
operator.OverrideDuplicateMoveMacro.define("TRANSFORM_OT_translate")
operator.OverrideDuplicateMoveLinkedMacro.define("BIM_OT_override_object_duplicate_move_linked")
operator.OverrideDuplicateMoveLinkedMacro.define("TRANSFORM_OT_translate")
operator.OverrideDuplicateMoveAggregateMacro.define("BIM_OT_override_object_duplicate_move_aggregate")
operator.OverrideDuplicateMoveAggregateMacro.define("TRANSFORM_OT_translate")
operator.DuplicateMoveLinkedAggregateMacro.define("BIM_OT_object_duplicate_move_linked_aggregate")
operator.DuplicateMoveLinkedAggregateMacro.define("TRANSFORM_OT_translate")
bpy.types.Object.BIMGeometryProperties = bpy.props.PointerProperty(type=prop.BIMObjectGeometryProperties)
bpy.types.Scene.BIMGeometryProperties = bpy.props.PointerProperty(type=prop.BIMGeometryProperties)
@@ -83,6 +93,7 @@ def register():
bpy.types.OUTLINER_MT_object.append(ui.outliner_menu)
bpy.types.VIEW3D_MT_object_context_menu.append(ui.object_menu)
bpy.types.VIEW3D_MT_edit_mesh.append(ui.edit_mesh_menu)
bpy.types.VIEW3D_HT_header.append(ui.mode_menu)
wm = bpy.context.window_manager
if wm.keyconfigs.addon:
km = wm.keyconfigs.addon.keymaps.new(name="Object Mode", space_type="EMPTY")
@@ -93,7 +104,7 @@ def register():
kmi = km.keymap_items.new("bim.override_object_duplicate_move_linked_macro", "D", "PRESS", alt=True)
addon_keymaps.append((km, kmi))
kmi = km.keymap_items.new(
"bim.override_object_duplicate_move_aggregate_macro", "D", "PRESS", ctrl=True, shift=True
"bim.object_duplicate_move_linked_aggregate_macro", "D", "PRESS", ctrl=True, shift=True
)
addon_keymaps.append((km, kmi))
kmi = km.keymap_items.new("bim.override_paste_buffer", "V", "PRESS", ctrl=True)
@@ -131,6 +142,7 @@ def unregister():
bpy.types.OUTLINER_MT_object.remove(ui.outliner_menu)
bpy.types.VIEW3D_MT_object_context_menu.remove(ui.outliner_menu)
bpy.types.VIEW3D_MT_edit_mesh.remove(ui.edit_mesh_menu)
bpy.types.VIEW3D_HT_header.remove(ui.mode_menu)
del bpy.types.Scene.BIMGeometryProperties
del bpy.types.Object.BIMGeometryProperties
wm = bpy.context.window_manager
@@ -38,6 +38,7 @@ class RepresentationsData:
def load(cls):
cls.data = {"representations": cls.representations()}
cls.data["contexts"] = cls.contexts()
cls.data["shape_aspects"] = cls.shape_aspects()
cls.is_loaded = True
@classmethod
@@ -92,6 +93,24 @@ class RepresentationsData:
)
return results
@classmethod
def shape_aspects(cls):
obj = bpy.context.active_object
element = tool.Ifc.get_entity(obj)
active_representation_id = obj.data.BIMMeshProperties.ifc_definition_id
base_representation = tool.Ifc.get().by_id(active_representation_id)
# shape aspects matching context of the active representation
matching_shape_aspects = []
for shape_aspect in tool.Geometry.get_shape_aspects(element):
matching_representation = tool.Geometry.get_shape_aspect_representation(shape_aspect, base_representation)
if matching_representation:
matching_shape_aspects.append(shape_aspect)
# blender enum items
new_shape_aspect = [("NEW", "Create A New Shape Aspect", "")]
return new_shape_aspect + [(str(s.id()), s.Name or "Unnamed", "") for s in matching_shape_aspects]
class RepresentationItemsData:
data = {}
@@ -759,13 +759,23 @@ class OverrideDuplicateMove(bpy.types.Operator):
self.new_active_obj = None
# Track decompositions so they can be recreated after the operation
relationships = tool.Root.get_decomposition_relationships(objects_to_duplicate)
decomposition_relationships = tool.Root.get_decomposition_relationships(objects_to_duplicate)
connection_relationships = tool.Root.get_connection_relationships(objects_to_duplicate)
old_to_new = {}
for obj in objects_to_duplicate:
element = tool.Ifc.get_entity(obj)
if element and element.is_a("IfcAnnotation") and element.ObjectType == "DRAWING":
continue # For now, don't copy drawings until we stabilise a bit more. It's tricky.
if element:
if element.is_a("IfcAnnotation") and element.ObjectType == "DRAWING":
self.report(
{"INFO"}, "Did not duplicate. Duplicate drawings through the Drawings and Documents UI."
)
obj.select_set(False)
continue # For now, don't copy drawings until we stabilise a bit more. It's tricky.
elif element.is_a("IfcProject"):
self.report({"INFO"}, "Did not duplicate. IFC can only have one project.")
obj.select_set(False)
continue # Only one IfcProject is allowed.
linked_non_ifc_object = linked and not element
@@ -819,9 +829,19 @@ class OverrideDuplicateMove(bpy.types.Operator):
if new.is_a("IfcRelSpaceBoundary"):
tool.Boundary.decorate_boundary(new_obj)
# Recreate aggregate relationship
for old in old_to_new.keys():
if old.is_a("IfcElementAssembly"):
tool.Root.recreate_aggregate(old_to_new)
# Remove connections with old objects and recreates paths
OverrideDuplicateMove.remove_old_connections(old_to_new)
tool.Root.recreate_connections(connection_relationships, old_to_new)
# Recreate decompositions
tool.Root.recreate_decompositions(relationships, old_to_new)
tool.Root.recreate_decompositions(decomposition_relationships, old_to_new)
blenderbim.bim.handler.refresh_ui_data()
return old_to_new
@staticmethod
def process_arrays(self, context):
@@ -859,6 +879,19 @@ class OverrideDuplicateMove(bpy.types.Operator):
return arrays_to_create, array_children
def remove_old_connections(old_to_new):
for new in old_to_new.values():
if not hasattr(new[0], "ConnectedTo"):
continue
for connection in new[0].ConnectedTo:
entity = connection.RelatedElement
if entity in old_to_new.keys():
core.remove_connection(tool.Geometry, connection=connection)
for connection in new[0].ConnectedFrom:
entity = connection.RelatingElement
if entity in old_to_new.keys():
core.remove_connection(tool.Geometry, connection=connection)
class OverrideDuplicateMoveLinkedMacro(bpy.types.Macro):
bl_idname = "bim.override_object_duplicate_move_linked_macro"
@@ -882,15 +915,15 @@ class OverrideDuplicateMoveLinked(bpy.types.Operator):
return OverrideDuplicateMove.execute_ifc_duplicate_operator(self, context, linked=True)
class OverrideDuplicateMoveAggregateMacro(bpy.types.Macro):
bl_idname = "bim.override_object_duplicate_move_aggregate_macro"
bl_label = "IFC Duplicate Objects Aggregate"
class DuplicateMoveLinkedAggregateMacro(bpy.types.Macro):
bl_idname = "bim.object_duplicate_move_linked_aggregate_macro"
bl_label = "IFC Duplicate Linked Aggregate"
bl_options = {"REGISTER", "UNDO"}
class OverrideDuplicateMoveAggregate(bpy.types.Operator):
bl_idname = "bim.override_object_duplicate_move_aggregate"
bl_label = "IFC Duplicate Objects Aggregate"
class DuplicateMoveLinkedAggregate(bpy.types.Operator):
bl_idname = "bim.object_duplicate_move_linked_aggregate"
bl_label = "IFC Duplicate Linked Aggregate"
bl_options = {"REGISTER", "UNDO"}
is_interactive: bpy.props.BoolProperty(name="Is Interactive", default=True)
@@ -902,263 +935,95 @@ class OverrideDuplicateMoveAggregate(bpy.types.Operator):
return OverrideDuplicateMove.execute_duplicate_operator(self, context, linked=False)
def _execute(self, context):
return DuplicateMoveLinkedAggregate.execute_ifc_duplicate_linked_aggregate_operator(self, context)
@staticmethod
def execute_ifc_duplicate_linked_aggregate_operator(self, context):
self.new_active_obj = None
self.group_name = "BBIM_Linked_Aggregate"
self.pset_name = "BBIM_Linked_Aggregate"
old_to_new = {}
### Adding the assembly data
def add_assembly_data(element, parent, data_to_add):
pset = ifcopenshell.util.element.get_pset(element, "BBIM_Aggregate_Data")
data = [data_to_add]
def select_objects_and_add_data(element):
add_linked_aggregate_group(element)
obj = tool.Ifc.get_object(element)
obj.select_set(True)
parts = ifcopenshell.util.element.get_parts(element)
if parts:
index = 0
for part in parts:
if part.is_a("IfcElementAssembly"):
select_objects_and_add_data(part)
if pset:
if parent == None:
ifcopenshell.api.run(
"pset.edit_pset",
tool.Ifc.get(),
pset=tool.Ifc.get().by_id(pset["id"]),
properties={"Data": tool.Ifc.get().createIfcText(json.dumps(data))},
)
else:
ifcopenshell.api.run(
"pset.edit_pset",
tool.Ifc.get(),
pset=tool.Ifc.get().by_id(pset["id"]),
properties={"Parent": parent.GlobalId, "Data": tool.Ifc.get().createIfcText(json.dumps(data))},
)
add_linked_aggregate_pset(part, index)
index += 1
obj = tool.Ifc.get_object(part)
obj.select_set(True)
def add_linked_aggregate_group(element):
linked_aggregate_group = None
product_groups_name = [
r.RelatingGroup.Name
for r in getattr(element, "HasAssignments", []) or []
if r.is_a("IfcRelAssignsToGroup")
]
if self.group_name in product_groups_name:
return
linked_aggregate_group = ifcopenshell.api.run("group.add_group", tool.Ifc.get(), Name=self.group_name)
ifcopenshell.api.run("group.assign_group", tool.Ifc.get(), products=[element], group=linked_aggregate_group)
def add_linked_aggregate_pset(part, index):
pset = ifcopenshell.util.element.get_pset(part, self.pset_name)
if not pset:
pset = ifcopenshell.api.run("pset.add_pset", tool.Ifc.get(), product=part, name=self.pset_name)
else:
pset = ifcopenshell.api.run(
"pset.add_pset", tool.Ifc.get(), product=element, name="BBIM_Aggregate_Data"
)
ifcopenshell.api.run(
"pset.edit_pset",
tool.Ifc.get(),
pset=pset,
properties={"Parent": parent.GlobalId, "Data": tool.Ifc.get().createIfcText(json.dumps(data))},
)
def add_child_to_assembly_data(new_entity):
pset = ifcopenshell.util.element.get_pset(new_entity, "BBIM_Aggregate_Data")
parent_element = tool.Ifc.get().by_guid(pset["Parent"])
parent_pset = ifcopenshell.util.element.get_pset(parent_element, "BBIM_Aggregate_Data")
data = json.loads(parent_pset["Data"])
data[0]["children"].append(new_entity.GlobalId)
pset = tool.Ifc.get().by_id(pset["id"])
ifcopenshell.api.run(
"pset.edit_pset",
tool.Ifc.get(),
pset=tool.Ifc.get().by_id(parent_pset["id"]),
properties={"Data": tool.Ifc.get().createIfcText(json.dumps(data))},
pset=pset,
properties={"Index": index},
)
def create_data_structure(entity, level=-1):
level += 1
return index
data_children = {
"children": [],
"instance_of": [],
}
data_parent = {
"children": [],
"instance_of": [entity.GlobalId],
}
if not entity.is_a("IfcElementAssembly"):
return
else:
if level == 0:
add_assembly_data(entity, entity, data_parent)
else:
add_assembly_data(entity, None, data_parent)
parts = ifcopenshell.util.element.get_parts(entity)
for part in parts:
if part.is_a("IfcElementAssembly"):
add_assembly_data(part, entity, data_children)
add_child_to_assembly_data(part)
create_data_structure(part, level)
continue
add_assembly_data(part, entity, data_children)
add_child_to_assembly_data(part)
return
def recreate_data_structure(entity, level=-1):
level += 1
pset = ifcopenshell.util.element.get_pset(entity, "BBIM_Aggregate_Data")
pset_data = json.loads(pset["Data"])[0]
instance_of = pset_data["instance_of"]
data_children = {
"children": [],
"instance_of": [],
}
# Keeps instance ID through all copies
data_parent = {
"children": [],
"instance_of": instance_of,
}
if not entity.is_a("IfcElementAssembly"):
return
else:
if level == 0:
add_assembly_data(entity, entity, data_parent)
else:
add_assembly_data(entity, None, data_parent)
parts = ifcopenshell.util.element.get_parts(entity)
for part in parts:
if part.is_a("IfcElementAssembly"):
add_child_to_assembly_data(part)
recreate_data_structure(part, level)
continue
add_assembly_data(part, entity, data_children)
add_child_to_assembly_data(part)
return
def duplicate_all(obj, level=-1, new_parent=None, parents=[]):
level += 1
entity = tool.Ifc.get_entity(obj)
if level == 0:
new_parent = duplicate_objects(obj)
parents.append(new_parent)
else:
pair = []
pair.append(new_parent)
new_parent = duplicate_objects(obj)
pair.append(new_parent)
parents.append(pair)
if not entity.is_a("IfcElementAssembly"):
return
else:
parts = ifcopenshell.util.element.get_parts(entity)
# Ensures that we are duplication all the IfcElementAssembly
# before duplicating the nested objects
for part in parts:
if part.is_a("IfcElementAssembly"):
pass
else:
part_obj = tool.Ifc.get_object(part)
new_part = duplicate_objects(part_obj)
blenderbim.core.aggregate.assign_object(
tool.Ifc,
tool.Aggregate,
tool.Collector,
relating_obj=tool.Ifc.get_object(new_parent),
related_obj=tool.Ifc.get_object(new_part),
)
for part in parts:
if part.is_a("IfcElementAssembly"):
part_obj = tool.Ifc.get_object(part)
# Recursion Call
duplicate_all(part_obj, level, new_parent, parents)
if level == 0:
for p in parents[1:]:
blenderbim.core.aggregate.assign_object(
tool.Ifc,
tool.Aggregate,
tool.Collector,
relating_obj=tool.Ifc.get_object(p[0]),
related_obj=tool.Ifc.get_object(p[1]),
)
return new_parent
return
def duplicate_objects(obj, is_root=False):
new_obj = obj.copy()
if obj.data:
new_obj.data = obj.data.copy()
if obj == context.active_object:
self.new_active_obj = new_obj
for collection in obj.users_collection:
collection.objects.link(new_obj)
obj.select_set(False)
new_obj.select_set(True)
# This is needed to make sure the new object gets unlink from
# the old object assembly collection
new_obj.BIMObjectProperties.collection = None
# Copy the actual class
new_entity = blenderbim.core.root.copy_class(
tool.Ifc, tool.Collector, tool.Geometry, tool.Root, obj=new_obj
)
if new_entity:
tool.Model.handle_array_on_copied_element(new_entity)
blenderbim.core.aggregate.unassign_object(
tool.Ifc,
tool.Aggregate,
tool.Collector,
relating_obj=tool.Ifc.get_object(selected_root_entity),
related_obj=tool.Ifc.get_object(new_entity),
)
old_to_new[tool.Ifc.get_entity(obj)] = [new_entity]
return new_entity
### Check if only one element and it's assembly
if len(context.selected_objects) != 1:
return {"FINISHED"}
selected_obj = context.selected_objects[0]
selected_root_entity = tool.Ifc.get_entity(selected_obj)
selected_element = tool.Ifc.get_entity(selected_obj)
if not selected_root_entity.is_a("IfcElementAssembly"):
if selected_element.is_a("IfcElementAssembly"):
pass
elif selected_element.Decomposes:
if selected_element.Decomposes[0].RelatingObject.is_a("IfcElementAssembly"):
selected_element = selected_element.Decomposes[0].RelatingObject
selected_obj = tool.Ifc.get_object(selected_element)
else:
self.report({"INFO"}, "Object is not part of a IfcElementAssembly.")
return {"FINISHED"}
pset = ifcopenshell.util.element.get_pset(selected_root_entity, "BBIM_Aggregate_Data")
if not pset:
create_data_structure(selected_root_entity)
select_objects_and_add_data(selected_element)
pset = ifcopenshell.util.element.get_pset(selected_root_entity, "BBIM_Aggregate_Data")
old_to_new = OverrideDuplicateMove.execute_ifc_duplicate_operator(self, context, linked=True)
selected_root_parent = selected_root_entity
new_root_entity = duplicate_all(selected_obj)
recreate_data_structure(new_root_entity)
# Remove connections with old objects
for new in old_to_new.values():
for connection in new[0].ConnectedTo:
entity = connection.RelatedElement
if entity in old_to_new.keys():
core.remove_connection(tool.Geometry, connection=connection)
for connection in new[0].ConnectedFrom:
entity = connection.RelatingElement
if entity in old_to_new.keys():
core.remove_connection(tool.Geometry, connection=connection)
old_objs = []
for old, new in old_to_new.items():
old_objs.append(tool.Ifc.get_object(old))
relationships = tool.Root.get_decomposition_relationships(old_objs)
tool.Root.recreate_decompositions(relationships, old_to_new)
# Recreate aggregate relationship
for old in old_to_new.keys():
if old.is_a("IfcElementAssembly"):
tool.Root.recreate_aggregate(old_to_new)
blenderbim.bim.handler.refresh_ui_data()
return {"FINISHED"}
return old_to_new
class RefreshAggregate(bpy.types.Operator):
bl_idname = "bim.refresh_aggregate"
bl_label = "IFC Refresh Aggregate"
class RefreshLinkedAggregate(bpy.types.Operator):
bl_idname = "bim.refresh_linked_aggregate"
bl_label = "IFC Refresh Linked Aggregate"
bl_options = {"REGISTER", "UNDO"}
@classmethod
@@ -1174,213 +1039,134 @@ class RefreshAggregate(bpy.types.Operator):
return {"FINISHED"}
def _execute(self, context):
refresh_start_time = time()
self.new_active_obj = None
self.group_name = "BBIM_Linked_Aggregate"
self.pset_name = "BBIM_Linked_Aggregate"
refresh_start_time = time()
old_to_new = {}
def remove_objects(entity, level=-1, parents=[]):
level += 1
if level == 0:
parents = [entity]
parts = ifcopenshell.util.element.get_parts(entity)
for part in parts:
if part.is_a("IfcElementAssembly"):
parents.append(part)
remove_objects(part, level, parents)
continue
else:
part_obj = tool.Ifc.get_object(part)
if part_obj:
tool.Geometry.delete_ifc_object(part_obj)
return parents
def duplicate_children(entity):
pset = ifcopenshell.util.element.get_pset(entity, "BBIM_Aggregate_Data")
pset_data = json.loads(pset["Data"])[0]
instance_of = pset_data["instance_of"][0]
instance_entity = tool.Ifc.get().by_guid(instance_of)
if not instance_entity.is_a("IfcElementAssembly"):
return
else:
parts = ifcopenshell.util.element.get_parts(instance_entity)
def delete_objects(element):
parts = ifcopenshell.util.element.get_parts(element)
if parts:
for part in parts:
pset = ifcopenshell.util.element.get_pset(part, "BBIM_Aggregate_Data")
if part.is_a("IfcElementAssembly"):
if not pset:
data_children = {
"children": [],
"instance_of": [part.GlobalId],
}
data = [data_children]
delete_objects(part)
pset = ifcopenshell.api.run(
"pset.add_pset", tool.Ifc.get(), product=part, name="BBIM_Aggregate_Data"
)
ifcopenshell.api.run(
"pset.edit_pset",
tool.Ifc.get(),
pset=pset,
properties={
"Parent": instance_entity.GlobalId,
"Data": tool.Ifc.get().createIfcText(json.dumps(data)),
},
)
tool.Geometry.delete_ifc_object(tool.Ifc.get_object(part))
part_obj = tool.Ifc.get_object(part)
new_part = duplicate_objects(part_obj)
tool.Geometry.delete_ifc_object(tool.Ifc.get_object(element))
def get_element_assembly(element):
if element.is_a("IfcElementAssembly"):
return element
elif element.Decomposes:
if element.Decomposes[0].RelatingObject.is_a("IfcElementAssembly"):
element = element.Decomposes[0].RelatingObject
return element
else:
return None
def handle_selection(selected_objs):
selected_elements = [tool.Ifc.get_entity(selected_obj) for selected_obj in selected_objs]
if None in selected_elements:
self.report({"INFO"}, "Object has no Ifc Metadata.")
return None, None
selected_parents = []
for selected_element in selected_elements:
selected_element = get_element_assembly(selected_element)
if not selected_element:
self.report({"INFO"}, "Object is not part of a IfcElementAssembly.")
return None, None
selected_parents.append(selected_element)
selected_parents = list(set(selected_parents))
linked_aggregate_groups = []
for selected_element in selected_parents:
product_linked_agg_group = [
r.RelatingGroup
for r in getattr(selected_element, "HasAssignments", []) or []
if r.is_a("IfcRelAssignsToGroup")
if self.group_name in r.RelatingGroup.Name
]
try:
linked_aggregate_groups.append(product_linked_agg_group[0].id())
except:
self.report({"INFO"}, "Object is not part of a Linked Aggregate.")
return None, None
return list(set(linked_aggregate_groups)), selected_parents
active_element = tool.Ifc.get_entity(context.active_object)
if not active_element:
self.report({"INFO"}, "Object has no Ifc metadata.")
return {"FINISHED"}
active_element = get_element_assembly(active_element)
selected_objs = context.selected_objects
linked_aggregate_groups, selected_parents = handle_selection(selected_objs)
if not linked_aggregate_groups or not selected_parents:
return {"FINISHED"}
if len(linked_aggregate_groups) > 1:
if len(selected_parents) != len(linked_aggregate_groups):
self.report(
{"INFO"},
"Select only one object from each Linked Aggregate or multiple objects from the same Linked Aggregate.",
)
return {"FINISHED"}
for group in linked_aggregate_groups:
elements = tool.Drawing.get_group_elements(tool.Ifc.get().by_id(group))
if len(linked_aggregate_groups) > 1:
base_instance = [e for e in elements if e in selected_parents][0]
instances_to_refresh = elements
elif (len(linked_aggregate_groups) == 1) and (len(selected_parents) > 1):
base_instance = active_element
instances_to_refresh = [element for element in elements if element in selected_parents]
else:
base_instance = active_element
instances_to_refresh = elements
for element in instances_to_refresh:
if element.GlobalId == base_instance.GlobalId:
continue
element_aggregate = ifcopenshell.util.element.get_aggregate(element)
selected_obj = tool.Ifc.get_object(base_instance)
selected_matrix = selected_obj.matrix_world
object_duplicate = tool.Ifc.get_object(element)
duplicate_matrix = object_duplicate.matrix_world.decompose()
delete_objects(element)
for obj in context.selected_objects:
obj.select_set(False)
tool.Ifc.get_object(base_instance).select_set(True)
old_to_new = DuplicateMoveLinkedAggregate.execute_ifc_duplicate_linked_aggregate_operator(self, context)
for old, new in old_to_new.items():
new_obj = tool.Ifc.get_object(new[0])
new_base_matrix = Matrix.LocRotScale(*duplicate_matrix)
matrix_diff = Matrix.inverted(selected_matrix) @ new_obj.matrix_world
new_obj_matrix = new_base_matrix @ matrix_diff
new_obj.matrix_world = new_obj_matrix
if element_aggregate and new[0].is_a("IfcElementAssembly"):
new_aggregate = ifcopenshell.util.element.get_aggregate(new[0])
if not new_aggregate:
blenderbim.core.aggregate.assign_object(
tool.Ifc,
tool.Aggregate,
tool.Collector,
relating_obj=tool.Ifc.get_object(entity),
related_obj=tool.Ifc.get_object(new_part),
relating_obj=tool.Ifc.get_object(element_aggregate),
related_obj=tool.Ifc.get_object(new[0]),
)
duplicate_children(new_part)
for part in parts:
pset = ifcopenshell.util.element.get_pset(part, "BBIM_Aggregate_Data")
if part.is_a("IfcElementAssembly"):
pass
else:
if not pset:
pset = ifcopenshell.api.run(
"pset.add_pset", tool.Ifc.get(), product=part, name="BBIM_Aggregate_Data"
)
else:
pset = ifcopenshell.util.element.get_pset(part, "BBIM_Aggregate_Data")
pset = tool.Ifc.get().by_id(pset["id"])
data_children = {
"children": [],
"instance_of": [],
}
data = [data_children]
ifcopenshell.api.run(
"pset.edit_pset",
tool.Ifc.get(),
pset=pset,
properties={
"Parent": instance_entity.GlobalId,
"Data": tool.Ifc.get().createIfcText(json.dumps(data)),
},
)
part_obj = tool.Ifc.get_object(part)
new_part = duplicate_objects(part_obj)
blenderbim.core.aggregate.assign_object(
tool.Ifc,
tool.Aggregate,
tool.Collector,
relating_obj=tool.Ifc.get_object(entity),
related_obj=tool.Ifc.get_object(new_part),
)
def duplicate_objects(obj, is_root=False):
new_obj = obj.copy()
if obj.data:
new_obj.data = obj.data.copy()
if obj == context.active_object:
self.new_active_obj = new_obj
for collection in obj.users_collection:
collection.objects.link(new_obj)
obj.select_set(False)
new_obj.select_set(True)
# This is needed to make sure the new object gets unlink from
# the old object assembly collection
new_obj.BIMObjectProperties.collection = None
# Copy the actual class
new_entity = blenderbim.core.root.copy_class(
tool.Ifc, tool.Collector, tool.Geometry, tool.Root, obj=new_obj
)
if new_entity:
tool.Model.handle_array_on_copied_element(new_entity)
if not new_entity.is_a("IfcElementAssembly"):
blenderbim.core.aggregate.unassign_object(
tool.Ifc,
tool.Aggregate,
tool.Collector,
relating_obj=obj,
related_obj=tool.Ifc.get_object(new_entity),
)
old_to_new[tool.Ifc.get_entity(obj)] = [new_entity]
return new_entity
if len(context.selected_objects) != 1:
self.report({"INFO"}, "Only 1 object need to be selected.")
return {"FINISHED"}
selected_root_obj = context.selected_objects[0]
selected_root_entity = tool.Ifc.get_entity(selected_root_obj)
if selected_root_entity.is_a("IfcElementAssembly"):
pass
elif selected_root_entity.Decomposes:
if selected_root_entity.Decomposes[0].RelatingObject.is_a("IfcElementAssembly"):
selected_root_entity = selected_root_entity.Decomposes[0].RelatingObject
selected_root_obj = tool.Ifc.get_object(selected_root_entity)
else:
self.report({"INFO"}, "Object is not part of a IfcElementAssembly.")
return {"FINISHED"}
pset = ifcopenshell.util.element.get_pset(selected_root_entity, "BBIM_Aggregate_Data")
if not pset:
self.report({"INFO"}, "Object is not part of an assembly aggregate.")
return {"FINISHED"}
pset_data = json.loads(pset["Data"])[0]
instance_of = pset_data["instance_of"][0]
original_root_entity = tool.Ifc.get().by_guid(instance_of)
if original_root_entity == selected_root_entity:
self.report({"INFO"}, "Cannot refresh original assembly. Select an assembly instance.")
return {"FINISHED"}
parents = remove_objects(selected_root_entity)
original_root_object = tool.Ifc.get_object(original_root_entity)
selected_matrix = selected_root_obj.matrix_world
original_matrix = original_root_object.matrix_world
for parent in parents:
duplicate_children(parent)
# Remove connections with old objects
for new in old_to_new.values():
for connection in new[0].ConnectedTo:
entity = connection.RelatedElement
if entity in old_to_new.keys():
core.remove_connection(tool.Geometry, connection=connection)
for connection in new[0].ConnectedFrom:
entity = connection.RelatingElement
if entity in old_to_new.keys():
core.remove_connection(tool.Geometry, connection=connection)
old_objs = []
for old, new in old_to_new.items():
old_objs.append(tool.Ifc.get_object(old))
new_obj = tool.Ifc.get_object(new[0])
matrix_diff = Matrix.inverted(original_matrix) @ new_obj.matrix_world
new_matrix = selected_matrix @ matrix_diff
new_obj.matrix_world = new_matrix
relationships = tool.Root.get_decomposition_relationships(old_objs)
tool.Root.recreate_decompositions(relationships, old_to_new)
blenderbim.bim.handler.refresh_ui_data()
@@ -1495,7 +1281,12 @@ class OverridePasteBuffer(bpy.types.Operator):
bpy.ops.view3d.pastebuffer()
if IfcStore.get_file():
for obj in context.selected_objects:
blenderbim.core.root.copy_class(tool.Ifc, tool.Collector, tool.Geometry, tool.Root, obj=obj)
# Pasted objects may come from another Blender session, or even
# from the same session where the original object has since
# been deleted. As the source element may not exist, paste will
# always unlink the element. If you want to duplicate an
# element, use the duplicate commands.
tool.Root.unlink_object(obj)
return {"FINISHED"}
@@ -1607,7 +1398,12 @@ class OverrideModeSetEdit(bpy.types.Operator):
# The active object is non-mesh-like. Set a valid object (or None) as active
context.view_layer.objects.active = context.selected_objects[0] if context.selected_objects else None
if context.active_object:
return tool.Blender.toggle_edit_mode(context)
tool.Blender.toggle_edit_mode(context)
context.scene.BIMGeometryProperties.is_changing_mode = True
if context.scene.BIMGeometryProperties.mode != "EDIT":
context.scene.BIMGeometryProperties.mode = "EDIT"
context.scene.BIMGeometryProperties.is_changing_mode = False
return {"FINISHED"}
# Restore the selection if nothing worked
for obj in selected_objs:
@@ -1678,6 +1474,11 @@ class OverrideModeSetObject(bpy.types.Operator):
bpy.ops.object.mode_set(mode="EDIT", toggle=True)
context.scene.BIMGeometryProperties.is_changing_mode = True
if context.scene.BIMGeometryProperties.mode != "OBJECT":
context.scene.BIMGeometryProperties.mode = "OBJECT"
context.scene.BIMGeometryProperties.is_changing_mode = False
if not tool.Ifc.get():
return {"FINISHED"}
@@ -1791,6 +1592,11 @@ class EnableEditingRepresentationItems(bpy.types.Operator, Operator):
new.surface_style = style.Name or "Unnamed"
elif inverse.is_a("IfcPresentationLayerAssignment"):
new.layer = inverse.Name or "Unnamed"
elif inverse.is_a("IfcShapeRepresentation"):
if inverse.OfShapeAspect:
shape_aspect = inverse.OfShapeAspect[0]
new.shape_aspect = shape_aspect.Name
new.shape_aspect_id = shape_aspect.id()
class DisableEditingRepresentationItems(bpy.types.Operator, Operator):
@@ -1801,3 +1607,196 @@ class DisableEditingRepresentationItems(bpy.types.Operator, Operator):
def _execute(self, context):
obj = context.active_object
obj.BIMGeometryProperties.is_editing = False
class RemoveRepresentationItem(bpy.types.Operator, Operator):
bl_idname = "bim.remove_representation_item"
bl_label = "Remove Representation Item"
bl_options = {"REGISTER", "UNDO"}
representation_item_id: bpy.props.IntProperty()
@classmethod
def poll(cls, context):
if context.active_object is None or len(context.active_object.BIMGeometryProperties.items) <= 1:
cls.poll_message_set(
"Active object need to have more than 1 representation items to keep representation valid"
)
return False
return True
def _execute(self, context):
obj = context.active_object
props = obj.BIMGeometryProperties
ifc_file = tool.Ifc.get()
representation_item = ifc_file.by_id(self.representation_item_id)
tool.Geometry.remove_representation_item(representation_item)
tool.Geometry.reload_representation(obj)
# reload style ui
bpy.ops.bim.disable_editing_representation_items()
bpy.ops.bim.enable_editing_representation_items()
class EnableEditingRepresentationItemStyle(bpy.types.Operator, Operator):
bl_idname = "bim.enable_editing_representation_item_style"
bl_label = "Enable Editing Representation Item Style"
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
props = context.active_object.BIMGeometryProperties
props.is_editing_item_style = True
ifc_file = tool.Ifc.get()
# set dropdown to currently active style
representation_item_id = props.items[props.active_item_index].ifc_definition_id
representation_item = ifc_file.by_id(representation_item_id)
style = tool.Style.get_representation_item_style(representation_item)
if style:
props.representation_item_style = str(style.id())
class EditRepresentationItemStyle(bpy.types.Operator, Operator):
bl_idname = "bim.edit_representation_item_style"
bl_label = "Edit Representation Item Style"
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
obj = context.active_object
props = obj.BIMGeometryProperties
props.is_editing_item_style = False
ifc_file = tool.Ifc.get()
surface_style = ifc_file.by_id(int(props.representation_item_style))
representation_item_id = props.items[props.active_item_index].ifc_definition_id
representation_item = ifc_file.by_id(representation_item_id)
tool.Style.assign_style_to_representation_item(representation_item, surface_style)
tool.Geometry.reload_representation(obj)
# reload style ui
bpy.ops.bim.disable_editing_representation_items()
bpy.ops.bim.enable_editing_representation_items()
class DisableEditingRepresentationItemStyle(bpy.types.Operator, Operator):
bl_idname = "bim.disable_editing_representation_item_style"
bl_label = "Disable Editing Representation Item Style"
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
props = context.active_object.BIMGeometryProperties
props.is_editing_item_style = False
class UnassignRepresentationItemStyle(bpy.types.Operator, Operator):
bl_idname = "bim.unassign_representation_item_style"
bl_label = "Unassign Representation Item Style"
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
obj = context.active_object
props = obj.BIMGeometryProperties
props.is_editing_item_style = False
# get active representation item
representation_item_id = props.items[props.active_item_index].ifc_definition_id
representation_item = tool.Ifc.get_entity_by_id(representation_item_id)
if not representation_item:
self.report({"ERROR"}, f"Couldn't find representation item by id {representation_item_id}.")
return {"CANCELLED"}
tool.Style.assign_style_to_representation_item(representation_item, None)
tool.Geometry.reload_representation(obj)
# reload style ui
bpy.ops.bim.disable_editing_representation_items()
bpy.ops.bim.enable_editing_representation_items()
class EnableEditingRepresentationItemShapeAspect(bpy.types.Operator, Operator):
bl_idname = "bim.enable_editing_representation_item_shape_aspect"
bl_label = "Enable Editing Representation Item Shape Aspect"
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
props = context.active_object.BIMGeometryProperties
props.is_editing_item_shape_aspect = True
# set dropdown to currently active shape aspect
shape_aspect_id = props.items[props.active_item_index].shape_aspect_id
if shape_aspect_id != 0:
props.representation_item_shape_aspect = str(shape_aspect_id)
class EditRepresentationItemShapeAspect(bpy.types.Operator, Operator):
bl_idname = "bim.edit_representation_item_shape_aspect"
bl_label = "Edit Representation Item Shape Aspect"
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
obj = context.active_object
element = tool.Ifc.get_entity(obj)
props = obj.BIMGeometryProperties
props.is_editing_item_shape_aspect = False
ifc_file = tool.Ifc.get()
representation_item_id = props.items[props.active_item_index].ifc_definition_id
representation_item = ifc_file.by_id(representation_item_id)
if props.representation_item_shape_aspect == "NEW":
active_representation = tool.Geometry.get_active_representation(obj)
# find IfcProductRepresentationSelect based on current representation
if hasattr(element, "Representation"): # IfcProduct
product_shape = element.Representation
else: # IfcTypeProduct
for representation_map in element.RepresentationMaps:
if representation_map.MappedRepresentation == active_representation:
product_shape = representation_map
previous_shape_aspect_id = props.items[props.active_item_index].shape_aspect_id
# will be None if item didn't had a shape aspect
previous_shape_aspect = tool.Ifc.get_entity_by_id(previous_shape_aspect_id)
shape_aspect = tool.Geometry.create_shape_aspect(
product_shape, active_representation, [representation_item], previous_shape_aspect
)
else:
shape_aspect = ifc_file.by_id(int(props.representation_item_shape_aspect))
tool.Geometry.add_representation_item_to_shape_aspect([representation_item], shape_aspect)
# set attributes from UI
shape_aspect_attrs = props.shape_aspect_attrs
shape_aspect.Name = shape_aspect_attrs.name
shape_aspect.Description = shape_aspect_attrs.description
# reload style ui
bpy.ops.bim.disable_editing_representation_items()
bpy.ops.bim.enable_editing_representation_items()
class DisableEditingRepresentationItemShapeAspect(bpy.types.Operator, Operator):
bl_idname = "bim.disable_editing_representation_item_shape_aspect"
bl_label = "Disable Editing Representation Item Shape Aspect"
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
props = context.active_object.BIMGeometryProperties
props.is_editing_item_shape_aspect = False
class RemoveRepresentationItemFromShapeAspect(bpy.types.Operator, Operator):
bl_idname = "bim.remove_representation_item_from_shape_aspect"
bl_label = "Remove Representation Item From Shape Aspect"
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
obj = context.active_object
props = obj.BIMGeometryProperties
ifc_file = tool.Ifc.get()
representation_item_id = props.items[props.active_item_index].ifc_definition_id
representation_item = ifc_file.by_id(representation_item_id)
shape_aspect = ifc_file.by_id(props.items[props.active_item_index].shape_aspect_id)
tool.Geometry.remove_representation_items_from_shape_aspect([representation_item], shape_aspect)
# reload style ui
bpy.ops.bim.disable_editing_representation_items()
bpy.ops.bim.enable_editing_representation_items()
@@ -17,6 +17,7 @@
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import bpy
import blenderbim.tool as tool
from blenderbim.bim.prop import StrProperty, Attribute
from blenderbim.bim.module.geometry.data import RepresentationsData
from bpy.types import PropertyGroup
@@ -38,11 +39,69 @@ def get_contexts(self, context):
return RepresentationsData.data["contexts"]
def update_mode(self, context):
if self.is_changing_mode:
return
if self.mode == "OBJECT":
bpy.ops.bim.override_mode_set_object("INVOKE_DEFAULT")
elif self.mode == "EDIT":
bpy.ops.bim.override_mode_set_edit("INVOKE_DEFAULT")
def get_styles(self, context):
# postponed import to avoid circular import
from blenderbim.bim.module.material.data import MaterialsData
if not MaterialsData.is_loaded:
MaterialsData.load()
return MaterialsData.data["styles"]
def get_shape_aspects(self, context):
if not RepresentationsData.is_loaded:
RepresentationsData.load()
return RepresentationsData.data["shape_aspects"]
def get_material_constituents(self, context, edit_text):
# TODO: cache in data.py
return [m.Name for m in tool.Ifc.get().by_type("IfcMaterialConstituent") if m.Name]
def update_shape_aspect(self, context):
shape_aspect_id = self.representation_item_shape_aspect
attrs = self.shape_aspect_attrs
if shape_aspect_id == "NEW": # new shape aspect
attrs.name = tool.Blender.get_blender_prop_default_value(attrs, "name")
attrs.description = tool.Blender.get_blender_prop_default_value(attrs, "description")
else:
shape_aspect = tool.Ifc.get().by_id(int(shape_aspect_id))
attrs.name = shape_aspect.Name or ""
attrs.description = shape_aspect.Description or ""
class RepresentationItem(PropertyGroup):
name: StringProperty(name="Name")
surface_style: StringProperty(name="Surface Style")
layer: StringProperty(name="Layer")
ifc_definition_id: IntProperty(name="IFC Definition ID")
shape_aspect: StringProperty(name="Shape Aspect")
shape_aspect_id: IntProperty(name="Shape Aspect IFC ID")
class ShapeAspect(PropertyGroup):
name: StringProperty(
name="Name",
description=(
"Note that IfcMaterialConstituent is applied based on shape aspects using the same name as material constituent.\n"
"In dropdown suggestions you can see names of existing material constituents."
),
search=get_material_constituents,
)
description: StringProperty(
name="Description",
)
class BIMObjectGeometryProperties(PropertyGroup):
@@ -50,6 +109,13 @@ class BIMObjectGeometryProperties(PropertyGroup):
is_editing: BoolProperty(name="Is Editing", default=False)
items: CollectionProperty(name="Representation Items", type=RepresentationItem)
active_item_index: IntProperty(name="Active Representation Item Index")
is_editing_item_style: BoolProperty(name="Is Editing Item's Style", default=False)
representation_item_style: EnumProperty(items=get_styles, name="Representation Item Style")
is_editing_item_shape_aspect: BoolProperty(name="Is Editing Item's Shape Aspect", default=False)
representation_item_shape_aspect: EnumProperty(
items=get_shape_aspects, name="Representation Item Shape Aspect", update=update_shape_aspect
)
shape_aspect_attrs: PointerProperty(type=ShapeAspect)
class BIMGeometryProperties(PropertyGroup):
@@ -59,3 +125,13 @@ class BIMGeometryProperties(PropertyGroup):
should_force_faceted_brep: BoolProperty(name="Force Faceted Breps", default=False)
# Navisworks workaround
should_force_triangulation: BoolProperty(name="Force Triangulation", default=False)
is_changing_mode: BoolProperty(name="Is Changing Mode", default=False)
mode: EnumProperty(
default="OBJECT",
items=[
("OBJECT", "IFC Object Mode", "", "OBJECT_DATAMODE", 0),
("EDIT", "IFC Edit Mode", "", "EDITMODE_HLT", 1),
],
name="IFC Interaction Mode",
update=update_mode,
)
@@ -29,6 +29,12 @@ from blenderbim.bim.module.geometry.data import (
PlacementData,
DerivedCoordinatesData,
)
from blenderbim.bim.module.layer.data import LayersData
def mode_menu(self, context):
row = self.layout.row(align=True)
row.prop(context.scene.BIMGeometryProperties, "mode", text="", icon_value=blenderbim.bim.icons["IFC"].icon_id)
def object_menu(self, context):
@@ -128,6 +134,7 @@ class BIM_PT_representations(Panel):
if not RepresentationsData.data["representations"]:
layout.label(text="No Representations Found")
return
for representation in RepresentationsData.data["representations"]:
row = self.layout.row(align=True)
@@ -145,6 +152,18 @@ class BIM_PT_representations(Panel):
op.ifc_definition_id = representation["id"]
op.disable_opening_subtractions = False
row.operator("bim.remove_representation", icon="X", text="").representation_id = representation["id"]
if representation["is_active"]:
active_representation = representation
layout.separator()
if not LayersData.is_loaded:
LayersData.load()
if LayersData.data["active_layers"]:
layout.label(text="Representation Presentation Layers:")
for layer_name in LayersData.data["active_layers"].values():
layout.label(text=layer_name, icon="STICKY_UVS_LOC")
else:
layout.label(text="Representation Has No Presentation Layers", icon="STICKY_UVS_LOC")
class BIM_PT_representation_items(Panel):
@@ -182,20 +201,47 @@ class BIM_PT_representation_items(Panel):
self.layout.template_list("BIM_UL_representation_items", "", props, "items", props, "active_item_index")
item_is_active = props.active_item_index < len(props.items)
if item_is_active:
surface_style = props.items[props.active_item_index].surface_style
presentation_layer = props.items[props.active_item_index].layer
if not item_is_active:
return
active_item = props.items[props.active_item_index]
surface_style = active_item.surface_style
shape_aspect = active_item.shape_aspect
row = self.layout.row(align=True)
if props.is_editing_item_style:
# NOTE: we currently support 1 item having just 1 style
# when IfcStyledItem can actually have multiple styles
row.prop(props, "representation_item_style", icon="SHADING_RENDERED", text="")
row.operator("bim.edit_representation_item_style", icon="CHECKMARK", text="")
row.operator("bim.disable_editing_representation_item_style", icon="CANCEL", text="")
else:
surface_style, presentation_layer = None, None
if surface_style:
row.label(text=surface_style, icon="SHADING_RENDERED")
else:
row.label(text="No Surface Style", icon="MESH_UVSPHERE")
row.operator("bim.enable_editing_representation_item_style", icon="GREASEPENCIL", text="")
if surface_style:
row.operator("bim.unassign_representation_item_style", icon="X", text="")
row = self.layout.row()
if surface_style:
row.label(text=surface_style, icon="MATERIAL")
else:
row.label(text="No Surface Style", icon="MESH_UVSPHERE")
row.label(text=active_item.layer or "No Presentation Layer", icon="STICKY_UVS_LOC")
row = self.layout.row()
row.label(text=presentation_layer or "No Presentation Layer", icon="STICKY_UVS_LOC")
row = self.layout.row(align=True)
if props.is_editing_item_shape_aspect:
row.prop(props, "representation_item_shape_aspect", icon="SHAPEKEY_DATA", text="")
row.operator("bim.edit_representation_item_shape_aspect", icon="CHECKMARK", text="")
row.operator("bim.disable_editing_representation_item_shape_aspect", icon="CANCEL", text="")
shape_aspect_attrs = props.shape_aspect_attrs
self.layout.label(text="Shape Aspect Attributes:")
self.layout.prop(shape_aspect_attrs, "name")
self.layout.prop(shape_aspect_attrs, "description")
else:
row.label(text=shape_aspect or "No Shape Aspect", icon="SHAPEKEY_DATA")
row.operator("bim.enable_editing_representation_item_shape_aspect", icon="GREASEPENCIL", text="")
if shape_aspect:
row.operator("bim.remove_representation_item_from_shape_aspect", icon="X", text="")
class BIM_PT_connections(Panel):
@@ -464,3 +510,5 @@ class BIM_UL_representation_items(UIList):
row.label(text=item.name, icon=icon)
if item.layer:
row.label(text="", icon="STICKY_UVS_LOC")
op = row.operator("bim.remove_representation_item", icon="X", text="")
op.representation_item_id = item.ifc_definition_id
@@ -18,7 +18,7 @@
import bpy
from blenderbim.bim.prop import StrProperty, Attribute
from blenderbim.bim.helper import import_attributes
from blenderbim.bim.module.pset.data import refresh as refresh_pset
from bpy.types import PropertyGroup
import json
from bpy.props import (
@@ -33,6 +33,10 @@ from bpy.props import (
)
def update_active_group_index(self, context):
refresh_pset()
class ExpandedGroups(StrProperty):
json_string: StringProperty(name="JSON String", default="[]")
@@ -50,5 +54,5 @@ class BIMGroupProperties(PropertyGroup):
group_attributes: CollectionProperty(name="Group Attributes", type=Attribute)
is_editing: BoolProperty(name="Is Editing", default=False)
groups: CollectionProperty(name="Groups", type=Group)
active_group_index: IntProperty(name="Active Group Index")
active_group_index: IntProperty(name="Active Group Index", update=update_active_group_index)
active_group_id: IntProperty(name="Active Group Id")
@@ -88,7 +88,7 @@ class IfcGitData:
try:
if tool.IfcGitRepo.repo.branches:
return tool.IfcGit.branches_by_hexsha(tool.IfcGitRepo.repo)
except:
except AttributeError:
return {}
@classmethod
@@ -40,14 +40,15 @@ class LayersData:
@classmethod
def active_layers(cls):
if not bpy.context.active_object:
return []
data = bpy.context.active_object.data
if not data:
return []
if not isinstance(data, bpy.types.Mesh) or not data.BIMMeshProperties.ifc_definition_id:
return []
results = []
results = dict()
shape = tool.Ifc.get().by_id(data.BIMMeshProperties.ifc_definition_id)
for inverse in tool.Ifc.get().get_inverse(shape):
if inverse.is_a("IfcPresentationLayerAssignment"):
results.append(inverse.id())
for inverse in shape.LayerAssignments:
results[inverse.id()] = inverse.Name or "Unnamed"
return results
@@ -137,6 +137,7 @@ class RemovePresentationLayer(bpy.types.Operator):
class AssignPresentationLayer(bpy.types.Operator):
bl_idname = "bim.assign_presentation_layer"
bl_label = "Assign Presentation Layer"
bl_description = "Assign presentation layer to the active representation of the active object"
bl_options = {"REGISTER", "UNDO"}
item: bpy.props.StringProperty()
layer: bpy.props.IntProperty()
@@ -158,6 +159,7 @@ class AssignPresentationLayer(bpy.types.Operator):
class UnassignPresentationLayer(bpy.types.Operator):
bl_idname = "bim.unassign_presentation_layer"
bl_label = "Unassign Presentation Layer"
bl_description = "Unassign presentation layer from the active representation of the active object"
bl_options = {"REGISTER", "UNDO"}
item: bpy.props.StringProperty()
layer: bpy.props.IntProperty()
@@ -84,6 +84,7 @@ class BIM_UL_layers(UIList):
op = row.operator("bim.assign_presentation_layer", text="", icon="KEYFRAME", emboss=False)
op.layer = item.ifc_definition_id
# TODO: replace placeholder UI for hiding presentation layers
row.operator("bim.disable_editing_layer", text="", icon="HIDE_OFF", emboss=False)
row.operator("bim.disable_editing_layer", text="", icon="FREEZE", emboss=False)
@@ -1,49 +0,0 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of BlenderBIM Add-on.
#
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# BlenderBIM Add-on is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import bpy
import olca
import blenderbim.tool as tool
def refresh():
LcaData.is_loaded = False
class LcaData:
data = {}
is_loaded = False
@classmethod
def load(cls):
cls.data = {
"product_systems": cls.product_systems(),
}
cls.is_loaded = True
@classmethod
def product_systems(cls):
if not bpy.context.scene.BIMLCAProperties.is_connected:
return []
results = []
client = olca.Client(context.preferences.addons["blenderbim"].preferences.openlca_port)
try:
results = [(ps.name, ps.name, "") for ps in client.get_all(olca.ProductSystem)]
except:
pass
return results
@@ -1,55 +0,0 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of BlenderBIM Add-on.
#
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# BlenderBIM Add-on is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import bpy
import olca
import ifcopenshell
class CalculateLCA(bpy.types.Operator):
bl_idname = "bim.calculate_lca"
bl_label = "Calculate LCA"
bl_options = {"REGISTER", "UNDO"}
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
filter_glob: bpy.props.StringProperty(default="*.xlsx", options={"HIDDEN"})
def execute(self, context):
props = context.scene.BIMLCAProperties
client = olca.Client(context.preferences.addons["blenderbim"].preferences.openlca_port)
setup = olca.CalculationSetup()
setup.calculation_type = olca.CalculationType.SIMPLE_CALCULATION
setup.impact_method = client.find(olca.ImpactMethod, "CML-IA baseline")
setup.product_system = client.find(olca.ProductSystem, props.product_systems)
setup.amount = props.amount
result = client.calculate(setup)
client.excel_export(result, self.filepath)
return {"FINISHED"}
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
return {"RUNNING_MODAL"}
class ConnectOpenLCA(bpy.types.Operator):
bl_idname = "bim.connect_openlca"
bl_label = "Connect OpenLCA"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
bpy.context.scene.BIMLCAProperties.is_connected = True
return {"FINISHED"}
@@ -1,43 +0,0 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of BlenderBIM Add-on.
#
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# BlenderBIM Add-on is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import bpy
from bpy.types import Panel
class BIM_PT_lca(Panel):
bl_label = "BIM Life Cycle Assessment"
bl_idname = "BIM_PT_lca"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
bl_parent_id = "BIM_PT_tab_sandbox"
def draw(self, context):
props = context.scene.BIMLCAProperties
if props.is_connected:
row = self.layout.row()
row.prop(props, "product_systems", text="")
row = self.layout.row()
row.prop(props, "amount", text="")
row = self.layout.row()
row.operator("bim.calculate_lca")
else:
row = self.layout.row()
row.operator("bim.connect_openlca")
@@ -181,7 +181,7 @@ class FilledOpeningGenerator:
return
opening = filling.FillsVoids[0].RelatingOpeningElement
voided_obj = tool.Ifc.get_object(opening.VoidsElements[0].RelatingBuildingElement)
voided_element = opening.VoidsElements[0].RelatingBuildingElement
opening_rep = ifcopenshell.util.representation.get_representation(opening, "Model", "Body", "MODEL_VIEW")
ifcopenshell.api.run(
@@ -217,16 +217,22 @@ class FilledOpeningGenerator:
"geometry.assign_representation", tool.Ifc.get(), product=opening, representation=mapped_representation
)
representation = tool.Ifc.get().by_id(voided_obj.data.BIMMeshProperties.ifc_definition_id)
blenderbim.core.geometry.switch_representation(
tool.Ifc,
tool.Geometry,
obj=voided_obj,
representation=representation,
should_reload=True,
is_global=True,
should_sync_changes_first=False,
)
# update voided object representation or all it's parts if it's an aggregate
voided_elements = ifcopenshell.util.element.get_parts(voided_element) or [voided_element]
for voided_element in voided_elements:
voided_obj = tool.Ifc.get_object(voided_element)
if not voided_obj.data:
continue
representation = tool.Ifc.get().by_id(voided_obj.data.BIMMeshProperties.ifc_definition_id)
blenderbim.core.geometry.switch_representation(
tool.Ifc,
tool.Geometry,
obj=voided_obj,
representation=representation,
should_reload=True,
is_global=True,
should_sync_changes_first=False,
)
def generate_opening_from_filling(self, filling, filling_obj):
# Since openings are reused later, we give a default thickness of 1.2m
@@ -515,6 +521,7 @@ class AddBoolean(Operator, tool.Ifc.Operator):
return {"FINISHED"}
if element2 and not element1:
obj1, obj2 = obj2, obj1
element1, element2 = element2, element1
if not obj1.data or not hasattr(obj1.data, "BIMMeshProperties"):
return {"FINISHED"}
representation = tool.Ifc.get().by_id(obj1.data.BIMMeshProperties.ifc_definition_id)
@@ -662,17 +669,17 @@ class RemoveBooleans(Operator, tool.Ifc.Operator, AddObjectHelper):
except:
continue
boolean = None
boolean_id = None
for inverse in tool.Ifc.get().get_inverse(item):
if inverse.is_a("IfcBooleanResult"):
boolean = inverse
boolean_id = inverse.id()
break
ifcopenshell.api.run("geometry.remove_boolean", tool.Ifc.get(), item=item)
if obj.data.BIMMeshProperties.obj:
upstream_obj = obj.data.BIMMeshProperties.obj
element = tool.Ifc.get_entity(upstream_obj)
bbim_boolean_updates.setdefault(element, []).append(boolean)
bbim_boolean_updates.setdefault(element, []).append(boolean_id)
body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
if body:
blenderbim.core.geometry.switch_representation(
@@ -686,8 +693,8 @@ class RemoveBooleans(Operator, tool.Ifc.Operator, AddObjectHelper):
)
bpy.data.objects.remove(obj)
for element, booleans in bbim_boolean_updates.items():
tool.Model.unmark_manual_booleans(element, booleans)
for element, boolean_ids in bbim_boolean_updates.items():
tool.Model.unmark_manual_booleans(element, boolean_ids)
tool.Blender.set_active_object(upstream_obj)
return {"FINISHED"}
@@ -755,8 +762,6 @@ class EditOpenings(Operator, tool.Ifc.Operator):
def _execute(self, context):
props = bpy.context.scene.BIMModelProperties
building_objs = set()
model = tool.Ifc.get()
all_openings = model.by_type("IfcOpeningElement")
for obj in context.selected_objects:
element = tool.Ifc.get_entity(obj)
@@ -767,23 +772,24 @@ class EditOpenings(Operator, tool.Ifc.Operator):
obj = tool.Ifc.get_object(element)
openings = [r.RelatedOpeningElement for r in element.HasOpenings]
for opening in openings:
similar_openings = [o for o in all_openings if o.ObjectPlacement == opening.ObjectPlacement]
opening_obj = tool.Ifc.get_object(opening)
building_objs.add(obj)
similar_openings = blenderbim.core.geometry.get_similar_openings(tool.Ifc, opening)
similar_openings_building_objs = blenderbim.core.geometry.get_similar_openings_building_objs(tool.Ifc, similar_openings)
building_objs.update(similar_openings_building_objs)
if opening_obj:
if tool.Ifc.is_edited(opening_obj):
tool.Geometry.run_geometry_update_representation(obj=opening_obj)
blenderbim.core.geometry.edit_similar_opening_placement(tool.Geometry, opening, similar_openings)
elif tool.Ifc.is_moved(opening_obj):
blenderbim.core.geometry.edit_object_placement(
tool.Ifc, tool.Geometry, tool.Surveyor, obj=opening_obj
)
for similar_opening in similar_openings:
similar_opening.ObjectPlacement = opening.ObjectPlacement
element = similar_opening.VoidsElements[0].RelatingBuildingElement
obj = tool.Ifc.get_object(element)
building_objs.add(obj)
building_objs.update(self.get_all_building_objects_of_similar_openings(opening))
blenderbim.core.geometry.edit_similar_opening_placement(tool.Geometry, opening, similar_openings)
building_objs.update(self.get_all_building_objects_of_similar_openings(opening)) #NB this has nothing to do with clone similar_opening
tool.Ifc.unlink(element=opening, obj=opening_obj)
bpy.data.objects.remove(opening_obj)
@@ -826,13 +832,10 @@ class CloneOpening(Operator, tool.Ifc.Operator):
continue
opening_placement = opening.ObjectPlacement
opening_representations = opening.Representation.Representations
opening_representation = opening.Representation
new_opening = ifcopenshell.api.run("root.create_entity", tool.Ifc.get(), ifc_class="IfcOpeningElement")
for representation in opening_representations:
ifcopenshell.api.run(
"geometry.assign_representation", tool.Ifc.get(), product=new_opening, representation=representation
)
new_opening.Representation = opening_representation
ifcopenshell.api.run("void.add_opening", tool.Ifc.get(), opening=new_opening, element=wall)
new_opening.ObjectPlacement = opening_placement
@@ -87,8 +87,8 @@ class VIEW3D_MT_PIE_bim(bpy.types.Menu):
pie.operator("bim.pie_add_opening")
pie.operator("bim.pie_update_container")
pie.operator("bim.open_pie_class", text="Assign IFC Class")
pie.operator("bim.assign_object", text ="Assign Aggregation")
pie.operator("bim.unassign_object", text ="Unassign Aggregation")
pie.operator("bim.aggregate_assign_object", text="Assign Aggregation")
pie.operator("bim.aggregate_unassign_object", text="Unassign Aggregation")
class VIEW3D_MT_PIE_bim_class(bpy.types.Menu):
@@ -189,12 +189,12 @@ class AddConstrTypeInstance(bpy.types.Operator):
collection_obj = collection.BIMCollectionProperties.obj
bpy.ops.bim.assign_class(obj=obj.name, ifc_class=instance_class)
tool.Blender.remove_data_block(mesh) # Remove "Instance" mesh
tool.Blender.remove_data_block(mesh) # Remove "Instance" mesh
mesh_data = obj.data
element = tool.Ifc.get_entity(obj)
blenderbim.core.type.assign_type(tool.Ifc, tool.Type, element=element, type=relating_type)
if obj.data != mesh_data: # remove orphaned mesh from "bim.assign_class"
if obj.data != mesh_data: # remove orphaned mesh from "bim.assign_class"
tool.Blender.remove_data_block(mesh_data)
# Update required as core.type.assign_type may change obj.data
@@ -204,7 +204,7 @@ class AddConstrTypeInstance(bpy.types.Operator):
if (
building_obj
and building_element
and building_element.is_a() in ["IfcWall", "IfcWallStandardCase", "IfcCovering"]
and building_element.is_a() in ["IfcWall", "IfcWallStandardCase", "IfcCovering", "IfcElementAssembly"]
and instance_class in ["IfcWindow", "IfcDoor"]
):
# Fills should be a sibling to the building element
@@ -234,14 +234,19 @@ class AddConstrTypeInstance(bpy.types.Operator):
if (
building_obj
and building_element
and building_element.is_a() in ["IfcWall", "IfcWallStandardCase", "IfcCovering"]
and building_element.is_a() in ["IfcWall", "IfcWallStandardCase", "IfcCovering", "IfcElementAssembly"]
):
if instance_class in ["IfcWindow", "IfcDoor"]:
# TODO For now we are hardcoding windows and doors as a prototype
bpy.ops.bim.add_filled_opening(voided_obj=building_obj.name, filling_obj=obj.name)
else:
if collection_obj and tool.Ifc.get_entity(collection_obj):
obj.location.z = collection_obj.location.z - tool.Blender.get_object_bounding_box(obj)["min_z"]
if props.rl_mode == "BOTTOM":
obj.location.z = collection_obj.location.z - tool.Blender.get_object_bounding_box(obj)["min_z"]
elif props.rl_mode == "CONTAINER":
obj.location.z = collection_obj.location.z
elif props.rl_mode == "CURSOR":
pass
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
for port in ifcopenshell.util.system.get_ports(relating_type):
@@ -456,7 +456,9 @@ class DumbProfileJoiner:
if not extrusion:
return None, None
position = extrusion.Position
return (position.Axis.DirectionRatios, position.RefDirection.DirectionRatios)
if position.Axis:
return (position.Axis.DirectionRatios, position.RefDirection.DirectionRatios)
return ((0.0, 0.0, 1.0), (1.0, 0.0, 0.0))
old_body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
new_body = ifcopenshell.api.run(
@@ -19,6 +19,7 @@
import bpy
import ifcopenshell
import blenderbim.tool as tool
import math
from blenderbim.bim.prop import ObjProperty
from blenderbim.bim.module.model.data import AuthoringData
from bpy.types import PropertyGroup, NodeTree
@@ -132,6 +133,15 @@ class BIMModelProperties(PropertyGroup):
x: bpy.props.FloatProperty(name="X", default=0.5, subtype="DISTANCE", description="Size by X axis for the opening")
y: bpy.props.FloatProperty(name="Y", default=0.5, subtype="DISTANCE", description="Size by Y axis for the opening")
z: bpy.props.FloatProperty(name="Z", default=0.5, subtype="DISTANCE", description="Size by Z axis for the opening")
rl_mode: bpy.props.EnumProperty(
items=(
("BOTTOM", "Bottom", "Snaps the element's lowest geometry to the container's Z value."),
("CONTAINER", "Container", "Positions the element's placement origin at the container's Z value."),
("CURSOR", "Cursor", "Places the object placement at the 3D cursor's Z value."),
),
name="RL Mode",
default="BOTTOM",
)
# Used for things like walls, doors, flooring, skirting, etc
rl1: bpy.props.FloatProperty(name="RL", default=1, subtype="DISTANCE", description="Z offset for walls")
# Used for things like windows, other hosted furniture, and MEP
@@ -683,6 +693,7 @@ class BIMRoofProperties(PropertyGroup):
"roof_type",
"generation_method",
"angle",
"percentage",
"rafter_edge_angle",
)
roof_types = (("HIP/GABLE ROOF", "HIP/GABLE ROOF", ""),)
@@ -701,7 +712,8 @@ class BIMRoofProperties(PropertyGroup):
height: bpy.props.FloatProperty(
name="Height", default=1.0, description="Maximum height of the roof to be generated.", subtype="DISTANCE"
)
angle: bpy.props.FloatProperty(name="Slope Angle", default=pi / 18, subtype="ANGLE")
angle: bpy.props.FloatProperty(name="Slope Angle", default=pi / 18, subtype="ANGLE", update=lambda self, context: self.update_percentage())
percentage: bpy.props.FloatProperty(name="Slope %", default=17.63269754733197, subtype="PERCENTAGE", update=lambda self, context: self.update_angle())
roof_thickness: bpy.props.FloatProperty(name="Roof Thickness", default=0.1, subtype="DISTANCE")
rafter_edge_angle: bpy.props.FloatProperty(name="Rafter Edge Angle", min=0, max=pi, default=pi / 2, subtype="ANGLE")
@@ -718,6 +730,7 @@ class BIMRoofProperties(PropertyGroup):
kwargs["height"] = self.height
else:
kwargs["angle"] = self.angle
kwargs["percentage"] = self.percentage
if not convert_to_project_units:
return kwargs
@@ -727,3 +740,9 @@ class BIMRoofProperties(PropertyGroup):
kwargs = tool.Model.convert_data_to_si_units(kwargs, self.non_si_units_props)
for prop_name in kwargs:
setattr(self, prop_name, kwargs[prop_name])
def update_angle(self):
self.angle = math.atan(self.percentage / 100)
def update_percentage(self):
self.percentage = math.tan(self.angle)*100
@@ -1120,11 +1120,11 @@ class DumbWallJoiner:
other = tool.Ifc.get_object(rel.RelatedElement)
if connection not in ["ATPATH", "NOTDEFINED"]:
self.join(
obj,
other,
connection,
rel.RelatedConnectionType,
is_relating=True,
obj,
other,
connection,
rel.RelatedConnectionType,
is_relating=True,
description=rel.Description
)
for rel in element.ConnectedFrom:
@@ -1226,6 +1226,7 @@ class DumbWallJoiner:
# If opening has filling then stick to the filling's position
# We're applying new openings position only after wall position is applied
for opening in [r.RelatedOpeningElement for r in element.HasOpenings if r.RelatedOpeningElement.HasFillings]:
similar_openings = blenderbim.core.geometry.get_similar_openings(tool.Ifc, opening)
filling_obj = tool.Ifc.get_object(opening.HasFillings[0].RelatedBuildingElement)
filling_moved = tool.Ifc.is_moved(filling_obj)
if filling_moved:
@@ -1234,6 +1235,7 @@ class DumbWallJoiner:
ifcopenshell.api.run(
"geometry.edit_object_placement", tool.Ifc.get(), product=opening, matrix=filling_obj.matrix_world
)
blenderbim.core.geometry.edit_similar_opening_placement(tool.Geometry, opening, similar_openings)
blenderbim.core.geometry.switch_representation(
tool.Ifc,
@@ -194,6 +194,7 @@ class PipeTool(BimTool):
def add_layout_hotkey_operator(layout, text, hotkey, description):
modifiers = {
"A": "EVENT_ALT",
"C": "EVENT_CTRL",
"S": "EVENT_SHIFT",
}
modifier, key = hotkey.split("_")
@@ -283,6 +284,9 @@ class BimToolUI:
row.prop(data=cls.props, property="rl2", text="RL")
elif cls.props.ifc_class in ("IfcSpaceType"):
add_layout_hotkey_operator(cls.layout, "Generate", "S_G", bpy.ops.bim.generate_space.__doc__)
else:
row = cls.layout.row(align=True)
row.prop(data=cls.props, property="rl_mode", text="RL")
@classmethod
def draw_edit_object_interface(cls, context):
@@ -414,7 +418,7 @@ class BimToolUI:
row.label(text="", icon="EVENT_SHIFT")
row.label(text="", icon="EVENT_O")
if len(context.selected_objects) == 2:
if len(context.selected_objects) > 1:
row.operator("bim.add_opening", text="Apply Void")
else:
row.operator("bim.add_potential_opening", text="Add Void")
@@ -445,6 +449,10 @@ class BimToolUI:
add_layout_hotkey_operator(cls.layout, "Void", "A_O", "Toggle openings")
add_layout_hotkey_operator(cls.layout, "Decomposition", "A_D", "Select decomposition")
cls.layout.row(align=True).label(text="Aggregation")
add_layout_hotkey_operator(cls.layout, "Assign", "C_P", bpy.ops.bim.aggregate_assign_object.__doc__)
add_layout_hotkey_operator(cls.layout, "Unassign", "A_P", bpy.ops.bim.aggregate_unassign_object.__doc__)
cls.layout.separator()
add_layout_hotkey_operator(
cls.layout, "Calculate All Quantities", "S_Q", bpy.ops.bim.calculate_all_quantities.__doc__
@@ -580,12 +588,12 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator):
def hotkey_C_P(self):
if not bpy.context.selected_objects:
return
bpy.ops.bim.assign_object()
bpy.ops.bim.aggregate_assign_object()
def hotkey_A_P(self):
if not bpy.context.selected_objects:
return
bpy.ops.bim.unassign_object()
bpy.ops.bim.aggregate_unassign_object()
def hotkey_S_C(self):
if not bpy.context.selected_objects:
@@ -1,5 +1,5 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
# Copyright (C) 2023 Dion Moult <dion@thinkmoult.com>
#
# This file is part of BlenderBIM Add-on.
#
@@ -20,16 +20,20 @@ import bpy
from . import ui, prop, operator
classes = (
operator.CalculateLCA,
operator.ConnectOpenLCA,
prop.BIMLCAProperties,
ui.BIM_PT_lca,
operator.BIM_OT_nest_assign_object,
operator.BIM_OT_disable_editing_nest,
operator.BIM_OT_enable_editing_nest,
operator.BIM_OT_select_components,
operator.BIM_OT_select_nest,
operator.BIM_OT_nest_unassign_object,
prop.BIMObjectNestProperties,
ui.BIM_PT_nest,
)
def register():
bpy.types.Scene.BIMLCAProperties = bpy.props.PointerProperty(type=prop.BIMLCAProperties)
bpy.types.Object.BIMObjectNestProperties = bpy.props.PointerProperty(type=prop.BIMObjectNestProperties)
def unregister():
del bpy.types.Scene.BIMLCAProperties
del bpy.types.Object.BIMObjectNestProperties
@@ -0,0 +1,67 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2023 Dion Moult <dion@thinkmoult.com>
#
# This file is part of BlenderBIM Add-on.
#
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# BlenderBIM Add-on is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import bpy
import blenderbim.tool as tool
import ifcopenshell.util.element
def refresh():
NestData.is_loaded = False
class NestData:
data = {}
is_loaded = False
@classmethod
def load(cls):
cls.data = {
"has_relating_object": cls.has_relating_object(),
"relating_object_label": cls.get_relating_object_label(),
"has_related_objects": cls.has_related_objects(),
"total_components": cls.total_components(),
}
cls.is_loaded = True
@classmethod
def get_relating_object(cls):
return ifcopenshell.util.element.get_nest(tool.Ifc.get_entity(bpy.context.active_object))
@classmethod
def has_relating_object(cls) -> bool:
return cls.get_relating_object() is not None
@classmethod
def get_relating_object_label(cls) -> str:
nest = cls.get_relating_object()
if nest:
return f"{nest.is_a()}/{nest.Name or ''}"
@classmethod
def get_related_objects(cls):
return ifcopenshell.util.element.get_components(tool.Ifc.get_entity(bpy.context.active_object))
@classmethod
def total_components(cls):
components = cls.get_related_objects()
return len(components) if components else 0
@classmethod
def has_related_objects(cls) -> bool:
return bool(cls.get_related_objects())
@@ -0,0 +1,151 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2023 Dion Moult <dion@thinkmoult.com>
#
# This file is part of BlenderBIM Add-on.
#
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# BlenderBIM Add-on is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import bpy
import ifcopenshell
import ifcopenshell.util.element
import blenderbim.tool as tool
import blenderbim.core.nest as core
import blenderbim.core.spatial
import blenderbim.bim.handler
from blenderbim.bim.ifc import IfcStore
class Operator:
def execute(self, context):
IfcStore.execute_ifc_operator(self, context)
blenderbim.bim.handler.refresh_ui_data()
return {"FINISHED"}
class BIM_OT_nest_assign_object(bpy.types.Operator, Operator):
"""Create nest relationship between two ifc elements"""
bl_idname = "bim.nest_assign_object"
bl_label = "Assign Object To Nesting"
bl_options = {"REGISTER", "UNDO"}
relating_object: bpy.props.IntProperty()
def _execute(self, context):
relating_obj = None
if self.relating_object:
relating_obj = tool.Ifc.get_object(tool.Ifc.get().by_id(self.relating_object))
elif context.active_object:
relating_obj = context.active_object
if not relating_obj:
return
for obj in bpy.context.selected_objects:
if obj == relating_obj:
continue
element = tool.Ifc.get_entity(obj)
if not element:
continue
result = core.assign_object(
tool.Ifc,
tool.Nest,
tool.Collector,
relating_obj=relating_obj,
related_obj=obj,
)
if not result:
self.report({"ERROR"}, f" Cannot nest {obj.name} to {relating_obj.name}")
class BIM_OT_nest_unassign_object(bpy.types.Operator, Operator):
"""Remove nest relationship between two ifc elements"""
bl_idname = "bim.nest_unassign_object"
bl_label = "Unassign Object From Nesting"
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
for obj in bpy.context.selected_objects:
element = tool.Ifc.get_entity(obj)
if not element:
continue
nest = ifcopenshell.util.element.get_nest(element)
if not nest:
continue
core.unassign_object(
tool.Ifc,
tool.Nest,
tool.Collector,
relating_obj=tool.Ifc.get_object(nest),
related_obj=tool.Ifc.get_object(element),
)
class BIM_OT_enable_editing_nest(bpy.types.Operator, Operator):
"""Enable editing nest relationship"""
bl_idname = "bim.enable_editing_nest"
bl_label = "Enable Editing Nest"
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
core.enable_editing_nest(tool.Nest, obj=context.active_object)
class BIM_OT_disable_editing_nest(bpy.types.Operator, Operator):
"""Disable editing nest relationship"""
bl_idname = "bim.disable_editing_nest"
bl_label = "Disable Editing Nest"
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
core.disable_editing_nest(tool.Nest, obj=context.active_object)
class BIM_OT_select_components(bpy.types.Operator):
"""Select Components"""
bl_idname = "bim.select_components"
bl_label = "Select Components"
bl_options = {"REGISTER", "UNDO"}
obj: bpy.props.StringProperty()
def execute(self, context):
self.file = IfcStore.get_file()
obj = bpy.data.objects.get(self.obj) or context.active_object
components = ifcopenshell.util.element.get_components(tool.Ifc.get_entity(obj))
component_objs = set(tool.Ifc.get_object(c) for c in components)
selectable_component_objs = set(context.selectable_objects).intersection(component_objs)
for selectable_component_obj in selectable_component_objs:
selectable_component_obj.select_set(True)
return {"FINISHED"}
class BIM_OT_select_nest(bpy.types.Operator):
"""Select Nest"""
bl_idname = "bim.select_nest"
bl_label = "Select Nest"
bl_options = {"REGISTER", "UNDO"}
obj: bpy.props.StringProperty()
def execute(self, context):
self.file = IfcStore.get_file()
obj = bpy.data.objects.get(self.obj) or context.active_object
nest = ifcopenshell.util.element.get_nest(tool.Ifc.get_entity(obj))
nest_obj = tool.Ifc.get_object(nest)
if nest_obj in context.selectable_objects:
nest_obj.select_set(True)
bpy.context.view_layer.objects.active = nest_obj
return {"FINISHED"}
@@ -1,5 +1,5 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
# Copyright (C) 2023 Dion Moult <dion@thinkmoult.com>
#
# This file is part of BlenderBIM Add-on.
#
@@ -17,8 +17,8 @@
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import bpy
from blenderbim.bim.module.lca.data import LcaData
from blenderbim.bim.prop import StrProperty, Attribute
from blenderbim.bim.module.spatial.data import SpatialData
from bpy.types import PropertyGroup
from bpy.props import (
PointerProperty,
@@ -32,13 +32,17 @@ from bpy.props import (
)
def get_product_systems(self, context):
if not LcaData.is_loaded:
LcaData.load()
return LcaData.data["product_systems"]
def update_relating_object(self, context):
def message(self, context):
self.layout.label(text="Please select a valid Ifc Element")
if self.relating_object is None:
return
if not self.relating_object.BIMObjectProperties.ifc_definition_id:
context.window_manager.popup_menu(message, title="Invalid Element Selected", icon="INFO")
self.relating_object = None
class BIMLCAProperties(PropertyGroup):
is_connected: BoolProperty(name="Is Connected")
amount: FloatProperty(name="Amount")
product_systems: EnumProperty(items=get_product_systems, name="Product Systems")
class BIMObjectNestProperties(PropertyGroup):
is_editing: BoolProperty(name="Is Editing")
relating_object: PointerProperty(name="Nest Host", type=bpy.types.Object, update=update_relating_object)
@@ -0,0 +1,83 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2023 Dion Moult <dion@thinkmoult.com>
#
# This file is part of BlenderBIM Add-on.
#
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# BlenderBIM Add-on is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
from bpy.types import Panel
from blenderbim.bim.module.nest.data import NestData
from blenderbim.bim.ifc import IfcStore
class BIM_PT_nest(Panel):
bl_label = "Nest"
bl_idname = "BIM_PT_nest"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "object"
bl_parent_id = "BIM_PT_tab_object_metadata"
@classmethod
def poll(cls, context):
if not context.active_object:
return False
props = context.active_object.BIMObjectProperties
if not props.ifc_definition_id:
return False
if not IfcStore.get_element(props.ifc_definition_id):
return False
if not IfcStore.get_file().by_id(props.ifc_definition_id).is_a("IfcObjectDefinition"):
return False
return True
def draw(self, context):
layout = self.layout
if not NestData.is_loaded:
NestData.load()
props = context.active_object.BIMObjectNestProperties
if props.is_editing:
row = layout.row(align=True)
row.prop(props, "relating_object", text="")
if props.relating_object:
op = row.operator("bim.nest_assign_object", icon="CHECKMARK", text="")
op.relating_object = props.relating_object.BIMObjectProperties.ifc_definition_id
row.operator("bim.disable_editing_nest", icon="CANCEL", text="")
else:
row = layout.row(align=True)
if NestData.data["has_relating_object"]:
row.label(text=NestData.data["relating_object_label"], icon="TRIA_UP")
op = row.operator("bim.select_nest", icon="RESTRICT_SELECT_OFF", text="")
op.obj = context.active_object.name
row.operator("bim.enable_editing_nest", icon="GREASEPENCIL", text="")
op = row.operator("bim.nest_unassign_object", icon="X", text="")
else:
row.label(text="No Host", icon="TRIA_UP")
row.operator("bim.enable_editing_nest", icon="GREASEPENCIL", text="")
row = layout.row(align=True)
total_components = NestData.data["total_components"]
if total_components == 0:
row.label(text="No Components", icon="TRIA_DOWN")
elif total_components == 1:
row.label(text="1 Component", icon="TRIA_DOWN")
else:
row.label(text=f"{total_components} Components", icon="TRIA_DOWN")
if NestData.data["has_related_objects"]:
op = row.operator("bim.select_components", icon="RESTRICT_SELECT_OFF", text="")
op.obj = context.active_object.name
@@ -88,7 +88,7 @@ class ExecuteIfcPatch(bpy.types.Operator):
file = ifcopenshell.open(props.ifc_patch_input)
# Store this in case the patch recipe resets the Blender session, such as by loading a new project.
ifc_patch_output = props.ifc_patch_output
ifc_patch_output = props.ifc_patch_output or props.ifc_patch_input
output = ifcpatch.execute(
{
@@ -100,6 +100,7 @@ class ExecuteIfcPatch(bpy.types.Operator):
}
)
ifcpatch.write(output, ifc_patch_output)
self.report({"INFO"}, f"{props.ifc_patch_recipes} patch executed successfully")
return {"FINISHED"}
@@ -53,7 +53,7 @@ def get_ifcpatch_recipes(self, context):
continue
docs = ifcpatch.extract_docs(f, "Patcher", "__init__", ("src", "file", "logger", "args"))
ifcpatchrecipes_enum.append((f, f, docs.get("description", "") if docs else ""))
return ifcpatchrecipes_enum
return sorted(ifcpatchrecipes_enum, key=lambda x: x[0])
def update_ifc_patch_recipe(self, context):
@@ -683,7 +683,7 @@ class RevertProject(bpy.types.Operator, IFCFileSelector):
bl_idname = "bim.revert_project"
bl_label = "Revert IFC Project"
bl_options = {"REGISTER", "UNDO"}
bl_description = "Reload currently opened IFC project discarding all unsaved changes"
bl_description = "Revert to a fresh session discarding all unsaved changes"
@classmethod
def poll(cls, context):
@@ -1105,6 +1105,7 @@ class ExportIFC(bpy.types.Operator):
save_blend_file = bool(bpy.data.is_saved and bpy.data.is_dirty and bpy.data.filepath)
if save_blend_file:
bpy.ops.wm.save_mainfile(filepath=bpy.data.filepath)
bpy.context.scene.BIMProperties.is_dirty = False
blenderbim.bim.handler.refresh_ui_data()
self.report(
{"INFO"},
@@ -157,6 +157,7 @@ class BIMProjectProperties(PropertyGroup):
is_coordinating: BoolProperty(name="For Coordination Only", default=False)
deflection_tolerance: FloatProperty(name="Deflection Tolerance", default=0.001)
angular_tolerance: FloatProperty(name="Angular Tolerance", default=0.5)
void_limit: IntProperty(name="Void Limit", default=30)
distance_limit: FloatProperty(name="Distance Limit", default=1000)
false_origin: StringProperty(name="False Origin", default="0,0,0")
element_offset: IntProperty(name="Element Offset", default=0)
@@ -146,6 +146,8 @@ class BIM_PT_project(Panel):
row = self.layout.row()
row.prop(pprops, "angular_tolerance")
row = self.layout.row()
row.prop(pprops, "void_limit")
row = self.layout.row()
row.prop(pprops, "distance_limit")
row = self.layout.row()
row.prop(pprops, "false_origin")
@@ -206,12 +208,14 @@ class BIM_PT_project(Panel):
if props.ifc_file:
row = self.layout.row(align=True)
row.label(text="Saved", icon="EXPORT")
if context.scene.BIMProperties.is_dirty:
row.label(text="Saved*", icon="EXPORT")
else:
row.label(text="Saved", icon="EXPORT")
row.label(text=ProjectData.data["last_saved"])
row = self.layout.row(align=True)
row.prop(props, "ifc_file", text="")
row.operator("bim.reload_ifc_file", icon="FILE_REFRESH", text="")
row.operator("bim.select_ifc_file", icon="FILE_FOLDER", text="")
row.operator("bim.unload_project", text="", icon="CANCEL")
else:
@@ -399,11 +403,3 @@ class BIM_PT_purge(Panel):
layout = self.layout
layout.operator("bim.purge_unused_profiles")
layout.operator("bim.purge_unused_types")
layout.operator("bim.purge_unused_representations")
layout.separator()
layout.label(text="Purge unused elements by class:")
row = layout.row(align=True)
row.prop(context.scene.BIMDebugProperties, "ifc_class_purge", text="")
row.operator("bim.purge_unused_elements_by_class", text="", icon="TRASH")
row.operator("bim.print_unused_elements_stats", text="", icon="INFO")
@@ -44,6 +44,7 @@ classes = (
prop.MaterialSetItemPsetProperties,
prop.TaskPsetProperties,
prop.ResourcePsetProperties,
prop.GroupPsetProperties,
prop.ProfilePsetProperties,
prop.WorkSchedulePsetProperties,
prop.RenameProperties,
@@ -57,6 +58,8 @@ classes = (
ui.BIM_PT_task_qtos,
ui.BIM_PT_resource_qtos,
ui.BIM_PT_resource_psets,
ui.BIM_PT_group_psets,
ui.BIM_PT_group_qtos,
ui.BIM_PT_profile_psets,
ui.BIM_PT_work_schedule_psets,
ui.BIM_PT_bulk_property_editor,
@@ -73,6 +76,7 @@ def register():
bpy.types.Material.PsetProperties = bpy.props.PointerProperty(type=prop.MaterialPsetProperties)
bpy.types.Scene.TaskPsetProperties = bpy.props.PointerProperty(type=prop.TaskPsetProperties)
bpy.types.Scene.ResourcePsetProperties = bpy.props.PointerProperty(type=prop.ResourcePsetProperties)
bpy.types.Scene.GroupPsetProperties = bpy.props.PointerProperty(type=prop.GroupPsetProperties)
bpy.types.Scene.ProfilePsetProperties = bpy.props.PointerProperty(type=prop.ProfilePsetProperties)
bpy.types.Scene.WorkSchedulePsetProperties = bpy.props.PointerProperty(type=prop.WorkSchedulePsetProperties)
bpy.types.Scene.RenameProperties = bpy.props.CollectionProperty(type=prop.RenameProperties)
@@ -85,6 +89,7 @@ def unregister():
del bpy.types.Material.PsetProperties
del bpy.types.Scene.TaskPsetProperties
del bpy.types.Scene.ResourcePsetProperties
del bpy.types.Scene.GroupPsetProperties
del bpy.types.Scene.ProfilePsetProperties
del bpy.types.Scene.WorkSchedulePsetProperties
del bpy.types.Scene.RenameProperties
@@ -36,6 +36,8 @@ def refresh():
TaskQtosData.is_loaded = False
ResourceQtosData.is_loaded = False
ResourcePsetsData.is_loaded = False
GroupQtosData.is_loaded = False
GroupPsetData.is_loaded = False
ProfilePsetsData.is_loaded = False
WorkSchedulePsetsData.is_loaded = False
AddEditCustomPropertiesData.is_loaded = False
@@ -89,7 +91,9 @@ class ObjectPsetsData(Data):
element = tool.Ifc.get_entity(obj)
if not element:
return []
psets = blenderbim.bim.schema.ifc.psetqto.get_applicable(element.is_a(), pset_only=True)
psets = blenderbim.bim.schema.ifc.psetqto.get_applicable(
element.is_a(), ifcopenshell.util.element.get_predefined_type(element), pset_only=True
)
psetnames = cls.format_pset_enum(psets)
assigned_names = ifcopenshell.util.element.get_psets(element, psets_only=True, should_inherit=False).keys()
return [p for p in psetnames if p[0] not in assigned_names]
@@ -100,7 +104,9 @@ class ObjectPsetsData(Data):
element = tool.Ifc.get_entity(obj)
if not element:
return []
qtos = blenderbim.bim.schema.ifc.psetqto.get_applicable(element.is_a(), qto_only=True)
qtos = blenderbim.bim.schema.ifc.psetqto.get_applicable(
element.is_a(), ifcopenshell.util.element.get_predefined_type(element), qto_only=True
)
return cls.format_pset_enum(qtos)
@classmethod
@@ -205,6 +211,30 @@ class ResourcePsetsData(Data):
cls.is_loaded = True
class GroupQtosData(Data):
data = {}
is_loaded = False
@classmethod
def load(cls):
props = bpy.context.scene.BIMGroupProperties
ifc_definition_id = props.groups[props.active_group_index].ifc_definition_id
cls.data = {"qtos": cls.psetqtos(tool.Ifc.get_entity_by_id(ifc_definition_id), qtos_only=True)}
cls.is_loaded = True
class GroupPsetData(Data):
data = {}
is_loaded = False
@classmethod
def load(cls):
props = bpy.context.scene.BIMGroupProperties
ifc_definition_id = props.groups[props.active_group_index].ifc_definition_id
cls.data = {"psets": cls.psetqtos(tool.Ifc.get_entity_by_id(ifc_definition_id), psets_only=True)}
cls.is_loaded = True
class ProfilePsetsData(Data):
data = {}
is_loaded = False
@@ -58,7 +58,8 @@ def get_pset_props(context, obj, obj_type):
return context.scene.ProfilePsetProperties
elif obj_type == "WorkSchedule":
return context.scene.WorkSchedulePsetProperties
elif obj_type == "Group":
return context.scene.GroupPsetProperties
class TogglePsetExpansion(bpy.types.Operator, Operator):
bl_idname = "bim.toggle_pset_expansion"
@@ -196,6 +197,7 @@ class EnablePsetEditing(bpy.types.Operator):
new.is_selected = enum in selected_enum_items
def load_from_pset_data(self, pset):
props = []
if pset.is_a("IfcElementQuantity"):
props = pset.Quantities
elif pset.is_a("IfcPropertySet"):
@@ -130,6 +130,23 @@ def get_resource_qto_names(self, context):
return qtonames[ifc_class]
def get_group_pset_names(self, context):
global psetnames
ifc_class = "IfcGroup"
if ifc_class not in psetnames:
psets = blenderbim.bim.schema.ifc.psetqto.get_applicable(ifc_class, pset_only=True)
psetnames[ifc_class] = blender_formatted_enum_from_psets(psets)
return psetnames[ifc_class]
def get_group_qto_names(self, context):
global qtonames
ifc_class = "IfcGroup"
if ifc_class not in qtonames:
psets = blenderbim.bim.schema.ifc.psetqto.get_applicable(ifc_class, qto_only=True)
qtonames[ifc_class] = blender_formatted_enum_from_psets(psets)
return qtonames[ifc_class]
def get_profile_pset_names(self, context):
global psetnames
pprops = context.scene.BIMProfileProperties
@@ -229,6 +246,14 @@ class ResourcePsetProperties(PropertyGroup):
qto_name: EnumProperty(items=get_resource_qto_names, name="Qto Name")
class GroupPsetProperties(PropertyGroup):
active_pset_id: IntProperty(name="Active Pset ID")
active_pset_name: StringProperty(name="Pset Name")
active_pset_type: StringProperty(name="Active Pset Type")
properties: CollectionProperty(name="Properties", type=IfcProperty)
pset_name: EnumProperty(items=get_group_pset_names, name="Pset Name")
qto_name: EnumProperty(items=get_group_qto_names, name="Qto Name")
class ProfilePsetProperties(PropertyGroup):
active_pset_id: IntProperty(name="Active Pset ID")
active_pset_name: StringProperty(name="Pset Name")
@@ -29,8 +29,11 @@ from blenderbim.bim.module.pset.data import (
TaskQtosData,
ResourceQtosData,
ResourcePsetsData,
GroupQtosData,
GroupPsetData,
ProfilePsetsData,
WorkSchedulePsetsData,
)
@@ -470,6 +473,76 @@ class BIM_PT_resource_psets(Panel):
draw_psetqto_ui(context, pset["id"], pset, props, self.layout, "Resource")
class BIM_PT_group_qtos(Panel):
bl_label = "Group Quantity Sets"
bl_idname = "BIM_PT_group_qtos"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
bl_parent_id = "BIM_PT_groups"
@classmethod
def poll(cls, context):
props = context.scene.BIMGroupProperties
total_resources = len(props.groups)
if total_resources > 0 and props.active_group_index < total_resources:
return True
return False
def draw(self, context):
if not GroupQtosData.is_loaded:
GroupQtosData.load()
props = context.scene.GroupPsetProperties
row = self.layout.row(align=True)
row.prop(props, "qto_name", text="")
op = row.operator("bim.add_qto", icon="ADD", text="")
op.obj_type = "Group"
if not props.active_pset_id and props.active_pset_name and props.active_pset_type == "QTO":
draw_psetqto_ui(context, 0, {}, props, self.layout, "Group")
for qto in GroupQtosData.data["qtos"]:
draw_psetqto_ui(context, qto["id"], qto, props, self.layout, "Group")
class BIM_PT_group_psets(Panel):
bl_label = "Group Property Sets"
bl_idname = "BIM_PT_group_psets"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
bl_parent_id = "BIM_PT_groups"
@classmethod
def poll(cls, context):
props = context.scene.BIMGroupProperties
total_resources = len(props.groups)
if total_resources > 0 and props.active_group_index < total_resources:
return True
return False
def draw(self, context):
if not GroupPsetData.is_loaded:
GroupPsetData.load()
props = context.scene.GroupPsetProperties
row = self.layout.row(align=True)
prop_with_search(row, props, "pset_name", text="")
op = row.operator("bim.add_pset", icon="ADD", text="")
op.obj_type = "Group"
if not props.active_pset_id and props.active_pset_name and props.active_pset_type == "PSET":
draw_psetqto_ui(context, 0, {}, props, self.layout, "Group")
for pset in GroupPsetData.data["psets"]:
draw_psetqto_ui(context, pset["id"], pset, props, self.layout, "Group")
class BIM_PT_profile_psets(Panel):
bl_label = "Profile Property Sets"
bl_idname = "BIM_PT_profile_psets"
@@ -20,6 +20,7 @@ import os
import bpy
import pathlib
import ifcopenshell
import ifcopenshell.util.attribute
import blenderbim.tool as tool
from blenderbim.bim.ifc import IfcStore
@@ -36,6 +37,7 @@ class PsetTemplatesData:
def load(cls):
cls.is_loaded = True
cls.data["primary_measure_type"] = cls.primary_measure_type()
cls.data["property_template_type"] = cls.property_template_type()
cls.data["pset_template_files"] = cls.pset_template_files()
cls.data["pset_templates"] = cls.pset_templates()
cls.data["pset_template"] = cls.pset_template()
@@ -43,13 +45,25 @@ class PsetTemplatesData:
@classmethod
def primary_measure_type(cls):
schema = tool.Ifc.schema()
version = tool.Ifc.get_schema()
ifc_file = IfcStore.pset_template_file
if not ifc_file:
return []
schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(ifc_file.schema)
version = ifc_file.schema
return [
(t, t, ifcopenshell.util.doc.get_type_doc(version, t).get("description", ""))
for t in sorted([d.name() for d in schema.declarations() if hasattr(d, "declared_type")])
]
@classmethod
def property_template_type(cls):
ifc_file = IfcStore.pset_template_file
if not ifc_file:
return []
schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(ifc_file.schema)
attribute = schema.declaration_by_name("IfcSimplePropertyTemplate").attributes()[0]
return [(i, i, "") for i in ifcopenshell.util.attribute.get_enum_items(attribute)]
@classmethod
def pset_template_files(cls):
results = []
@@ -307,7 +307,8 @@ class EditPropTemplate(bpy.types.Operator, Operator):
bpy.ops.bim.disable_editing_prop_template()
IfcStore.pset_template_file.write(IfcStore.pset_template_path)
blenderbim.bim.handler.refresh_ui_data()
blenderbim.bim.schema.reload(tool.Ifc.get().schema)
if tool.Ifc.get():
blenderbim.bim.schema.reload(tool.Ifc.get().schema)
# TODO -This will need to go into the
# api code at some point - vulevukusej
@@ -42,6 +42,8 @@ def updatePsetTemplateFiles(self, context):
PsetTemplatesData.data["pset_template_files"] = PsetTemplatesData.pset_template_files()
PsetTemplatesData.data["pset_templates"] = PsetTemplatesData.pset_templates()
PsetTemplatesData.data["prop_templates"] = PsetTemplatesData.prop_templates()
PsetTemplatesData.data["primary_measure_type"] = PsetTemplatesData.primary_measure_type()
PsetTemplatesData.data["property_template_type"] = PsetTemplatesData.property_template_type()
def getPsetTemplateFiles(self, context):
@@ -67,6 +69,12 @@ def get_primary_measure_type(self, context):
return PsetTemplatesData.data["primary_measure_type"]
def get_property_template_type(self, context):
if not PsetTemplatesData.is_loaded:
PsetTemplatesData.load()
return PsetTemplatesData.data["property_template_type"]
def get_template_type(self, context):
return [
(
@@ -157,10 +165,7 @@ class PropTemplate(PropertyGroup):
description=get_attribute_doc("IFC4", "IfcPropertyTemplate", "Description"),
)
primary_measure_type: EnumProperty(items=get_primary_measure_type, name="Primary Measure Type")
template_type: EnumProperty(
items=[("P_SINGLEVALUE", "P_SINGLEVALUE", ""), ("P_ENUMERATEDVALUE", "P_ENUMERATEDVALUE", "")],
name="Template Type",
)
template_type: EnumProperty(items=get_property_template_type, name="Template Type")
enum_values: CollectionProperty(type=EnumerationValues)
def get_value_name(self):
@@ -158,6 +158,9 @@ class AssignClass(bpy.types.Operator, Operator):
ifc_context = tool.Ifc.get().by_id(ifc_context)
active_object = context.active_object
for obj in objects:
if obj.mode != "OBJECT":
self.report({"ERROR"}, "Object must be in OBJECT mode to assign class")
continue
core.assign_class(
tool.Ifc,
tool.Collector,
@@ -177,48 +180,45 @@ class UnlinkObject(bpy.types.Operator):
bl_label = "Unlink Object"
bl_options = {"REGISTER", "UNDO"}
obj: bpy.props.StringProperty()
should_delete: bpy.props.BoolProperty(name="Should Delete", default=True)
should_delete: bpy.props.BoolProperty(name="Delete IFC Element", default=True)
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context):
self.file = IfcStore.get_file()
if self.obj:
objects = [bpy.data.objects.get(self.obj)]
else:
objects = context.selected_objects
for obj in objects:
was_active_object = obj == context.active_object
object_name = obj.name
if obj in IfcStore.edited_objs:
IfcStore.edited_objs.remove(obj)
element = tool.Ifc.get_entity(obj)
if element:
if self.should_delete:
obj_copy = obj.copy()
if element and self.should_delete:
object_name = obj.name
# copy object data, so it won't be removed by `delete_ifc_object`
if obj.data:
obj_copy.data = obj.data.copy()
if obj.type == "MESH":
obj_copy.data.BIMMeshProperties.ifc_definition_id = 0
# Copy object, so it won't be removed by `delete_ifc_object`
obj_copy = obj.copy()
if obj.data:
obj_copy.data = obj.data.copy()
for collection in obj.users_collection:
collection.objects.link(obj_copy)
tool.Geometry.delete_ifc_object(obj)
obj = obj_copy
if obj in IfcStore.edited_objs:
IfcStore.edited_objs.remove(obj)
tool.Ifc.unlink(obj=obj)
tool.Geometry.delete_ifc_object(obj)
for material_slot in obj.material_slots:
if material_slot.material:
material_slot.material = material_slot.material.copy()
blenderbim.core.style.unlink_style(tool.Ifc, tool.Style, obj=material_slot.material)
blenderbim.core.material.unlink_material(tool.Ifc, obj=material_slot.material)
if "Ifc" in object_name and "/" in object_name:
obj.name = object_name.split("/", 1)[1]
if was_active_object:
tool.Blender.set_active_object(obj)
obj = obj_copy
obj.name = object_name
tool.Root.unlink_object(obj)
for collection in obj.users_collection:
# Reset collection because its original collection may be removed too.
collection.objects.unlink(obj)
bpy.context.scene.collection.objects.link(obj)
if was_active_object:
tool.Blender.set_active_object(obj)
return {"FINISHED"}
def draw(self, context):
@@ -116,8 +116,9 @@ class BIMSearchProperties(PropertyGroup):
("classification", "Classification", "", "OUTLINER", 4),
("location", "Location", "", "PACKAGE", 5),
("type", "Type", "", "FILE_VOLUME", 6),
("query", "Query", "", "POINTCLOUD_DATA", 7),
("instance", "GlobalId", "", "GRIP", 8),
("group", "Group", "", "OUTLINER_COLLECTION", 7),
("query", "Query", "", "POINTCLOUD_DATA", 8),
("instance", "GlobalId", "", "GRIP", 9),
],
)
saved_searches: EnumProperty(items=get_saved_searches, name="Saved Searches")
@@ -32,7 +32,7 @@ class SpatialData:
@classmethod
def load(cls):
cls.data = {
"parent_container_id": cls.get_parent_container_id(),
"parent_container_id": cls.parent_container_id(),
"is_directly_contained": cls.is_directly_contained(),
"label": cls.label(),
"references": cls.references(),
@@ -55,12 +55,12 @@ class SpatialData:
return results
@classmethod
def get_parent_container_id(cls):
def parent_container_id(cls):
container_id = bpy.context.scene.BIMSpatialProperties.active_container_id
if not container_id:
return
container = tool.Ifc.get().by_id(container_id)
if container.is_a("IfcProject"):
container = tool.Ifc.get_entity_by_id(container_id)
if not container or container.is_a("IfcProject"):
return
return container.Decomposes[0].RelatingObject.id()

Some files were not shown because too many files have changed in this diff Show More