Compare commits

...

880 Commits

Author SHA1 Message Date
Dion Moult c352181f8c Fix bug where underlays didn't work 2022-05-22 21:32:57 +10:00
Thomas Krijnen 3a1416536a Oops 2022-05-17 22:31:37 +02:00
Thomas Krijnen 4eec863cad Example for turning IDS test cases into spec. Sorry for butchering your code @Moult 2022-05-17 22:21:11 +02:00
Thomas Krijnen 96bcd31d99 #2202 Check for nested compound of solid in boolean result handling 2022-05-16 14:24:57 +02:00
Thomas Krijnen b09ff828de #2186 rerun codegen 2022-05-16 13:07:40 +02:00
Thomas Krijnen 3cb68ca388 #2186 optional attribute setter fix 2022-05-16 13:07:40 +02:00
Dion Moult c95d3693ea OpenSourceBIM contributors badge now in README 2022-05-16 11:35:39 +10:00
Dion Moult 67d2ab2577 Write documentation on materials, locations, and measurements 2022-05-16 08:15:31 +10:00
Dion Moult 970b24b63b #1153 New annotation objects have their placement Z in the drawing direction to allow for 2D geometry. 2022-05-15 11:45:47 +10:00
Dion Moult fdaae048c2 Implement IDS partOf facet 2022-05-12 14:33:44 +10:00
Dion Moult 4da5a38237 Implement IDS material null checks, inheritance checks, and set item checks 2022-05-12 12:06:23 +10:00
Dion Moult 0cf2d7e05e Implement IDS uri/use/instructions for material facet 2022-05-11 23:30:10 +10:00
Dion Moult 3122cf21db Implement IDS property facet measure conversions 2022-05-11 19:59:10 +10:00
Dion Moult 4bf25d5d9c Minor fix 2022-05-11 18:46:27 +10:00
Dion Moult dc10c7a360 Implement null check for IDS properties, and support restrictions 2022-05-11 17:10:22 +10:00
Dion Moult 94e082fc7d Support IDS uri/use/instructions for property facets 2022-05-11 15:30:39 +10:00
Dion Moult 9671df8491 You can now choose between lightweight and full classifications 2022-05-11 15:19:31 +10:00
Dion Moult 8aa38917eb New classification utility module 2022-05-11 15:19:17 +10:00
Dion Moult 0774100606 IDS classification facets not handle inheritance, full refs, and restrictions 2022-05-11 14:49:47 +10:00
Dion Moult 95b48b02e4 Add support for IDS classification uri, use, and instructions 2022-05-11 13:27:19 +10:00
Dion Moult c5de1d8727 Add IDS support for restrictions in attribute facets and handle inheritance 2022-05-11 13:06:15 +10:00
Dion Moult 7e09ab0ced Add support for IDS attribute use/instructions 2022-05-11 10:12:36 +10:00
Dion Moult eb3e137a6e #2196 Get psets now handleds inheritance by default 2022-05-11 09:18:23 +10:00
Dion Moult f528ad9744 Minor fix 2022-05-10 23:49:14 +10:00
Dion Moult 650f9eb7f5 Attribute filtering in IDS now checks null-like values too. 2022-05-10 22:42:21 +10:00
Dion Moult a78d6f42e5 Allow IDS to audit any entity, not just IfcObjects 2022-05-10 22:13:28 +10:00
Dion Moult 4326a74c64 Fix IDS to not allow subclasses and allow restrictions in the class name 2022-05-10 21:40:35 +10:00
Dion Moult 08fc62d4d9 Add support for userdefined predefined types in IDS entity facets 2022-05-10 21:20:10 +10:00
Dion Moult ae298de8dc Implement identifier, desc, and instructions for specification node in IDS 2022-05-10 19:26:12 +10:00
Dion Moult a24c4b269a Fix mandatory title and optional date and check invalid dates in IDS 2022-05-10 17:36:24 +10:00
Dion Moult 083d9d3e3e Implement attribute facet for IDS 2022-05-10 16:23:07 +10:00
Dion Moult 12e7ed356c Fix IDS namespace bug that prevented XML encoding, simplify XML writing, and new to_string() method to decrease dependency on the filesystem 2022-05-10 16:22:38 +10:00
Dion Moult 3f15dd71b1 Run black on ids module 2022-05-10 16:18:08 +10:00
Adrien SCHVALBERG 42448cae4e Add missing exports for ifcopenshell-python when we build shared libs. 2022-05-09 14:13:47 +02:00
Dion Moult 7e28d193ec #2185 Document installation and hello world examples for IfcOpenShell-python 2022-05-09 19:27:33 +10:00
Thomas Krijnen 8f2b32532c validate.py text result formatting improvements 2022-05-09 11:00:04 +02:00
Dion Moult f0a00cbc94 #2185 Set up auto generated API reference documentation via sphinx-autoapi 2022-05-09 15:35:52 +10:00
Dion Moult fd73b2cf0a #2185 Migrate web files to website repo 2022-05-08 19:51:17 +10:00
Adrien SCHVALBERG e5b3455533 Fix memory leak for HeaderEntity instances:
the getArgumentCount() call in IfcEntityInstanceData destructor isn't virtually dispatched so it returns 0 instead of correct number of arguments => memory leak
added IfcEntityInstanceData::clearArguments() which clears all arguments and resets the attributes_ field to nullptr

Remove unnecessary copy in IfcEntityInstanceData::setArgument() when argument is created at the call site (no more leak of original argument)

Fix memory leak in IfcFile: delete entities from both byid and entity_file_map
2022-05-08 10:21:46 +02:00
Adrien SCHVALBERG e677355438 Add IfcParse::clear_schemas() and individual Ifc___::clear_schema() to clear any resource allocated by registered schemas
Add missing virtual destructors in IfcSchema.h
2022-05-08 10:21:46 +02:00
Dion Moult 93fd3f97e3 #2111 New feature to select by style and see style usage 2022-05-08 18:03:18 +10:00
Dion Moult 910d189f35 #2188 support displaying inherited materials 2022-05-08 16:27:35 +10:00
Dion Moult c8d6d12442 The material manager now groups materials into categories 2022-05-08 15:48:09 +10:00
Dion Moult 1f3ba3d09d New makefile targets to specifically run tests of single modules 2022-05-08 15:45:47 +10:00
Dion Moult f67941221e Enable syntax highlighting in test suite for easier debugging 2022-05-08 15:24:06 +10:00
Dion Moult 8252ef82ad #2185 Write documentation about viewing construction types 2022-05-07 21:50:38 +10:00
Dion Moult 3b825e35ae #1153 You can now provide custom stylesheets, markers, symbols, and hatches 2022-05-07 20:28:24 +10:00
Dion Moult 4a4912e817 Editing URIReference properties now provides a file browse button 2022-05-07 20:26:25 +10:00
Dion Moult 5a38604ec2 #1153 Boilerplate styles and definitions now can be used with any drawing layer combination 2022-05-07 17:57:05 +10:00
Dion Moult b15e0d0e2b #2185 Commit logos and homepage mockup by Paulina Godoy and Meetlat 2022-05-07 11:48:20 +10:00
Dion Moult 5a5f52a5c2 #2185 Standardise graphic style for annotation images in documentation 2022-05-07 11:42:12 +10:00
Dion Moult 816a690519 Commit WIP website design by Paulina Godoy :) 2022-05-07 10:20:11 +10:00
Dion Moult 60303c6560 Minor fix 2022-05-06 20:51:36 +10:00
Dion Moult cc88e6f112 #2185 Write documentation about viewing attributes, pets, and qtos 2022-05-06 20:44:26 +10:00
Dion Moult 5f1b5d0c63 You can now manage actor assignments to products 2022-05-06 17:47:24 +10:00
Dion Moult b54caa8967 Person / organisation roles and acting actors are now shown 2022-05-06 16:21:12 +10:00
Dion Moult 73e048a9a8 #1153 You can now manage assigned products for all annotations 2022-05-06 15:35:39 +10:00
Dion Moult 316a2b7f53 #2185 Write documentation for viewing element classes 2022-05-06 12:46:29 +10:00
Dion Moult 42edcda8bd Deleting an element now also auto deletes its associated distribution ports 2022-05-06 10:38:38 +10:00
Dion Moult 92432f453d You can now remove ports 2022-05-06 10:35:12 +10:00
Dion Moult 4b6ebe7b1c Fix bug where deleted objects were not cleared after saving 2022-05-06 09:20:37 +10:00
Emad Aghajani 3f6d25ed43 Fixes #2175: Fixes a crash when blender is in background mode or is used as bpy Python module 2022-05-05 19:23:36 +10:00
Dion Moult d4b2cc7f2c Bump IfcOpenShell build 2022-05-05 18:00:58 +10:00
Dion Moult 53c050c6ed Port locations now sync relative to their parent even when not actively displayed in the viewport 2022-05-05 18:00:42 +10:00
Dion Moult 3213af2f48 #2185 Write documentation on the object outliner panel 2022-05-05 16:29:40 +10:00
Dion Moult 85122ba184 #2185 Document how to navigate through a model 2022-05-05 14:43:34 +10:00
Dion Moult b8791d43aa Minor documentation update for loading an IFC model 2022-05-05 12:19:40 +10:00
Dion Moult 6a82f27d48 Standalone Blender import script now available for debugging 2022-05-05 09:59:04 +10:00
Dion Moult 4e2400952a Fix #2147 #2143 Fix hanging when reloading a cache of a file with 2D and 3D data 2022-05-05 09:58:10 +10:00
Dion Moult de1dae8b6e Grids are now locked by default when loading IFCs 2022-05-05 09:28:36 +10:00
Dion Moult dd127db969 Minor documentation improvements 2022-05-04 16:04:18 +10:00
Dion Moult 194bc53bdb Minor refactor 2022-05-04 15:54:15 +10:00
Dion Moult 8c3e29c351 Split BlenderBIM and other docs to not overwhelm users. Use Furo theme for Sphinx. 2022-05-04 13:06:39 +10:00
Gorgious56 6e55a04550 Refactor Standalone section shader (#2172)
* Refactor Standalone section shader

* Remove leftover code
2022-05-03 18:21:51 +02:00
Dion Moult 9333839267 Copying an element now also copies its distribution ports 2022-05-03 18:05:27 +10:00
Dion Moult 280236290e Fix #2182. File export IFC menu item now defaults to save as to improve usability for new users 2022-05-03 17:36:55 +10:00
Dion Moult c5773b5665 Port locations are now auto synchronised when hiding ports 2022-05-03 16:50:55 +10:00
Dion Moult 96babc4506 Creating a flow segment now also creates 2 ports at either end 2022-05-03 15:48:41 +10:00
Dion Moult f8acde0801 You can now add ports to types, and ports are created when placing occurrences of that type 2022-05-03 14:32:18 +10:00
Dion Moult 32c101cf74 #1153 Implement angular dimension annotations 2022-05-02 09:20:40 +10:00
Bruno Perdigão 2a1aa535ba Added VSCode extension to Live Development section 2022-05-02 08:06:27 +10:00
Dion Moult 02903c4929 #1153 New arc from 3 points command for 2D CAD 2022-05-01 17:25:03 +10:00
Dion Moult d80e26e4ab Design new icon for CAD workspace tool 2022-05-01 15:30:37 +10:00
Dion Moult a88b83306e #1153 New arc from 2 points command for 2D CAD (based on view direction) 2022-05-01 14:43:03 +10:00
Dion Moult 9577c40386 Fix #2107. Materials and styles in the scene manager are now shown alphabetically 2022-05-01 12:01:24 +10:00
Dion Moult f54a5cf6e0 Fix #2048. Fix bug where material and style IFC links were severed after reloading a saved Blender file 2022-05-01 09:41:03 +10:00
Dion Moult abcb492e5a Fix #2174. Don't show meaningless context dropdown when reassigning class. 2022-05-01 08:57:23 +10:00
Dion Moult 4d447c169f Fix #2176 2022-05-01 08:50:25 +10:00
Dion Moult b465605b63 #1153 CAD tool now provides hotkey access to offset edges command 2022-04-30 22:55:45 +10:00
Dion Moult fb6dc45b1b #1153 New fillet command for 2D CAD supporting both bevel and chamfer 2022-04-30 22:44:37 +10:00
Dion Moult b2aca0ae36 #1153 New mitre command for 2D cad (like chamfer with 0 distance) 2022-04-30 18:17:39 +10:00
Dion Moult 03831d440c #1153 New CAD workspace with hotkeys for super fast CAD operations 2022-04-30 15:50:40 +10:00
Dion Moult ac4679a376 #1153 New CAD module with basic trim / extend tool for 2D linework 2022-04-30 11:40:19 +10:00
Dion Moult 21fee721be #1153 Implement adding and removing schedules from a sheet 2022-04-29 13:32:34 +10:00
Dion Moult 6bcf00ef31 #1153 Reimplement ability to build SVG tables from referenced ODS documents 2022-04-28 19:57:47 +10:00
Christian Martínez de la Rosa 952d1c3b79 [IfcConvert] Added IfcWorkPlan to xml export 2022-04-28 17:54:43 +10:00
Christian Martínez de la Rosa 2d7a90bd2b [IfcConvert] Fix crash when IfcWorkCalendar does not contain IfcWorkTime 2022-04-28 17:54:43 +10:00
Christian Martínez de la Rosa b14de15a68 [IfcConvert] Added Output objects in the XML file 2022-04-28 17:54:43 +10:00
Christian Martínez de la Rosa 29ecba293b [IfcConvert] Added calendars to xml output file 2022-04-28 17:54:43 +10:00
Christian Martínez de la Rosa 8396c68e75 Added task to property / quanty relationship 2022-04-28 17:54:43 +10:00
Christian Martínez de la Rosa 01c6a0d9be [IfcConver] Changed OperatesOn hierarchy level in xml task export 2022-04-28 17:54:43 +10:00
Christian Martínez de la Rosa 99d8a225a9 Get IfcTasks from IfcWorkSchedule 2022-04-28 17:54:43 +10:00
Christian Martínez de la Rosa 89f13aa1e4 [IfcConvert] Added IsSuccessorFrom and IsPredecessorTo in xml task export 2022-04-28 17:54:43 +10:00
Christian Martínez de la Rosa f089d03dc6 [ifcConvert] Add related objects in tasks 2022-04-28 17:54:43 +10:00
Christian Martínez de la Rosa a4366ef151 [IfcConvert] Export tasks in the xml conversion
The exportion in the XML format exports in a new tag, named "tasks", all the tasks from the file with a hierarchy. Each task contains the IfcTaskTime object if it has it.
2022-04-28 17:54:43 +10:00
Dion Moult 9483abcdd5 Revert "Refactor BIM_PT_gis"
This reverts commit 265ebaebb8.
2022-04-28 17:25:31 +10:00
Dion Moult 200a343746 Revert "Refactor BIM_PT_gis added files"
This reverts commit 445e77bac0.
2022-04-28 17:25:31 +10:00
Dion Moult 2313fa00cc Revert "minox fix"
This reverts commit dfb980e0d1.
2022-04-28 17:25:31 +10:00
Dion Moult 99c5f895fc Revert "Finish refactor georeference module"
This reverts commit fcd5908ac9.
2022-04-28 17:25:31 +10:00
maxfb87 fcd5908ac9 Finish refactor georeference module 2022-04-28 14:34:35 +10:00
maxfb87 dfb980e0d1 minox fix 2022-04-28 14:34:35 +10:00
maxfb87 445e77bac0 Refactor BIM_PT_gis added files 2022-04-28 14:34:35 +10:00
maxfb87 265ebaebb8 Refactor BIM_PT_gis 2022-04-28 14:34:35 +10:00
Dion Moult 07fa7003fb Minor fix 2022-04-28 14:34:18 +10:00
Ryan Schultz 3a90d664fa added 3"=1'-0" scale (a common scale), and corrected 6"=1'-0" scale 2022-04-28 14:26:37 +10:00
Dion Moult 3f28ae18c0 #1153 You can now add / remove / open schedules, implemented as IFC documents 2022-04-28 14:16:39 +10:00
Dion Moult 9ea6499b5a #2082 You can now filter by metadata existence in the selector syntax 2022-04-27 22:20:12 +10:00
Adrien SCHVALBERG 11c3a6e547 Fix ifcparse/Ifc____.h template 2022-04-27 14:08:05 +02:00
Adrien SCHVALBERG e7f3856d64 Fix shared libraries exports. 2022-04-27 14:08:05 +02:00
Dion Moult 7e7d576855 Basic WIP Australian project library with some materials and walls 2022-04-27 20:45:34 +10:00
Dion Moult cfa1428a75 Fix bug where material layer set usage axis was not consistent for non walls 2022-04-27 20:44:26 +10:00
Dion Moult d4ee74455c Show construction type descriptions as tooltips in BIM authoring tool 2022-04-27 20:36:32 +10:00
Dion Moult 07d2cc62c1 Fix #2163 bug where launching viewing apps could freeze Blender 2022-04-26 09:53:32 +10:00
Dion Moult 8aff4badf7 Minor housekeeping 2022-04-25 19:37:03 +10:00
Dion Moult aa3325c010 #1153 Implement diameter annotation 2022-04-25 10:32:33 +10:00
Dion Moult 9b13cd8d5f #1153 Dimensions can now have custom text 2022-04-25 10:02:00 +10:00
Dion Moult 0fe32c0c75 #1153 Add support for radius annotations 2022-04-25 09:54:08 +10:00
Thomas Krijnen efc43c0fe9 #1153 merge-cells option in draw 2022-04-24 14:54:49 +02:00
Thomas Krijnen 5bfaa32c7c #1153 add ray_distance to tree result 2022-04-24 14:51:24 +02:00
Thomas Krijnen 38e4ccdb0a #1153 submodule 2022-04-24 14:50:53 +02:00
Dion Moult 7b71a7a866 #1153 You can now include and exclude elements from a drawing using queries 2022-04-24 22:15:14 +10:00
Dion Moult b5ee20cb7a #1153 New global referencing property determines whether or not a drawing is mean to be referenced from all other drawings 2022-04-24 18:39:22 +10:00
Dion Moult cc8da4abf4 #1153 Symbol background option for text is now stored in a pset 2022-04-24 13:13:21 +10:00
Dion Moult f2c4b20187 #1153 Experimental support for curve bounded planes for 3D annotations 2022-04-24 12:25:04 +10:00
Dion Moult 7c460bcf29 #1153 Viewport decoration now supports miscellaneous linework and fills 2022-04-24 11:12:24 +10:00
Dion Moult 31ddb0f2c5 #1153 Add support for Annotation3D representations 2022-04-24 10:00:31 +10:00
Thomas Krijnen c2cad06cfa Default BUILD_QTVIEWER to off for the time being 2022-04-23 15:39:03 +02:00
Dushyant Basson 421b58850c Updated to use Qt5 2022-04-23 15:19:10 +02:00
Dushyant Basson 7c89aa1098 Enabled option to build QtViewer 2022-04-23 15:19:10 +02:00
Dushyant Basson be7e85fc2d Revert "Updated to use Qt5"
This reverts commit 06fe4abf6a.
2022-04-23 15:19:10 +02:00
Dushyant Basson d91442de59 Revert "Enabled option to build QtViewer"
This reverts commit b82dddf441.
2022-04-23 15:19:10 +02:00
Dushyant Basson 9cd3d73a23 Enabled option to build QtViewer 2022-04-23 15:19:10 +02:00
Dushyant Basson 0cf3c97eb2 Updated to use Qt5 2022-04-23 15:19:10 +02:00
Dushyant Basson d49095844a added output directories to .gitignore 2022-04-23 15:19:10 +02:00
Dion Moult be65f77311 #1153 Breaklines are now pure lines separate from fill areas and orient correctly 2022-04-23 18:56:06 +10:00
Thomas Krijnen 2081dc4c08 #2156 Don't try to convert unset placements in element hierarchy parsing 2022-04-23 10:51:16 +02:00
Dion Moult bd16345b76 #1153 Utility to convert an edge to an arc in preparation for angular dimensions 2022-04-23 17:25:39 +10:00
Dion Moult 3264f338c7 #1153 Section RLs now show storey names or custom descriptions 2022-04-23 11:10:02 +10:00
Dion Moult 0c06c67f2d #1153 Storey level annotations are now automatically generated for sections and elevations 2022-04-22 12:41:21 +10:00
Dion Moult a66ba6a6f9 #1153 Reimplement metadata keys so you can specify attribute-based classes in drawings 2022-04-22 10:06:28 +10:00
Massimo Fabbro 80d8665992 Insert the link to Reboot Addon 2022-04-21 11:13:32 +10:00
Dion Moult 337ce89919 Fix bug where "Not Georeferenced" showed up twice in the Georeferencing UI panel 2022-04-21 10:47:09 +10:00
Dion Moult 1237a8ce97 Improve georeferencing documentation 2022-04-21 10:41:47 +10:00
Dion Moult c3c9ed2975 #2150 Document how to manually reconcile large coordinate ranges 2022-04-21 09:58:28 +10:00
Dion Moult 87cb1c69f4 #2150 Fix bug where cartesian point offsets didn't consider object placement rotations 2022-04-21 09:09:34 +10:00
Dion Moult 3aa7f8e4c8 #2150 Fix bug where manual offsets and autodetected offset could stack 2022-04-21 09:08:08 +10:00
Dion Moult 2c301d734b #1153 You can now specify the ZIndex of 2D annotations 2022-04-20 16:28:10 +10:00
Thomas Krijnen 1705e6f565 Changes for mpfr toolset discussed in #1993 2022-04-19 15:33:09 +02:00
renjianqiang 2cd7b08c47 Fix the problem that build dependencies failed under Debug mode 2022-04-18 09:56:41 +02:00
renjianqiang 647a5105b5 Fix the bug that can't install python when IFCOS_INSTALL_PYTHON is true 2022-04-18 09:56:41 +02:00
Dion Moult 9fc0baf175 #1153 Add support for IfcAnnotationFillArea representation 2022-04-18 10:27:20 +10:00
Dion Moult ca502cc293 #1153 Fix bugs with importing existing annotations 2022-04-18 10:27:20 +10:00
CyrilWaechter f70ab060e3 Add default when getting space collection
A case has been encountered where space object and space collection did
not share the same name (eg. space.0011 and space.0021).
@Moult I wonder if it is expected or if there is a consistency issue
with space object/collection naming.
2022-04-17 15:10:18 +02:00
Dion Moult 6fb3c28f1a #1153 Switching representations for annotations and drawing cameras now works 2022-04-17 18:41:19 +10:00
Dion Moult c79eb60959 #1153 Drawings that are pure annotation now do not generate references 2022-04-17 12:29:34 +10:00
Dion Moult 538419a23f #1153 New markers and patterns for insulation batting, steel, and wood grain 2022-04-17 12:19:20 +10:00
Dion Moult eb19165ebc #1153 Predefined type is now also included in CSS classes for drawings. 2022-04-16 18:32:17 +10:00
Dion Moult 1138ffee7e #2139 Fix bug where some objects were invalidated during undo or redo 2022-04-15 19:45:59 +10:00
Dion Moult 0f3416774c #1153 Drawing layers (underlay, linework, annotation) are now per drawing instead of global 2022-04-15 11:45:23 +10:00
Dion Moult 1ba7977587 #1153 Fix newlines in text literals and smart tags in SVGs 2022-04-14 21:08:06 +10:00
Dion Moult 5b1b8e8f50 #1153 Annotation reference and sheet IDs now implemented in SVG. 2022-04-14 15:20:09 +10:00
Dion Moult 78811d331b #2136 Minor fix 2022-04-14 15:20:09 +10:00
Jesusbill 4aa91cfa7f add feature to toggle wireframe visibility for linked models - no testcases 2022-04-13 18:28:48 +02:00
Dion Moult c099a6cb75 #1153 Drawing titles now display IFC scale and identification data 2022-04-13 12:21:18 +10:00
Dion Moult 029b402d11 Minor fix 2022-04-12 18:30:03 +10:00
Dion Moult a243cdd6c6 Minor fix 2022-04-12 18:09:45 +10:00
Dion Moult 1fae835de9 Update installation docs to distinguish between package from source and developer setups 2022-04-12 18:07:39 +10:00
Dion Moult f0c7ff1e57 #1153. Titleblock revision / data is are derived from IfcDocumentInformation 2022-04-12 08:29:29 +10:00
Thomas Krijnen 460fd5ca87 #2133 faceset helper: compare index vectors by address instead of value 2022-04-11 14:12:17 +02:00
Thomas Krijnen 054c0a9a27 Improve faceset logging 2022-04-11 14:08:44 +02:00
Thomas Krijnen 5af1baa48b Update release.yml 2022-04-10 20:47:01 +02:00
Thomas Krijnen 0e5d2cc0c9 Update release.yml 2022-04-10 10:15:07 +02:00
Dion Moult 48148f00a3 #2129 Support some FreeCAD annotation types. 2022-04-09 10:22:12 +10:00
Thomas Krijnen 564c4b7644 #2126 reverse 1st operand inner wires in 2d opening subtraction 2022-04-08 10:45:28 +02:00
Bruno Postle 33c53a2c9a regenerate site library 2022-04-08 16:49:06 +10:00
Bruno Postle c049007ee8 add missing import 2022-04-08 16:49:06 +10:00
Bruno Postle 05345e8ddf Add missing demo library 2022-04-08 16:49:06 +10:00
Thomas Krijnen 461b78665a OCCT 7.6 compatibility 2022-04-04 08:29:29 +00:00
Boris Brangeon 6160d65d18 Update selector.py
filter_rule.children[2].children[0].lower() == 'true'
2022-03-31 20:50:26 +02:00
Thomas Krijnen d526314190 #2115 Let --strict-tolerance govern degeneracy of periodic curve trim 2022-03-31 11:28:30 +02:00
Massimo Fabbro 85bac37bed Fix two broken links
Fixed "./installation.rst" and "./running_tests.rst" links
2022-03-30 20:31:00 +11:00
Dion Moult e38184b152 Adding a document now auto adds a reference for convenience. 2022-03-30 18:15:26 +11:00
Dion Moult a0c8d87ad0 #1153. You can now assign and remove drawings from sheets. 2022-03-30 18:15:11 +11:00
Dion Moult 8686462eed #1153. Section markers are now supported in SVG. 2022-03-29 20:44:20 +11:00
Dion Moult 6cd4a2d78b #1153. Add support for elevation symbols in generated SVGs. 2022-03-29 20:30:10 +11:00
Dion Moult 2c6cb2fbb2 #1153. Section references are now auto generated when activating a drawing. 2022-03-29 17:04:50 +11:00
Dion Moult a60dfd8dd5 #1153. Elevation references are now auto generated when activating drawings. 2022-03-29 10:01:41 +11:00
Dion Moult 1b1fd1e6c5 #2048. Minor fix. 2022-03-28 12:52:29 +11:00
Dion Moult 1e60f23880 #2081. Bump IfcOpenShell build. 2022-03-28 11:58:38 +11:00
Dion Moult eb7babb1b0 #2053. Minor fix. Enable strict tolerance everywhere. 2022-03-27 19:05:05 +11:00
Dion Moult 5797937825 #1951. Fix bug where opening standard cases couldn't be added as voids. 2022-03-27 18:21:22 +11:00
Dion Moult 28336140ca #2048. Blender materials, IFC materials and IFC presentation style names are now bi-directional. 2022-03-27 14:43:05 +11:00
Dion Moult ca03a88601 #2104. Support for adding conversion based units to the project. 2022-03-27 10:39:01 +11:00
Thomas Krijnen 0303d217bf #2105 Fix fillets 2022-03-26 13:50:36 +01:00
Dion Moult 4184f2bf46 #2094. Removing styles now also purges Blender material object if possible. 2022-03-26 21:17:52 +11:00
Dion Moult 3b0429ace9 #2106. Fix bug where geometry cache was not cleared when openings were changed 2022-03-26 20:38:30 +11:00
Dion Moult baf75f552e New barebones presentation style manager to see the styles in your project. 2022-03-26 18:15:30 +11:00
Dion Moult 4ea0c73081 Fix bug where we now accommodate invalid durations in the scheduling tree 2022-03-25 19:37:52 +11:00
Dion Moult 33852be3f1 Fix bug where lag times couldn't be edited 2022-03-25 19:37:09 +11:00
Dion Moult 48d476c2b3 Gantt charts now show ISO durations and task captions 2022-03-25 16:09:22 +11:00
Dion Moult 9db07883b7 Scheduling calculations no longer calculate from midnight and now handle a single 9-5 day as a duration of 1 day, as per industry standard practice. 2022-03-25 16:09:22 +11:00
Dion Moult 7af53c543d Fix bug with schedule calculation on zero duration tasks 2022-03-25 16:09:22 +11:00
Dion Moult e7f6ed73ac Fix bug where recalculating a schedule with no time data would fail 2022-03-25 16:09:22 +11:00
Dion Moult 8afa1a4f7d Fix bug where counting working days on a task without a calendar would fail 2022-03-25 16:09:22 +11:00
Dion Moult 1dd85f6c48 Fix bug where removing a rooted or calendar constrained task would purge all calendar assignments 2022-03-25 16:09:22 +11:00
dependabot[bot] 8bd826fb20 Bump numpy in /src/ifcopenshell-python/ifcopenshell
Bumps [numpy](https://github.com/numpy/numpy) from 1.20.3 to 1.21.0.
- [Release notes](https://github.com/numpy/numpy/releases)
- [Changelog](https://github.com/numpy/numpy/blob/main/doc/HOWTO_RELEASE.rst.txt)
- [Commits](https://github.com/numpy/numpy/compare/v1.20.3...v1.21.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-24 10:02:23 +01:00
dependabot[bot] cbc6eefca7 Bump lxml from 4.6.3 to 4.6.5 in /src/ifcopenshell-python/ifcopenshell
Bumps [lxml](https://github.com/lxml/lxml) from 4.6.3 to 4.6.5.
- [Release notes](https://github.com/lxml/lxml/releases)
- [Changelog](https://github.com/lxml/lxml/blob/master/CHANGES.txt)
- [Commits](https://github.com/lxml/lxml/compare/lxml-4.6.3...lxml-4.6.5)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-24 10:01:13 +01:00
Boris Brangeon 4f86dd143a Update selector.py (#2102)
Add BOOLEAN: "TRUE" | "FALSE" | "true" | "false"| "True" | "False"
2022-03-24 11:02:58 +11:00
Dion Moult c45c73c563 #2078. Fix bug where flat materials didn't import correctly. 2022-03-23 20:19:19 +11:00
Dion Moult 38f1c622ac Allow case insensitive IFC file extensions when exporting 2022-03-23 11:54:54 +11:00
Thomas Krijnen 38170db32a #2080 Don't allow parameterless Kernel constructor 2022-03-22 22:30:27 +01:00
Dion Moult 43d0c098a0 #2089. Fix and simplify OffsetObjectPlacement recipe for multiple absolute placements. 2022-03-22 17:27:38 +11:00
Dion Moult 533111a1fb #2099. Materials are now sorted alphabetically. 2022-03-22 14:30:45 +11:00
Dion Moult 0bba31e5f9 You can now select elements by material in the material manager. 2022-03-22 13:25:26 +11:00
Dion Moult 9b9f0058f9 The material manager now shows total elements used by a material 2022-03-22 12:50:10 +11:00
Dion Moult 0c8273eb70 Minor fix 2022-03-22 11:47:37 +11:00
Dion Moult c072554f21 You can now use the selector with a prefiltered list of elements 2022-03-22 11:32:40 +11:00
Dion Moult 18c9a57bea Using the selector now supports recursive decomposition in the spatial hierarchy 2022-03-22 11:23:13 +11:00
Dion Moult eaa3301b8f New support for filtering elements by checking values against an enumeration 2022-03-22 10:49:48 +11:00
Dion Moult efb89acfa3 You can now use a "not" operator when selecting elements 2022-03-22 10:40:05 +11:00
Dion Moult 96256d365a Fix bug where you couldn't compare less than or equal to with the selector. 2022-03-22 10:39:10 +11:00
Dion Moult fa6ec25a39 #2082. You can now use the selector to filter by strict data types. 2022-03-21 18:03:25 +11:00
Dion Moult 44afa0df5d Selector now uses classmethods to make it easier for scriptwriters to use 2022-03-21 17:28:16 +11:00
Dion Moult c478e5bf1b #2094. Deleting materials now also deletes Blender material if not used for a style 2022-03-21 12:04:51 +11:00
Dion Moult ea530858bd You can now remove material sets independent of elements 2022-03-21 12:03:15 +11:00
Dion Moult 0b4cfea3a9 Removing a material now also removes material properties 2022-03-21 10:50:19 +11:00
Dion Moult e909f25432 Removing a property set now also removes properties 2022-03-21 10:22:31 +11:00
Thomas Krijnen 2985bba157 #650 Unsure about 1b5c20e14f 2022-03-19 20:18:12 +00:00
Thomas Krijnen a1ba61636d HDF5 debug postfix 2022-03-19 19:32:42 +00:00
Thomas Krijnen 1b5c20e14f #650 fclose() 2022-03-19 14:45:19 +01:00
Dion Moult a43fae68b6 #2094. You can now add material sets independently of IFC objects in the material manager 2022-03-19 22:13:07 +11:00
Dion Moult fdd03d7294 #2094. Removing materials now removes material definitions too for styles 2022-03-19 22:13:07 +11:00
Thomas Krijnen 2049bdd976 todo note 2022-03-19 11:36:38 +01:00
Thomas Krijnen d45a439f6c todo note 2022-03-19 11:36:30 +01:00
Thomas Krijnen 8fa5873e7e #2095 take into account face-face distance on operand a 2022-03-19 10:49:51 +01:00
Dion Moult 3fa8fce994 Fix bug where gizmo still shown after deleting objects 2022-03-19 17:27:45 +11:00
Dion Moult b8aa90de00 #2094. New rudimentary project materials browser to manage materials. 2022-03-18 17:24:59 +11:00
Dion Moult dcc1ed5607 #1153. References to grids in drawings are now auto synchronised when activating a drawing view. 2022-03-18 16:00:49 +11:00
Dion Moult 2bfff12988 #1153. Drawing names are now generated based on their location and show an icon based on the drawing type. 2022-03-18 15:49:56 +11:00
Dion Moult 074a55b816 Fix bug where delete confirmation was not consistent with vanilla Blender 2022-03-17 14:10:51 +11:00
Dion Moult a53fd97e5a Fix #2092. Fix bug where door and window styles were excluded from authoring options. 2022-03-17 09:49:55 +11:00
Dion Moult b39a0d1a7c #2053. Revert strict tolerance due to side-effects with caching 2022-03-16 19:11:37 +11:00
Dion Moult 44b786a21b Ensure that exported 4D schedules use a .xml extension 2022-03-16 19:11:13 +11:00
Dion Moult bddc64cad3 Fix #2049. Allow relative paths for IFC links. 2022-03-16 10:55:36 +11:00
Dion Moult 3713ec00f9 #2049. You can now edit linked project paths when unloaded. Thanks brunoperdigao! 2022-03-16 10:46:42 +11:00
Dion Moult b5a58998d5 Fix #2083. hpp-fcl now has support for Python 3.10. 2022-03-16 09:58:52 +11:00
Dion Moult ca3b4d17ba Fix #2055. Disable IfcContext in IFC2X3 because it didn't exist. 2022-03-16 09:37:20 +11:00
Dion Moult 983a1530b8 #2053. Potentially fix geometry issues with importing small objects. 2022-03-16 09:09:43 +11:00
Dion Moult 74e18a56ce Fall back to information data if reference data is null for associated documents. 2022-03-15 19:54:04 +11:00
Cyril Waechter 05834d071f Handle empty ConnectionGeometry + refactor (#2085) 2022-03-15 19:34:37 +11:00
Dion Moult 8f70f462cc You can now easily open associated documents with support for inherited locations 2022-03-15 19:32:29 +11:00
Dion Moult 456f45b346 You can now use relative paths when browsing for file URIs. 2022-03-15 18:58:51 +11:00
Dion Moult 53a9175802 You can now browse to select files for any URI reference attributes 2022-03-15 18:05:44 +11:00
Dion Moult 379d6d9392 Minor fix 2022-03-15 16:53:31 +11:00
Dion Moult 38b708ff1f Redesigned document UI to handle hierarchical documents and make it easier to reference 2022-03-15 14:49:20 +11:00
Dion Moult 233e73f9f6 Removing documents now removes all subdocuments recursively and references 2022-03-15 14:45:18 +11:00
Dion Moult db85aa90d3 You can now add document references that are part of a document information 2022-03-15 14:43:24 +11:00
Dion Moult 6fb1e34ab4 You can now add IFC subdocuments or supersede documents 2022-03-15 14:41:47 +11:00
Dion Moult 9cc683ea6e Fix bug where ownership history was not tracked on document association. 2022-03-14 20:39:39 +11:00
Dion Moult e343494ab4 Document information is now always assigned to the project by default. 2022-03-14 20:39:11 +11:00
Dion Moult bc9e403a58 #2088. Minor fix. Sorry more stuff I forgot to commit. 2022-03-14 20:07:51 +11:00
Thomas Krijnen 8ffd9dc1f9 #2086 surface_area and volume 2022-03-14 09:54:28 +01:00
Dion Moult 57f6c07240 Minor fix 2022-03-14 18:40:54 +11:00
Dion Moult f518bbfb00 Minor fix 2022-03-14 16:48:44 +11:00
Dion Moult 0f34420834 Fix missing pkg_resources brickschema dependency 2022-03-14 16:43:27 +11:00
Dion Moult 3ef6077a0d Create build for Blender 3.1 Python 3.10 2022-03-14 16:43:10 +11:00
Gorgious56 3c26cb672b Compute wall net quantities (#2086)
* Compute wall net quantities

* Remove opening check clause
2022-03-14 12:48:50 +11:00
Dion Moult 673f757a51 Fix bug where adding document references didn't work. 2022-03-09 22:14:59 +11:00
Dion Moult 578ab140cc Create section and elevation contexts on new projects. See #1153. 2022-03-09 21:44:26 +11:00
Thomas Krijnen 96902bf38a Safeguard against runtime error when invoking HLR without storey containment 2022-02-27 20:57:05 +01:00
Thomas Krijnen b5133c6874 Add caching option in iterate() helper 2022-02-27 12:56:16 +01:00
Thomas Krijnen 5dfe85691b #1153 Exclude walls and slabs in profile edges hack 2022-02-27 12:55:39 +01:00
Thomas Krijnen 3b7bf8844a Update default SVG CSS for projections 2022-02-27 12:55:11 +01:00
ArturTomczak 8038b77504 typo 2022-02-26 21:56:02 +01:00
ArturTomczak b64601ee0a Update subrepo with sample files 2022-02-26 21:56:02 +01:00
ArturTomczak 320bb7a086 Update to IDS 0.5.8 2022-02-26 21:56:02 +01:00
ArturTomczak ad135f70f1 encoding clean up
the use of binary access to file was not intentional, switched to @aothms  proposal.
2022-02-26 21:56:02 +01:00
ArturTomczak 15b24d8ba4 validation tests 2022-02-26 21:56:02 +01:00
ArturTomczak 3c2b25d923 fix recurrsion 2022-02-26 21:56:02 +01:00
ArturTomczak 3282d75aa5 fix encoding bcf bug, add tests 2022-02-26 21:56:02 +01:00
ArturTomczak 72fc617196 internalise tests #1711
moved test files to submodule to avoid URL requests. Had to turn off (comment) two tests as they were not working but will fix them with new schema update.
2022-02-26 21:56:02 +01:00
ArturTomczak 47cad88c38 add IDS requirements #1727 2022-02-26 21:56:02 +01:00
Thomas Krijnen e81dd9c7df Option to yield progress from iterate() and consume_iterator() 2022-02-26 14:57:21 +01:00
Thomas Krijnen d16f92a445 Option to set log format from python 2022-02-26 14:53:09 +01:00
Thomas Krijnen 8be79f9aee Add XML and glTF serializers to python 2022-02-26 14:52:58 +01:00
Dion Moult ccc6f2c4c6 Fix bug in date calculations when editing task times without sufficient data 2022-02-23 13:08:52 +11:00
Dion Moult deb0597000 Fix infinite loops: accommodate invalid calendars with no working times in sequence utility 2022-02-23 13:04:29 +11:00
Dion Moult 348373d47e Fix bug with editing task start times without finishes 2022-02-23 12:28:34 +11:00
Dion Moult 9e22e9486a You can now export Microsoft Project XMLs from Blender 2022-02-21 19:57:00 +11:00
Dion Moult 3cf9017f27 New IFC2MSP conversion tool in IFC4D to convert IFC to Microsoft Project 2022-02-21 19:56:06 +11:00
Dion Moult 21479482b4 Fix bug where MS project durations expressed in hours were not parsed correctly 2022-02-21 19:55:32 +11:00
Dion Moult 97c660fec8 Fix bug with incorrect MS Project XML working day imports 2022-02-21 19:55:08 +11:00
Dion Moult 32af2025ec Fix bug with incorrect MS Project XML sequence type import 2022-02-21 19:54:45 +11:00
Dion Moult ffc82dfb1e Fix bug where timeline date didn't update during animations 2022-02-21 19:52:17 +11:00
Dion Moult 2a69af43f6 Calendars are now assigned when importing Microsoft Project XML to IFC 2022-02-21 15:24:03 +11:00
Dion Moult 5c9984a12a Fix bug with broken MS Project to IFC conversion 2022-02-21 13:51:26 +11:00
Dion Moult c938d6ae9b Very minimal implementation of generating parametric duct segments from duct size properties 2022-02-18 19:21:35 +11:00
Dion Moult 63d65f0c39 Fix #2046. Fix crash if style was updated from an already loaded IFC dataset 2022-02-18 18:44:25 +11:00
Dion Moult 472a66b7ed Fix bug where element types couldn't get psets applied to them due to lack of specificity in IFC templates 2022-02-18 17:11:24 +11:00
Dion Moult ddaed9e93d Fix bug where psets were unable to be applied for specific predefined types 2022-02-18 17:10:38 +11:00
Dion Moult 4d6eba0a06 Fix #2045. Fix bug where IFC2X3 occurrences not correctly autodetected from types. 2022-02-18 10:06:13 +11:00
ssg ed09b7ba31 Fixed failure for VS2017
Script was trying to pick up vs19 instead of vs15 even for builds on vs15, fixed that by adding a check for vs15.
2022-02-12 08:13:16 +01:00
Thomas Krijnen 2c2954b11a #2036 check for null curve in IfcIndexedPolyCurve 2022-02-10 14:04:08 +01:00
Thomas Krijnen 5317746a82 #2037 Rerun codegen 2022-02-10 13:06:32 +01:00
Thomas Krijnen 5af7e95bee #2037 Option to optain declaration from early bound select definitions 2022-02-10 13:06:04 +01:00
Thomas Krijnen 618d902a57 #2037 throw on invalid cast in schema code 2022-02-10 13:05:33 +01:00
Thomas Krijnen 68baa416fa #2037 option to throw on invalid cast 2022-02-10 13:05:05 +01:00
Dion Moult 7f212963d5 The storey is now the default container when starting a project for convenience. 2022-02-10 11:40:38 +11:00
Dion Moult d3a43c6e81 Be more forgiving to invalid Revit IFCs when importing. 2022-02-10 11:06:23 +11:00
Dion Moult a44a13f04c Implement loading glTF style textures from IFC. 2022-02-09 16:40:23 +11:00
Dion Moult 126ba8d88f Fix #2032. Fix bug where certain elements could not be extracted or appended. 2022-02-09 15:16:22 +11:00
Dion Moult caf04200ce Experimental optimisation attempt for native faceted brep handling by aothms. See #841. 2022-02-09 14:36:14 +11:00
Dion Moult 05f3cd41e2 glTF rendering styles for unlit and PBR materials are now supported when loading IFCs 2022-02-09 14:26:39 +11:00
Dion Moult 80bc4cdb1b Fix #2028. OffsetObjectPlacements can now rotate then move, or vice versa. Warning: new default! Thanks hvn74! 2022-02-09 12:17:07 +11:00
Dion Moult dd7a47ab99 Texture support is now compatible with glTF texture maps 2022-02-09 11:34:59 +11:00
Dion Moult 4eaf0e7a9c Fix bug where you couldn't downgrade or upgrade between shading and rendering styles 2022-02-09 11:31:09 +11:00
Dion Moult 3db252bcc0 Minor fix 2022-02-08 15:50:55 +11:00
Dion Moult d03685abfa UV coordinates for triangulated face sets are now saved in IFC. 2022-02-08 15:32:03 +11:00
Dion Moult 3e4b1d95cb Standalone section shader add-on for those who don't need the full BlenderBIM add-on features 2022-02-06 20:27:44 +11:00
Dion Moult b151ec2b0e Fix bug where false geolocation offsets did not take into account rotation. 2022-02-06 20:04:53 +11:00
Thomas Krijnen f909adc2dc Update README.md 2022-02-05 09:57:56 +01:00
Dion Moult 913a428794 Fix #2017. Fix P6 import bug where older P6 XER versions <8.1 did not store hours per day. 2022-02-05 17:44:59 +11:00
Dion Moult fe6d9ae926 Triangulated face set geometry exports are now supported. 2022-02-05 11:33:14 +11:00
Thomas Krijnen 4a679812ca #2018 prevent select types to be serialized as booleans 2022-02-04 13:54:07 +01:00
Dion Moult 6be87c035f Annotations are now auto collected correctly and simpler drawings UI. See #1153. 2022-02-04 19:21:24 +11:00
Dion Moult 6ce8a3621a You can now manage IFC actors in Blender. 2022-02-04 18:21:53 +11:00
Dion Moult 60ea856e51 New functions for IFC actor in API 2022-02-04 17:56:45 +11:00
Dion Moult 419502ddb7 Disable adding a grid before a project exists. 2022-02-04 16:23:04 +11:00
Dion Moult 9f530d0578 Updating drawing names now updates it in IFC too. See #1153. 2022-02-04 15:04:30 +11:00
Dion Moult 524483e9cc Target view is now read from IFC. See #1153. 2022-02-04 12:04:20 +11:00
Dion Moult 7116a9f71a Removing drawings now roundtrips in IFC. See #1153. 2022-02-04 10:54:18 +11:00
Dion Moult 868ced2990 See #2017. Fix bug in P6 importer where nonlabor types were not considered. 2022-02-04 09:10:55 +11:00
Dion Moult 6a8292d24c Fix #890. New N/S/E/W location presets for creating sections, elevations, and RCPs. See #1153. 2022-02-04 08:14:36 +11:00
Dion Moult fca4d9dea0 New save and save as buttons in project panel with last saved date. 2022-02-03 21:29:44 +11:00
Dion Moult 89d5c44855 You can now create new projects starting from a template. 2022-02-03 18:25:03 +11:00
Dion Moult 3e99393895 You can now add plan views based on a building storey. See #890. 2022-02-03 17:28:19 +11:00
Gorgious56 098ab2c237 Spatial Elements Utilities and QOL (#2008)
* Tool to add storey to specific building

* Operator to add new storey to building

* Add new storey to building button in aggregates panel

* Minor fix

* add method to data class to retrieve ifc class name

* Extract aggregate creation logic

* Prevent aggregating non-ifc elements + display popup info

* Add tooltips + rename operators

* Rename function to get relating object

* Rename relating object data variable to be able to have access to related objects

* Add utilities to get related objects

* Add utility function to get related objects from element

* display related object in Aggregates panel in object properties

* Revert "Rename function to get relating object"

This reverts commit 5248627a41.

* Update method names

* Add Operator to select parts of an element

* Ditch box layout and only display number of parts

* Add operator to select aggregate of an element

* Display operator to select aggregate of element

* Move logic to aggregate module

* Add utility methods to Blender tool

* Add buttons to create Ifcsite and IfcBuilding from object panel
2022-02-03 07:16:43 +01:00
Dion Moult 94ff8349d3 You can now connect, disconnect, and set flow directions of distribution ports in Blender. 2022-02-03 12:05:05 +11:00
Henri J. Norden d333cb1b78 Cleanup changes 2022-02-02 14:20:46 +01:00
Henri J. Norden 3f4c4a8e52 Hoist compiling mpir & mpfr before HDF5, since they are more likely to fail and HDF5 takes way too long 2022-02-02 14:20:46 +01:00
Henri J. Norden b103e0dde5 Fix build-deps.cmd for VS2019 2022-02-02 14:20:46 +01:00
Dion Moult 883b8a523c Material node trees compliant with basic X3D multitextures are now stored in IFC 2022-02-02 17:51:40 +11:00
Dion Moult 8c0b06b36e Different BSDFs are now recognised for different reflectance methods. 2022-02-02 17:50:38 +11:00
Dion Moult 72397f3aff Saving surface specular roughness values to IFC is now supported. 2022-02-02 15:09:37 +11:00
Dion Moult 77821caae4 Adding surface styles now respect mutually exclusive style types 2022-02-02 15:04:19 +11:00
Dion Moult 10138e28c9 You can now add ports to distribution elements in Blender. 2022-02-01 19:16:31 +11:00
Gorgious f9ce0903f7 Fix #2010 : Authoring data class throws error in BIM tool if no ifc file is selected 2022-02-01 09:12:42 +01:00
Dion Moult a2c0045a40 Support relative object placements for IFC2X3 ports. 2022-02-01 18:23:55 +11:00
Dion Moult 3e10f52965 Relative placements for nested elements are now supported. 2022-02-01 17:39:55 +11:00
Dion Moult fd8aa6d142 IfcOpenShell API now supports port connections and disconnections. 2022-02-01 16:51:42 +11:00
Dion Moult ddb38b2ea0 The IfcOpenShell API now supports adding a distribution port. 2022-01-31 18:28:31 +11:00
Dion Moult 74174b1e09 Fix bug where drawing objects could be added to the wrong context. 2022-01-30 15:09:54 +11:00
Dion Moult 96179db7ad IFC element names no longer treat the Blender name suffix as significant 2022-01-30 15:08:32 +11:00
Dion Moult 116252f857 Object names now guard against duplicate IFC prefixes when assigning. 2022-01-30 15:06:01 +11:00
Dion Moult 538024e035 All element's spatial containment collections are now synced when assigning a class. 2022-01-30 15:04:50 +11:00
Dion Moult e9670a6906 Fix bug where editing then switching to the current representation crashes. 2022-01-30 14:47:22 +11:00
Dion Moult c7428dd160 Fix bug where syncing an object removed from the collection tree breaks. 2022-01-30 14:44:21 +11:00
Dion Moult bf9d46b506 Structural items in the collection hierarchy are now auto synced. 2022-01-30 10:11:19 +11:00
Dion Moult 7ee589cfc2 Remove legacy unassign class feature in favour of just deleting objects. Thanks theoryshaw! 2022-01-30 08:56:43 +11:00
Dion Moult 3083b66f61 Fix #2012. Minor fix. 2022-01-30 08:49:46 +11:00
Dion Moult 074fe0d3a6 You can now specify a target view when creating a new drawing. See #1153. 2022-01-29 21:11:35 +11:00
Dion Moult 46e1a46dd2 Drawing collection hierarchy is now auto fixed if shuffled around manually. See #1153. 2022-01-29 21:11:35 +11:00
Thomas Krijnen 677a9d715c #2009 2022-01-29 09:43:33 +01:00
Dion Moult a210cf654e Drawings list now reads data from IFC, not Blender. See #1153. 2022-01-29 17:26:09 +11:00
Dion Moult 44e4ee70ae Removing sheets now works in IFC. See #1153. 2022-01-29 15:45:45 +11:00
Dion Moult 27e6d702e9 You can now create drawing sheets in IFC. See #1153. 2022-01-29 13:42:46 +11:00
Dion Moult fb76f985c7 Minor fix 2022-01-29 09:01:35 +11:00
Dion Moult 9279aeccf6 You can now show and hide distribution ports of an element. 2022-01-29 08:52:37 +11:00
Dion Moult 92a1739d93 Minor fix - sorry, really need a local Sphinx setup 2022-01-28 20:41:20 +11:00
Dion Moult dfba9827a6 Minor fix 2022-01-28 20:37:18 +11:00
Dion Moult 19e007a67c New diagrams on the developer guide. 2022-01-28 20:32:41 +11:00
Dion Moult d3ef3f1501 Finish building minimum viable product of the IFC2P6 converter. Thanks myoualid! 2022-01-28 18:14:16 +11:00
Dion Moult f2e9f76631 Minor tweaks. See #2006. 2022-01-28 10:48:22 +11:00
Dion Moult 52739418d8 Fix #1514. You can now customise the default names of occurrences create from a type. 2022-01-27 17:21:11 +11:00
Dion Moult 7f1fddf426 New software architecture section in the developer guide. 2022-01-27 09:32:24 +11:00
Dion Moult ee4f48b566 Minor fix 2022-01-26 21:35:27 +11:00
Dion Moult 1d47ad3cd4 New demo module to teach developers how to hack on the BlenderBIM Add-on. 2022-01-26 21:27:59 +11:00
Dion Moult 36a7985f5a Fix #2002. Fix bug where sometimes shape IDs wouldn't be pure integers. 2022-01-25 08:59:01 +01:00
Dion Moult e129f4afa9 Fix #2001. Fix bug where you could not undo deletions. 2022-01-25 17:27:42 +11:00
Dion Moult 8fd4351195 Minor fix. Don't know all the answers. 2022-01-25 16:50:36 +11:00
Dion Moult 01f25528e8 Build untested IFC to P6 XML export feature. 2022-01-25 16:44:37 +11:00
Dion Moult 3222c9ce99 Fix #1998. Fix bug where colours don't show if styling is applied to the mapped item instead of the actual representation item. 2022-01-25 13:40:52 +11:00
Gorgious 28e6bb9e3d Fix error when updating representation on object data with no BIMMeshProperties 2022-01-24 13:22:17 +01:00
Dion Moult 5884af0678 Updates to space boundary geometry now auto updates on export. 2022-01-24 21:52:51 +11:00
Dion Moult f88a1038a8 IfcCSV now loads the selector by default so its much more fun to use a lib. 2022-01-24 17:52:32 +11:00
Dion Moult cafe8ce5f2 You can now remove bricks from a Brickschema model. 2022-01-24 17:52:04 +11:00
Dion Moult 8569bcf350 New utility function for getting ports of an element 2022-01-24 16:16:08 +11:00
Dion Moult e38759c39d Implement assign and unassign ports in IfcOpenShell API 2022-01-24 16:13:55 +11:00
Dion Moult 50ed8eccfd Fix bug where you couldn't select properties with a dot in the name for IfcCSV 2022-01-24 11:07:01 +11:00
Gorgious56 d63d62ddd6 Improve section cutaway shader (#2000)
* Dynamically update section shader backface color

* Refactor add_section_plane operator

* Prevent error if last section has been removed

* Refactor section plane remover
No longer removes object if it is not a section plane
Offsets nodes so that they are always nice and ordered

* Fix shader glitch when user deletes last cutaway object

* Fix nodes offset when deleting cutaway object
2022-01-24 09:47:22 +11:00
Dion Moult 0bdb4b9fcf Smart annotation variables for drawings now reimplemented. See #1153. 2022-01-22 21:00:26 +11:00
aothms 7e7c54a535 #1993 vs2022 hdf5 2022-01-21 12:13:58 +00:00
Dion Moult 817fdead9b Fix bug where zero durations were not allowed in task times. 2022-01-21 18:52:40 +11:00
Dion Moult 68a43b92ee Editing task times no longer tries to be clever with actual dates because you know what you're doing. 2022-01-21 18:42:59 +11:00
Dion Moult 595c72e08d Fix #1995. Bug where section cutaways didn't work in Blender 3.0. 2022-01-21 14:22:07 +11:00
Dion Moult 5709efbdcb Fix bug where you could not add properties to projects themselves. 2022-01-21 12:01:19 +11:00
Dion Moult 67952ed159 Fix #1576. Managing IfcSystem subtypes is now possible. 2022-01-20 18:22:20 +11:00
Thomas Krijnen 000e3acb2b #1948 clear tmp list 2022-01-19 15:36:58 +01:00
Thomas Krijnen b337d5a174 #1993 msvc 2022 config 2022-01-19 14:14:15 +01:00
Dion Moult 6b0bdc564d Nicer looking gantt charts and make it easier to print to PDF. 2022-01-19 17:38:42 +11:00
Dion Moult ab5db2fa23 Minor fix 2022-01-19 12:19:36 +11:00
Dion Moult 810c71dfa2 IfcOpenShell-python licensing and black. See #1082. 2022-01-19 12:18:33 +11:00
Dion Moult 5b281260f8 Licensing and black for BIMTester and IfcBlender. See #1082. 2022-01-19 11:07:43 +11:00
Dion Moult 07264dafb1 Bump IfcOpenShell to latest bot build. See #1984 #1977 #1959 2022-01-18 18:01:27 +11:00
Dion Moult 9572fef222 Fix #1908. Extracting elements using IfcPatch now includes all properties. 2022-01-18 17:47:59 +11:00
Dion Moult 1bbbc82b72 Fix #1962. You can now easily append product assets using the API. 2022-01-18 17:44:50 +11:00
Dion Moult aa6d03ade2 You can now reset and restore your API owner settings. 2022-01-18 17:42:17 +11:00
Thomas Krijnen 4c40fc1629 #1985 consolidate and correct shell count checks in tree 2022-01-17 13:56:21 +01:00
Thomas Krijnen 90e5b5576d Fix propagation of kernel settings (affects multi threading) 2022-01-17 13:55:40 +01:00
Thomas Krijnen 531805fefb #1692 get_info_2() logical 2022-01-17 11:19:30 +01:00
Dion Moult 2852f53afa Fix #1934. Fix bug with improper nesting of subfolders when exporting IfcZip. 2022-01-17 20:30:02 +11:00
Dion Moult 8b06aa9e0b Header data previews for IFCZIP are now supported as well. 2022-01-17 18:21:20 +11:00
Dion Moult 52ea69bcec Fix #1954. Fix bug where importing MSProject XMLs with no project names failed. 2022-01-17 17:37:09 +11:00
Dion Moult 9ebe0aaa95 Fix #1956. One-button upgrade from IFC2X3 to IFC4. 2022-01-17 16:20:01 +11:00
Dion Moult 1a735c4bef Minor improvement to the sequence UI to always allow you to cancel editing mode. 2022-01-17 15:11:48 +11:00
Thomas Krijnen 482788a46f Update build-all.py 2022-01-16 13:59:19 +01:00
Thomas Krijnen a30a8cd920 Missing job dependency in bbim ci 2022-01-16 13:43:14 +01:00
Thomas Krijnen c2e7c27421 ADD_COMMIT_SHA in build scripts 2022-01-16 13:16:44 +01:00
Thomas Krijnen 2f24343f8f Update and rename README.txt to README.md 2022-01-14 11:14:42 +01:00
Dion Moult d259e0f490 Fix IfcCSV import bug. 2022-01-14 20:25:35 +11:00
Dion Moult c09ed228c0 Whoops. 2022-01-14 19:06:17 +11:00
Dion Moult d63be20c8b Fix #1989. Fix bug where you couldn't export to IfcJSON, since it couldn't be imported again. 2022-01-14 18:55:44 +11:00
Dion Moult f8c7f48674 You can now associate annotations with objects for smart annotations in IFC. See #1153. 2022-01-14 18:00:08 +11:00
Dion Moult 258ca7fb8c Fix bug where you could assign a product twice to a process. 2022-01-14 16:04:55 +11:00
Dion Moult ec38fe2edc Fix #1931. Fix bug where duplicated type products should not share material associations. 2022-01-14 13:15:47 +11:00
Dion Moult 5a87ab8171 Minor cleanup to Brick code after failed attempt to detect feed relationships. 2022-01-14 11:47:58 +11:00
Dion Moult d23d382854 Minor fix to BIMTester IDS support. Disable IDS test due to unstable nature of IDS. 2022-01-14 11:46:15 +11:00
Dion Moult 30270db0c3 Fix bug where annotation decorations didn't show correctly in Blender 3.0 2022-01-14 10:54:27 +11:00
Vukas Pajic 6d78ac4918 adjusted UI based on feedback received (#1986)
* adjusted UI based on feedback received

* typo
2022-01-14 10:09:51 +11:00
Benjamin Dahl 81016f3110 define the vector sass [0] as a std::vector<std::unique_ptr<ShapeAnalysis_Surface>>
https: //github.com/IfcOpenShell/IfcOpenShell/issues/1975
Co-Authored-By: Dion Moult <dion@thinkmoult.com>
Co-Authored-By: Thomas Krijnen <1096535+aothms@users.noreply.github.com>
2022-01-13 13:38:51 +01:00
Dion Moult 189e566740 Remove broken and obsolete Blender clasher which is incompatible with the geom.tree broadphase approach. 2022-01-13 17:05:44 +11:00
Thomas Krijnen 5f7e4793c2 #1960 correct distinguish outer bound by pre increment 2022-01-12 15:14:18 +01:00
Thomas Krijnen be286c4d9d #1974 populate attribute map upon schema registration 2022-01-12 15:09:22 +01:00
Dion Moult 5ba542488a Implement adding plain bricks not based on IFC 2022-01-12 16:22:32 +11:00
Dion Moult 39960998de Support type-based class checking for converting IFC2X3 models to Brickschema 2022-01-12 15:39:43 +11:00
Dion Moult 92642e6a6a The IFC Class panel now shows inherited predefined types 2022-01-12 14:29:02 +11:00
Dion Moult 78d33437ea New utility to get predefined type 2022-01-12 14:11:31 +11:00
Gorgious56 65b3960ab3 Show ifc specs in file selector (#1980)
* Create simple parser for ifc header specs

* Refactor logic to be usable with other file selectors

* Fix parsing error
2022-01-12 10:01:49 +11:00
Vukas Pajic e81848dfba UI Simplification with Panel Groups (#1979)
* concept demo

* small naming change + added missing ui classes

* added newline to end of file
2022-01-11 20:03:29 +11:00
Dion Moult fe9cda8ba1 Fix #1925. Fix bug where nested collection deletions were not synchronised properly. 2022-01-11 15:45:29 +11:00
Dion Moult 49221739cf Implement auto and manual refresh of the Brick model navigator 2022-01-11 14:46:58 +11:00
Dion Moult c6966f9b39 You can now create a new blank Brick project 2022-01-11 13:25:03 +11:00
Dion Moult f08ed629b5 New debug feature to purge HDF5 cache in case the cache is corrupted. 2022-01-11 11:24:13 +11:00
Dion Moult ba675c0d28 Implement human readable rdfs labels in Brickschema navigator 2022-01-11 10:44:52 +11:00
Gorgious56 3a80722621 Display IFC schema in file selector right hand side panel (#1978)
* Display IFC schema in file selector right hand side panel

* Formatting

* Fix possible error when the file name or description contains the word "schema"
2022-01-11 08:52:14 +11:00
Dion Moult 9b77bb6e37 New utility to get IFC all elements that are relevant in Brickschema 2022-01-10 17:31:42 +11:00
Dion Moult 7a6df49348 Basic implementation to bulk convert relevant IFC classes to a Brick model 2022-01-10 17:30:49 +11:00
Dion Moult 666e484b2b Run black on IfcOpenShell-python. 2022-01-10 15:42:24 +11:00
Dion Moult 256f40ed44 New IFC to Brick class mapping utility 2022-01-10 15:35:37 +11:00
Dion Moult 8eccb1a5ed You can now select all system objects from the object panel for convenience. 2022-01-10 12:21:38 +11:00
Dion Moult ce3490824d Optimise all editing API functions to be 100x faster when you're dealing with proprietary BIM exports with loads of shared owner histories. See #1904 and #1905. 2022-01-10 12:12:34 +11:00
Dion Moult 43c74010a5 Schema versions are no longer locked to IFC4 and 2X3. You can now load express schemas at run time. 2022-01-10 10:45:46 +11:00
Dion Moult 6c991ed356 Purge obsolete IFC types mappings 2022-01-10 09:40:42 +11:00
Thomas Krijnen c01c2a737a test runner doesn't have dae 2022-01-09 21:26:40 +01:00
Thomas Krijnen c9c11a2905 Install to system local root 2022-01-09 21:11:47 +01:00
Thomas Krijnen e5037dc41a #1960 test cases 2022-01-09 20:52:48 +01:00
Thomas Krijnen acd9250028 Also log perf with quiet 2022-01-09 15:21:16 +01:00
Thomas Krijnen a3dc477921 Fix perf logging in convert 2022-01-09 15:15:50 +01:00
Thomas Krijnen 6246dda8d6 #1960 make configurable 2022-01-09 15:15:29 +01:00
Thomas Krijnen 2683bcba20 Fix: shift count negative or too big 2022-01-09 15:14:13 +01:00
Thomas Krijnen 97f2aa976e Merge branch 'v0.7.0' of https://github.com/IfcOpenShell/IfcOpenShell into v0.7.0 2022-01-09 14:32:23 +01:00
Thomas Krijnen 9b96073ac0 #1960 BRepBuilderAPI_MakeFace on non-overlapping 2d subtraction 2022-01-09 14:32:13 +01:00
Thomas Krijnen 7efd9cea2a Log performance aggregates as logger messages 2022-01-09 14:31:13 +01:00
Thomas Krijnen c917881a8e Logging statement for total file conversion 2022-01-09 14:30:56 +01:00
Thomas Krijnen fa46eb140f Update template.py main.schema_identifier doesn't exist 2022-01-09 11:41:24 +01:00
Thomas Krijnen b8ea42540e #1973 Performance logging 2022-01-07 15:12:32 +01:00
Thomas Krijnen 671ab78c76 #1969 take uppercase in register_schema() 2022-01-07 12:49:28 +01:00
jakob-beetz a8550cea26 build-deps.cmd typo 0.7.0 branch 2022-01-07 12:47:28 +01:00
Dion Moult 4f047274bf The IfcOpenShell API now supports automatic primitive casting for editing properties. See #1943. 2022-01-07 17:38:43 +11:00
Dion Moult eb04a7d579 Visual clean up of bulk property editing UI 2022-01-07 17:38:43 +11:00
Dion Moult 84096529e1 Primary measure type lists are now dynamically generated based on the schema. 2022-01-07 17:38:43 +11:00
Dion Moult 3350f31312 Write documentation to help developers get acquainted with testing 2022-01-07 17:38:43 +11:00
Dion Moult 0d79963ed8 Clear outdated README text which we won't maintain to prevent confusion 2022-01-07 17:38:43 +11:00
Dion Moult 43a8fa8bcb It's 2022! 2022-01-07 17:38:43 +11:00
Thomas Krijnen ca8f3cf1e6 Update README.md 2022-01-06 10:17:56 +01:00
garylzimmer 313a1c5705 Update README.md
Updated based on my personal findings. Can provide documentation if necessary.
2022-01-06 10:17:31 +01:00
Vukas Pajic 53f6454784 Fix integers in enumerations #1943 (#1946)
* first draft

* small change
2022-01-04 13:10:42 +11:00
Vukas Pajic 2c0df1d4f5 fix umlauts not showing up correctly in dynamic enums #1941 (#1945)
* first attempt

* adopted new solution for umlauts in enums

* fix typo
2022-01-04 13:09:47 +11:00
molitoris e384dbd548 Correct HeaderFile (#1942)
Rename file_name to filename (without underline)
2022-01-04 13:06:01 +11:00
Cyril Waechter d22a0c7cb8 Boundary geom creation : fix math + small ui fix (#1938)
_Neo accidentally took the red pill so I transposed the matrix to make
it blue_

* Fix how placement matrix of updated geometry is created.
* Fix ui: was throwing an error when boundary had no
`RelatedBuildingElement`
2022-01-04 13:04:06 +11:00
Gorgious56 02a62a943c Fix #1717 : Disable task editing if task was deleted as a result of deleting its parent (#1936)
* Disable task editing if task was deleted as a result of deleting its parent

* Optimize logic for bigger number of tasks

* Don't try to disable editing if not editing in the first place
2022-01-04 13:03:39 +11:00
Vukas Pajic 0625e4153c bulk property editor (#1896)
* first revision

* update incorporating review comments

* added a new feature - remove psets

* type dropdown now references ifc data types

* adopt new solution for enumerations and umlauts

* Revert "adopt new solution for enumerations and umlauts"

This reverts commit 203a31a2bf.
2022-01-04 12:59:55 +11:00
Jesusbill 03f5feca88 refactor structural module - wip - structural analysis models #1848 2022-01-02 00:34:38 +01:00
ssg 85e3c6588f Updated README.md for OCCT/IfcOpenShell7
After #1930 the 0.7 branch isn't compatible anymore with OCCT version < 7.2. That includes the liboce that's found in debian (derivatives), which is OCCT 6.9 or even less. Updated the Linux section of README.md to explain the dependency on OCCT and include instructions to build on Unix with OCCT (from the OS repository as well). Also included changes that became necessary when we moved to v0.7.0 from v0.6.0 (like recursive clone and cmake additions).
2021-12-31 13:46:13 +01:00
bosonprojets 6e913a0ff9 BlenderBIM Feature - Filter By Building Storey 2021-12-28 23:46:22 +01:00
Thomas Krijnen ff4e58a8ff Update README.md 2021-12-20 11:46:07 +01:00
Dion Moult 1db028fa7b Fix bug where IfcPatch IFC4 migration could break on Revit files 2021-12-20 16:39:14 +11:00
Thomas Krijnen 69a7efdab4 Does this make gcc happy for uint64_t enums? 2021-12-19 22:31:40 +01:00
Thomas Krijnen 20dcb68009 Invalidate the boolean result for a particular warning reported by pavefiller 2021-12-19 22:14:09 +01:00
Thomas Krijnen 6b0b64b869 Good news everyone! Option to process wall opening subtractions in 2D. 2021-12-19 22:08:16 +01:00
Thomas Krijnen 09631e9565 Remove some erroneous or wrong (hopefully) lines on mintol and prec 2021-12-19 22:01:07 +01:00
Thomas Krijnen e9bb70f56b Eliminate boolean sub operands b that only touch operand a 2021-12-19 21:59:29 +01:00
Thomas Krijnen bd008bdefe option to write boolean operands to disk for debugging 2021-12-19 21:57:47 +01:00
Thomas Krijnen 03d4782cb7 Ground work for processing opening placement up to parent wall 2021-12-19 21:55:41 +01:00
Thomas Krijnen 49c555c508 Log BRepCheck_Analyser results 2021-12-18 22:02:47 +01:00
Thomas Krijnen c51c5651b2 Small changes to iterator settings 2021-12-18 16:16:44 +01:00
Thomas Krijnen 364c2286d1 #1937 use git branch --contains instead show 2021-12-18 16:16:21 +01:00
Jesusbill 37d20eb360 changes in ifc2ca standard case 2021-12-17 17:01:18 +01:00
Jesusbill dd6912bdaa ifc2ca - possibility to exclude zero length springs in point connections 2021-12-17 16:27:41 +01:00
Jesusbill a1fe68a17e changes in ifc2ca bonded case 2021-12-17 09:58:02 +01:00
Thomas Krijnen 3ec79cac3f #1914 only proceed with 'face addition check' when first operand contains open shells 2021-12-15 13:37:12 +01:00
Thomas Krijnen dbc6f521fe #1912 apply also on types 2021-12-14 09:57:31 +01:00
Thomas Krijnen c52f851d79 #1807 method mapping proposal 2021-12-14 09:57:31 +01:00
Thomas Krijnen 3380158f55 #1692 memory leak in get_info_2() 2021-12-13 10:32:05 +01:00
Jesusbill 4e9a5f246f minor changes in ifc2ca scripts 2021-12-12 19:03:14 +01:00
Dion Moult 664c49a07d Bump IfcOpenShell. See #1906. 2021-12-11 21:10:03 +11:00
Thomas Krijnen 06b9cc6b38 CMakeLists ADD_COMMIT_SHA option 2021-12-10 10:03:41 +01:00
fichter b9ea741e5e OCC7.5 to OCC7.6 -> depracated Poly_Triangulation Nodes() 2021-12-10 08:44:42 +01:00
Thomas Krijnen 0098e38c77 Switch to libocct for yml workflows: add data-exchange libs 2021-12-10 08:08:03 +01:00
Dion Moult ece796fc2d Fix #1917. Fix warning where the relating type classes data couldn't be accessed. 2021-12-10 14:06:54 +11:00
Dion Moult de4659570a See #1917. Refactor and fix warning message when you haven't got any contexts in a project yet. 2021-12-10 13:31:23 +11:00
Dion Moult 62236c44ae Fix #1923. Fix bug where creating a new structural analysis model broke. 2021-12-10 10:58:06 +11:00
Dion Moult ce5db914db Fix #1913. Fix bug where filtering by decomposition didn't work on IFC2X3. 2021-12-10 10:14:43 +11:00
Dion Moult 12699f29b7 Minor fix 2021-12-10 09:44:23 +11:00
Dion Moult 54e326983f Fix #1921. Document issue with installing on outdated Ubuntu distributions. 2021-12-10 09:43:28 +11:00
Thomas Krijnen 5fb40a390c Switch to libocct for yml workflows: update include dir 2021-12-09 20:52:30 +01:00
Thomas Krijnen 0cb8716c6a Switch to libocct for yml workflows 2021-12-09 20:47:00 +01:00
Thomas Krijnen 099013fa05 revert #1927 for mpir 2021-12-09 20:27:55 +01:00
Ttofuuu 4a8843b6bb fix mpfr checking problem
https://github.com/BrianGladman/mpir.git 's mpfr will lead to a merge problem and make IFCOS fail to build depends on Windows. Change the link to anothms will fix this problem.
2021-12-09 20:26:19 +01:00
Thomas Krijnen 33d15b42b7 bop face addition check only for subtraction 2021-12-09 20:24:21 +01:00
Gorgious56 44581c77ac Extend ifc debug panel (#1924)
* Prevent searching for invalid ID in IFC debugger
You can still input <= 0 IDs for debugging purposes
But the field won't let you drag below ID = 1
Also changed default to 1 so it doesn't throw an error by default

* Extend ops descriptions, labels and debug output

* Use Operator property for selecting dense meshes

* Prevent error if id has no geom representation

* Use Poll operator and poll fail message

* Debug op to select highest meshes by polygon percentile
2021-12-09 13:41:15 +01:00
Bernd Hahnebach e848b249a6 bimtester: if lang argument is not set use feature file lang for output messages, improves #1910 2021-12-09 12:04:47 +01:00
Bernd Hahnebach f85c3ae24c bimtester: update log and smartview, they still are deactivated by default, fixes #1892 2021-12-09 09:06:51 +01:00
Dion Moult 7c633fab31 Fix bug where libraries didn't work in IFC2X3 2021-12-09 17:51:29 +11:00
Dion Moult f716f8a357 You can now add feed relationships in a Brick model from IFC elements 2021-12-09 17:50:57 +11:00
Dion Moult d156927e13 From an IFC element, you can now add a new Brick element in a Brick model 2021-12-09 17:48:47 +11:00
Dion Moult 2ef1f4d812 Assigning a brick reference now adds a 2-way IFC reference in the Brick model 2021-12-09 17:45:08 +11:00
Dion Moult 78a36ab429 Fix #1816. Fix bug where cameras weren't allowed to be optional in BCF 2.1. 2021-12-09 13:51:55 +11:00
Dion Moult 763a38bc9c Fix bug with missing system arrows in visualising offset models and sink/source mixed flows. 2021-12-09 13:40:23 +11:00
Dion Moult a23c88a53e Minor fix for #1902. 2021-12-09 13:38:46 +11:00
Dion Moult f6bf626349 Little utility to help printing out maintainable classes and other classes in contractual documentation. 2021-12-09 13:36:33 +11:00
Bernd Hahnebach b66792f981 bimtester: get lang from json report and use it to generate html report, fixes #1928 2021-12-08 15:53:34 +01:00
Bernd Hahnebach 195af0c850 bimtester: do not break bimtester if translation files are missing, fixes #1910 2021-12-08 15:19:36 +01:00
Bernd Hahnebach ee56e06d86 bimtester: German eleclass attributes steps, fix translation redirection 2021-12-08 08:42:21 +01:00
Bernd Hahnebach 146dd1e74d bimtester: German geometrirc detail step, fix quotation marks 2021-12-08 08:23:00 +01:00
Bernd Hahnebach 6d47cf3a51 bimtester, attributes ele class steps, use generic assert methond 2021-12-07 18:01:10 +01:00
Bernd Hahnebach 9b6d53fe38 bimtester: example steps, delete more steps which are implemented already 2021-12-07 15:51:31 +01:00
Bernd Hahnebach 6902e2e2c6 bimtester: features, move geometric detail tests from examples to implemented steps 2021-12-07 15:47:15 +01:00
Bernd Hahnebach 82d136b9b3 bimtester: util, improve generic assert method 2021-12-07 14:58:18 +01:00
Bernd Hahnebach b8dd0450d1 bimtester: features, fixes in element class attributes 2021-12-07 14:47:13 +01:00
Bernd Hahnebach 85d76c084b bimstester: features, move element class attributes tests from examples to implemented steps 2021-12-07 12:59:50 +01:00
Bernd Hahnebach c658f7610e bimstester: features, move spatial structure tests from examples to implemented steps 2021-12-07 12:16:18 +01:00
Bernd Hahnebach 3fad28572b bimstester: features, move application tests from examples to implemented steps 2021-12-07 11:44:21 +01:00
Bernd Hahnebach 455fe80497 bimtester: features import module, sort by abc 2021-12-07 11:27:42 +01:00
Bernd Hahnebach 3f46d54ad9 bimtester: features, move qset test from example steps to implemented steps 2021-12-07 09:38:22 +01:00
Bernd Hahnebach 89e3276c90 bimtester: example steps, delete the ones which are implemented already 2021-12-07 08:47:31 +01:00
aothms bb9eba07c0 build-all.py python library proposal #1884 2021-12-03 10:21:55 +01:00
aothms 6d4573fb58 build-all.py option PYTHON_USER_SITE 2021-12-03 10:21:55 +01:00
aothms 24890dd001 build-all.py option NO_CLEAN 2021-12-03 10:21:55 +01:00
Dion Moult 65d599bbd9 Fix bug where Blender name wouldn't match IFC Name when reassigning class. See #1902. 2021-12-03 11:00:54 +11:00
Dion Moult ebe5460a9f Fix segfault thanks to aothms. 2021-12-02 09:28:04 +01:00
Dion Moult 01bc29c5b2 Purge by_ref_cached_ 2021-12-02 09:28:04 +01:00
Dion Moult 5ad6d455f9 get_total_inverses now efficiently gets the full number of references to an entity 2021-12-02 09:28:04 +01:00
Dion Moult cef1c86f6d Experimental get_inverse_cardinality proposal. See #1904. 2021-12-02 09:28:04 +01:00
Cyril Waechter d1caa3242f Refactor to usecase as asked in #1881 (#1907) 2021-11-30 21:24:19 +11:00
Dion Moult 4f7bfec026 Merge branch 'v0.7.0' of github.com:IfcOpenShell/IfcOpenShell into v0.7.0 2021-11-30 21:23:16 +11:00
Dion Moult df6ad70345 Fix percentage loading implementation with the new more accurate progress number. 2021-11-30 21:23:06 +11:00
Bernd Hahnebach a7a3ccb23a bimtester: gui, initialize ifc file and feature file text 2021-11-30 10:30:41 +01:00
Cyril Waechter b65f2ca062 Allow to assign IfcRelSpaceBoundary to an object (#1902) 2021-11-30 13:35:55 +11:00
Cyril Waechter f991b053d3 Allow to manually update boundary connection geometry (#1899)
* Allow to manually update boundary geometry

Following have been created in this purpose:
* usecase in ifcopenshell.api
* blenderbim operator
* button in blenderbim manually trigger it

* Assign kwargs to object instead of settings dict

* Add forgotten helper file
2021-11-30 13:29:22 +11:00
Cyril Waechter 47a25ca8b9 Prevent crash on export for obj with no material (#1903)
During export material_checksum was always checked against a stringed
list `'[]'` for an empty material while default value
for material_checksum default was `''`. So it was trying to update
material of object which have none (eg. IfcRelSpaceBoundary).
See: https://github.com/IfcOpenShell/IfcOpenShell/blob/a3f27830c579e567218f79faa52dfc941a441241/src/blenderbim/blenderbim/bim/export_ifc.py#L147
2021-11-30 13:27:08 +11:00
Bernd Hahnebach a3f27830c5 bimtester: gui, improve prints and error handling 2021-11-29 14:59:30 +01:00
Bernd Hahnebach 1a5b5dd303 bimtester: fix gui, works with feature file now instead of feature directory 2021-11-29 10:28:49 +01:00
Dion Moult f529bc37ee Fix bug where selecting type occurrences did not work 2021-11-29 20:25:51 +11:00
Dion Moult 825ffe03b0 Converting brickschema projects to IFC libraries now works on IFC2X3 2021-11-29 20:19:58 +11:00
Dion Moult 622ff05247 Fix bug where updating owner history may not work if you were using it outside Blender. 2021-11-29 20:17:02 +11:00
Dion Moult d638405ff8 Assigning library references now works on IFC2X3. 2021-11-29 20:16:25 +11:00
Dion Moult c0f547c6e4 Test expectations for new remove_deep2 proposal. See #1812. 2021-11-29 20:14:50 +11:00
Dion Moult 3accc1dbd6 Editing object placements is now 3x faster on large files. See #1807. 2021-11-29 19:58:03 +11:00
aothms 6eab9fb593 #1785 HdfSerializer read-only option 2021-11-28 19:34:54 +00:00
Felipe Mesquita 2708133493 Added information for debugging level parameter 2021-11-27 08:53:17 +01:00
Thomas Krijnen c5e6c3e2c7 #1897 handle nesting in get_decomposing_entity_impl() 2021-11-26 14:15:51 +01:00
Thomas Krijnen d7b27c0620 ifcopenshell.ifcopenshell_wrapper.turn_on_detailed_logging() 2021-11-25 15:23:14 +01:00
Thomas Krijnen 75ffc4454d #1895 don't signal element ready when result is empty 2021-11-25 12:05:39 +01:00
Dion Moult a1817e8a61 Fix #1889. Allow explicitly overriding data types when editing psets. 2021-11-24 16:38:19 +11:00
Bernd Hahnebach b4b334145f BIMTester: steps, improve imports in the regard of rebases 2021-11-23 15:18:01 +01:00
Bernd Hahnebach 04535d27ec Bimtester, improve German attribute feature translation, get rid of umlaut since it makes problems in some circumstances 2021-11-21 21:19:10 +01:00
Thomas Krijnen 3c41f6b0f8 #1153 draw.py timing 2021-11-20 14:05:53 +01:00
Thomas Krijnen a17c44c076 #1153 update draw.py with two new options 2021-11-20 14:05:11 +01:00
Thomas Krijnen eccf72ee8a #1153 option to preprocess shape into sets of profile edges 2021-11-20 14:02:12 +01:00
Thomas Krijnen 9c9bec9c4e Some more things related to #1853 2021-11-19 15:08:24 +01:00
Thomas Krijnen 71c1cd6a9e Check for null result and handle some bop alerts 2021-11-19 10:39:14 +01:00
Thomas Krijnen 0504556e7a #1885 consider schema type when copying attribute values 2021-11-19 09:38:28 +01:00
Thomas Krijnen e9a18d030e Small cleanup and try to please gcc 2021-11-19 09:22:09 +01:00
Thomas Krijnen 6d725f60fa finish up 2021-11-19 09:22:09 +01:00
Thomas Krijnen 72206bc441 async init, crashes now on get() 2021-11-19 09:22:09 +01:00
Dion Moult c0eb9addfd Fix bug in FM utility for grabbing classes from the draft FMHEM standard 2021-11-19 19:13:21 +11:00
Dion Moult ff3a98234d Fix bug in convert length unit recipe where IFC2X3 ownership history fails. 2021-11-19 19:12:55 +11:00
Dion Moult ebf6b9fc88 Fix #1886. 2021-11-19 19:08:34 +11:00
Cyril Waechter c2e4b5f1fa [BlenderBIM] Boundaries: add relations editing (#1881)
* SelectGlobalId: set object as active

Use tool module instead of previous custom search loop.

* Boundaries: add relations editing

For entity of class IfcRelSpaceBoundary :
* Display relationship attributes
* Add button to jump to related objects
* Allow to edit relations attributes including entity filter.
UI similar to current base attributes editing.
2021-11-19 10:06:22 +11:00
Bernd Hahnebach 9728c4d345 bimtester: features psets, use quots the same way as in all other features 2021-11-18 18:16:37 +01:00
Gorgious f0d6500539 Merge branch 'v0.7.0' of https://github.com/IfcOpenShell/IfcOpenShell into v0.7.0 2021-11-18 10:38:27 +01:00
Gorgious 124ccf0404 (slightly) Optimise batch removing objects 2021-11-18 10:38:17 +01:00
Bernd Hahnebach df03f7ad29 bimtester: steps, fix undefined name 2021-11-18 10:20:50 +01:00
Bernd Hahnebach ff8fc898a1 bimtester: examples, trailing ws 2021-11-18 09:53:53 +01:00
Bernd Hahnebach 2d62d8181a bimtester: improve French translation for the provided example 2021-11-17 16:08:42 +01:00
Bernd Hahnebach 6853735bb2 bimtester: fix example feature files to work with this bimtester 2021-11-17 10:36:37 +01:00
Bernd Hahnebach 1c3980b6f0 bimtester: fix italian and dutch project setup step files 2021-11-17 10:36:37 +01:00
Dion Moult 5c224150ec More slow progress in getting annotations round-tripping. See #1153. 2021-11-17 18:25:01 +11:00
Dion Moult 18719c16df Fix bug where subelement coordinates were incorrectly calculated in non SI units 2021-11-17 16:22:23 +11:00
Dion Moult ddb8abff0f Minor fix 2021-11-17 15:32:26 +11:00
Dion Moult f9221717ff You can now associate IFC elements with Brick elements and navigate to them 2021-11-17 13:27:07 +11:00
Dion Moult da6945b352 You can now assign and unassign library references 2021-11-17 11:43:53 +11:00
Dion Moult 62cdf76a5a Fix bug where you couldn't copy quantities to other elements 2021-11-17 10:35:46 +11:00
Dion Moult fae607f8ce New UI to add / edit / remove library information and references. 2021-11-16 22:15:54 +11:00
Dion Moult 468192e849 Fix bug where unlinking a style that was invalidly linked didn't work. 2021-11-16 22:08:55 +11:00
Dion Moult edd01637e2 Minor fix 2021-11-16 21:51:23 +11:00
Bruno Postle 604fbc4bc9 Raise default section height from storey from 1.0 to 1.2 2021-11-16 09:12:42 +01:00
Dion Moult 4617d9f234 A whole bunch of new usecases for managing IFC library information and references 2021-11-16 18:30:23 +11:00
Dion Moult 41486ba08a New utility to get layers of an element. See #1869. 2021-11-16 18:07:47 +11:00
Dion Moult 2120653104 Minor fix. 2021-11-16 17:52:08 +11:00
Dion Moult 43869c33f6 Rebuild text annotations to have leaders in the same object and use text literals. See #1153. 2021-11-16 08:54:29 +11:00
Dion Moult fcfdc179df Viewport annotation decorations now scale based on paper size. See #1153. 2021-11-16 08:48:50 +11:00
Dion Moult 30c164d223 Fix minor error in console when changing drawings. See #1153. 2021-11-16 08:45:52 +11:00
Dion Moult b28d8b0cca Drawing annotations now are stored in IFC using ObjectType conventions. See #1153. 2021-11-16 08:35:49 +11:00
Dion Moult 4b5907f5dd Reimplement roundtrip of most annotation elements for drawings. See #1153. 2021-11-16 08:32:43 +11:00
Dion Moult 78a27ef44c New experimental OBJ2IFC script 2021-11-14 18:30:56 +11:00
Thomas Krijnen 528e27234c tree.protrusion_distances() 2021-11-13 15:54:42 +01:00
Dion Moult 8ac885ced1 Fix bug where annotative grids did not sync. 2021-11-12 15:16:10 +11:00
Dion Moult 63872abaef Purge old 2D element drawing system, so we don't get double the symbols on drawings. 2021-11-12 14:01:13 +11:00
Dion Moult 5ba155eb17 Fix #1857. Fix bug where deleting from the outliner was not tracked. 2021-11-11 22:27:40 +11:00
Dion Moult 2ec3c4c06b Swapping a Blender material for another is now auto synchronised. 2021-11-11 18:21:02 +11:00
Dion Moult 6f55cd5169 Optimise object placement change checking, so medium sized project exports are 5 seconds faster at minimum, but can be much higher since false positives are culled. 2021-11-11 17:31:23 +11:00
Dion Moult f5f88f4b97 Fix #1872. Bug where duplicating an object didn't duplicate its styles. 2021-11-11 17:31:23 +11:00
Dion Moult d5b92c0fea Fix bug where style colours were not auto synchronised. 2021-11-11 17:31:23 +11:00
Laurens Oostwegel 9355969110 Load CJIO on execution instead of initialisation 2021-11-10 20:08:18 +01:00
Dion Moult 6c998e0b8a Fix #1871. 2021-11-10 22:23:52 +11:00
Dion Moult 51f8d648ad Minor fix 2021-11-10 21:01:17 +11:00
Vukas Pajic a26539d6a5 show property description in pset_template ui 2021-11-10 10:18:55 +01:00
Dion Moult a9f27fcf17 Fix bug #1854. Materials are now auto replaced in Blender when you assign a new IFC material. 2021-11-10 18:04:54 +11:00
Dion Moult 28e5af853d Fix bug where an added default material was not linked and auto synced. 2021-11-10 18:01:10 +11:00
Dion Moult a7b3b5261f Fix bug in IFC2X3 where updating style colours didn't work. 2021-11-10 16:45:16 +11:00
Dion Moult 0dac56dbdf Fix bug where diffuse colour was not correctly extracted from nodes 2021-11-10 16:39:06 +11:00
Dion Moult 0992f43f11 Geometry caching is now enabled for model importing 2021-11-10 13:52:14 +11:00
Dion Moult c1c50f8868 Minor fix. 2021-11-10 12:46:07 +11:00
Dion Moult d733960068 Fix #1856. Bug where you lost your existing container or aggregation when you added a new aggregation assembly. 2021-11-09 20:50:31 +11:00
Dion Moult 66fa3f0096 Changes are now synchronised prior to switching representations. 2021-11-09 20:49:25 +11:00
Dion Moult cbf7b0a77a Minor clean up for CityJSON PR. 2021-11-09 20:46:46 +11:00
Dion Moult 91f2f9438f Fix #1764. Bug where OpenLCA UI would freeze if it couldn't connect to OpenLCA. 2021-11-09 20:43:30 +11:00
Dion Moult fcbb6e60ac Another Makefile fix :( 2021-11-09 10:05:06 +11:00
Dion Moult f33d613f9d Minor Makefile fix 2021-11-09 09:45:36 +11:00
Cyril Waechter 849dfbed28 [BlenderBIM] Tools to load IfcRelSpaceBoundary (#1864)
* Tools to load IfcRelSpaceBoundary

At scene level to load all.
At space level to load ones from a specific space or a specific one.

* Add a tool to colour boundaries

Colour is based on its related building element.
2021-11-09 09:28:44 +11:00
Dion Moult 8a4bc34fad Scale changes are now auto synchronised on export. 2021-11-09 09:22:03 +11:00
Laurens Oostwegel aa18c1b7d6 Small fix in Makefile error due to ifccityjson 2021-11-08 23:03:31 +01:00
Laurens Oostwegel ae45a9b9a5 Fix error in Makefile 2021-11-08 22:59:09 +01:00
ecke101 7aa75d98d3 Improve support for Powerproject import (#1865)
This is tested with swedish PowerProject 14.0 file
2021-11-09 08:52:24 +11:00
Laurens Oostwegel ff6db5f24d Integrate ifccityjson in BlenderBIM (#1859)
* ifccityjson-BB integration: include CJIO in makefile

* Add ifccityjson to BlenderBIM

* Make ifccityjson work like a package

* Use pip while installing CJIO dependency for ifccityjson

* revert removal of brick module

* Changes from pull request review

* Use ifccityjson as CLI and as package for BlenderBIM
2021-11-09 08:51:30 +11:00
Dion Moult de7c54b771 Fix #1855. Bug where authoring type dropdowns didn't refresh. 2021-11-08 21:12:38 +11:00
Dion Moult 7fbf317ad2 Fix representation where removing a representation left objects with an empty mesh. Now a proper empty is used. 2021-11-08 21:12:38 +11:00
Dion Moult 0471f17d9a Fix bug where removing a representation from a typed instance didn't remove all data users. 2021-11-08 21:12:38 +11:00
Dion Moult bf134eb0f4 Implement cache updating for drawings. See #1153. 2021-11-08 21:12:38 +11:00
Dion Moult 7ccb6ee110 Resolve #1836. Scaling in Blender is now auto resolved when representations are updated. 2021-11-08 21:12:38 +11:00
Dion Moult 9076043a5c Purge old dumb door and dumb window tool to prevent confusing users 2021-11-08 21:12:38 +11:00
Dion Moult 1c0fcf7b40 Add annotation symbols for doors, windows, and bunny to the demo library 2021-11-08 21:12:38 +11:00
Dion Moult 4fdcb87a28 New IfcOpenShell utility to get instances of an element type easily 2021-11-08 21:12:38 +11:00
Dion Moult 246460dec4 Fix bug where non-geometric objects caused the representations UI to not display 2021-11-08 21:12:38 +11:00
Thomas Krijnen a89a40e637 #1785 omplete stored_settings / requested_settings check 2021-11-08 10:59:49 +01:00
Thomas Krijnen c1fcf2528e swig uint64_t 2021-11-08 10:58:55 +01:00
Thomas Krijnen d4f0495caa Consistent setting type 2021-11-08 09:59:15 +01:00
Dion Moult 669295d4ac Resolve #1860. Brickschema is now optional. 2021-11-08 11:17:11 +11:00
Dion Moult e4a27d49c9 Drawing module now is in sync with latest C++ upgrades. See #1153. 2021-11-08 08:36:13 +11:00
Thomas Krijnen e0e8281a1e Allow tree.select(BRepElement) 2021-11-07 19:49:37 +01:00
Thomas Krijnen 1cdf695891 #1785 check settings in cache and upgrade to world coords 2021-11-07 19:23:48 +01:00
Thomas Krijnen 580678c2c1 Fix casts in XML serializer 2021-11-07 11:16:47 +01:00
Laurens Oostwegel a038ae434b ifccityjson: it is now easier to extract one LOD 2021-11-07 11:04:27 +01:00
Laurens Oostwegel 698ef40ada ifccityjson: prevent interior holes in faces as there is a bug in the geometry generation 2021-11-07 10:55:47 +01:00
Laurens Oostwegel 276077964d ifccityjson: refactor GeometryIO class 2021-11-07 10:51:09 +01:00
Laurens Oostwegel 45080a8a1f ifccityjson: temporary fix until CJIO can build its LOD dictionary again 2021-11-07 10:50:12 +01:00
Laurens Oostwegel dd348de0b7 ifccityjson: lazy build vertex dictionary 2021-11-07 10:40:01 +01:00
Dion Moult 61e510e45c New feature to select all objects in same container 2021-11-07 17:02:05 +11:00
Dion Moult 05068754b9 New feature to select the container of an object 2021-11-07 15:32:33 +11:00
Dion Moult de561b78e0 Resolve #1750. You can now save IFCs under a relative path to your Blend file. 2021-11-07 13:20:33 +11:00
Thomas Krijnen e6e1978584 only call expensive wstring_convert() when necessary 2021-11-06 14:36:58 +01:00
Thomas Krijnen 0b7bdc0f9d #1785 geometry caching caching 2021-11-06 13:16:13 +01:00
Dion Moult e868186897 Resolve #1749. You can now save as another IFC filename. 2021-11-06 21:41:31 +11:00
Dion Moult 5450233f8e Minor fix 2021-11-06 21:10:56 +11:00
Dion Moult 8e215382f7 Fix bug where assigning a type did not use existing representation data in the scene if the context had changed 2021-11-06 20:56:53 +11:00
Dion Moult 4149292a13 Fix bug where adding a representation to a type instance didn't share meshes 2021-11-06 18:51:00 +11:00
Dion Moult 117f663f48 Fix bug where you couldn't add a plan representation to something with no context identifier 2021-11-06 17:48:31 +11:00
Dion Moult 960610325f Fix interface bug where representations assigned directly to a context aren't shown 2021-11-06 17:36:47 +11:00
Thomas Krijnen ab50609854 Rename some files with more sensical names 2021-11-05 16:54:16 +01:00
Thomas Krijnen c203b78d34 #1153 #1852 project points to form SVG HLR cut plane 2021-11-05 13:27:27 +01:00
Thomas Krijnen 5633445008 #1153 evenodd 2021-11-05 11:21:19 +01:00
Thomas Krijnen 22ceb35f47 IfcConvert : SEW_SHELLS on svg serializer 2021-11-05 11:07:27 +01:00
Thomas Krijnen 06a988e141 #1153 boolean subtraction on parts 2021-11-05 11:04:46 +01:00
Thomas Krijnen 2dc29978f6 --cache and subtraction in draw.py 2021-11-05 11:03:44 +01:00
Thomas Krijnen 10b502512f #1853 don't blindly explode compounds but check whether there are solids inside 2021-11-05 10:09:12 +01:00
Thomas Krijnen 2d0c44e25a #1785 Set trsf form 2021-11-05 09:22:24 +01:00
Dion Moult 3e52d93db0 Fix bug where existing meshes were not deleted when switching representations. 2021-11-05 17:57:05 +11:00
Dion Moult 07bee4acfa Fix bug where adding a representation to type instances didn't ensure it was added to a geometric type 2021-11-05 15:14:10 +11:00
Dion Moult 0da5d807b8 Minor fix 2021-11-05 13:15:28 +11:00
Dion Moult 657aafbd2e Minor fix 2021-11-05 12:20:33 +11:00
Dion Moult 04449d08e8 Add schema caching to Brick schema loading. 2021-11-05 09:19:42 +11:00
Dion Moult 2b710ffbde Fix bug where pie update container didn't work. 2021-11-04 22:23:26 +11:00
Dion Moult 7fbe365599 Implement new Brickschema project browser. 2021-11-04 18:02:50 +11:00
Dion Moult f49b9f6781 Slightly more robust deletion tracking. 2021-11-03 21:39:17 +11:00
Dion Moult 901318b73b Fix bug where copying an instance did not auto link the meshes. 2021-11-03 19:04:22 +11:00
Laurens Oostwegel 8545d870c0 ifccityjson: Fix bug dependency updated CJIO
In the new CJIO module, cityjson vertices got transformed automatically when calling the object faces. Now this is not the case anymore.
2021-11-03 08:45:36 +01:00
Thomas Krijnen 3124d306d6 #1153 another small hack to get boolean projection working more generally 2021-11-02 15:52:51 +01:00
Thomas Krijnen 8e529f3f10 #1850 Process extrusions of comp profiles seperately 2021-11-02 15:11:56 +01:00
Thomas Krijnen 1ff45165d9 #1743 don't consider bbox in opening subtractions 2021-11-02 15:09:26 +01:00
Thomas Krijnen 05256fc0f4 #1785 geometry caching on multi threaded iterator 2021-11-02 11:39:49 +01:00
Thomas Krijnen be9ef33932 #1153 svg subtraction settings 2021-11-02 08:57:00 +01:00
Thomas Krijnen fc424ebd6b #1785 quick identity check to keep trsf form at identity 2021-11-01 21:47:56 +01:00
Laurens Oostwegel 030f3cf307 Add tutorial to ifccityjson 2021-11-01 18:18:46 +01:00
HassanEmam c8eb933453 Added support for Powerproject (#1849)
* implemented initial cli and support for resource parsing for ms project

* Added command line interface and resource support for MS Project XML files

* Removed the test code

* Check for output exists before writting the file to avoid failure

* Added support for Asta Powerproject files
2021-11-01 10:52:43 +11:00
Dion Moult 208132785f Minor fix 2021-10-31 10:08:41 +01:00
Dion Moult cfa435e9c8 Do the same things in 10 times less code thanks to aothms :) 2021-10-31 10:08:41 +01:00
Dion Moult 515e972906 Fix projection of origin onto camera plane. 2021-10-31 10:08:41 +01:00
Thomas Krijnen 100b0637ac submodule 2021-10-30 16:16:03 +02:00
HassanEmam 2d8ec8a6bf Command Line Interface and MS Project Resource Parsing (#1845)
* implemented initial cli and support for resource parsing for ms project

* Added command line interface and resource support for MS Project XML files

* Removed the test code

* Check for output exists before writting the file to avoid failure
2021-10-30 22:29:10 +11:00
Dion Moult 02461233a6 Reimplement drawing material classes, fix cut projection order, and auto refresh camera representation 2021-10-30 22:13:50 +11:00
Dion Moult dcac1e69ca We now support importing 3D annotations, not just 2D ones. Neat. 2021-10-30 22:13:50 +11:00
Dion Moult b118400cc6 Invalid annotation contexts coming from Revit are now accepted 2021-10-30 22:13:50 +11:00
Roch Bertucat bc430b80f6 Fix Python validate to handle Logical (see #1787) 2021-10-30 13:12:45 +02:00
Dion Moult d2d3addf10 New feature to draw system arrows showing flow direction and port connections. 2021-10-30 18:12:58 +11:00
Dion Moult 683e316088 New MEP utility to select and print debug information about connected system elements 2021-10-30 18:00:36 +11:00
HassanEmam e279fee518 Resource Parsing for P6XML (#1842)
* P6XML Parser failing on calendar type

* add work plan references a get_person which is not an attribute. modified to get_user to work

* Initial support for Primavera XER files

* IFC4D impl XER calendars exceptions and work time

* merged updats

* refactoring code for the 4D model to avoid duplicate code

* Assign process in the api module was missing an atrribute QuantityIn Process

* Merged changes from master

* refactoring

* resolved conflicts

* refactoring code for IFC4D

* refactoring code for IFC4D

* refactoring code for IFC4D

* Reverted the changes to IfcRelAssignsToProcess usecase

* Changed the class to be more discriptive from Utils to ScheduleIfcGenerator

* reverted changes to add_work_plan and Makefile

* Added resource parsing support for Primavera XER

* added relative path for importing common as it was failing in BlenderBIM

* Initial support for Primavera XER files

* merged updats

* Assign process in the api module was missing an atrribute QuantityIn Process

* Merged changes from master

* refactoring

* resolved conflicts

* refactoring code for IFC4D

* refactoring code for IFC4D

* Reverted the changes to IfcRelAssignsToProcess usecase

* Changed the class to be more discriptive from Utils to ScheduleIfcGenerator

* reverted changes to add_work_plan and Makefile

* Added resource parsing support for Primavera XER

* added relative path for importing common as it was failing in BlenderBIM

* deleted repetitive classes for P6XER import

* Resource Parsing added Primavera P6 XML module

* Initial support for Primavera XER files

* IFC4D impl XER calendars exceptions and work time

* merged updats

* refactoring code for the 4D model to avoid duplicate code

* Assign process in the api module was missing an atrribute QuantityIn Process

* Merged changes from master

* refactoring

* resolved conflicts

* refactoring code for IFC4D

* refactoring code for IFC4D

* refactoring code for IFC4D

* Reverted the changes to IfcRelAssignsToProcess usecase

* Changed the class to be more discriptive from Utils to ScheduleIfcGenerator

* reverted changes to add_work_plan and Makefile

* Added resource parsing support for Primavera XER

* added relative path for importing common as it was failing in BlenderBIM

* Initial support for Primavera XER files

* merged updats

* Assign process in the api module was missing an atrribute QuantityIn Process

* Merged changes from master

* refactoring

* resolved conflicts

* refactoring code for IFC4D

* refactoring code for IFC4D

* Reverted the changes to IfcRelAssignsToProcess usecase

* Changed the class to be more discriptive from Utils to ScheduleIfcGenerator

* reverted changes to add_work_plan and Makefile

* Added resource parsing support for Primavera XER

* deleted repetitive classes for P6XER import

* Resource Parsing added Primavera P6 XML module

* P6XML Parser failing on calendar type

* add work plan references a get_person which is not an attribute. modified to get_user to work

* Initial support for Primavera XER files

* IFC4D impl XER calendars exceptions and work time

* refactoring code for the 4D model to avoid duplicate code

* Assign process in the api module was missing an atrribute QuantityIn Process

* Merged changes from master

* refactoring

* resolved conflicts

* refactoring code for IFC4D

* refactoring code for IFC4D

* refactoring code for IFC4D

* Reverted the changes to IfcRelAssignsToProcess usecase

* Changed the class to be more discriptive from Utils to ScheduleIfcGenerator

* reverted changes to add_work_plan and Makefile

* Added resource parsing support for Primavera XER

* added relative path for importing common as it was failing in BlenderBIM

* Initial support for Primavera XER files

* merged updats

* Assign process in the api module was missing an atrribute QuantityIn Process

* Merged changes from master

* refactoring

* resolved conflicts

* refactoring code for IFC4D

* refactoring code for IFC4D

* Reverted the changes to IfcRelAssignsToProcess usecase

* Changed the class to be more discriptive from Utils to ScheduleIfcGenerator

* reverted changes to add_work_plan and Makefile

* Added resource parsing support for Primavera XER

* deleted repetitive classes for P6XER import

* Resource Parsing added Primavera P6 XML module

* Initial support for Primavera XER files

* IFC4D impl XER calendars exceptions and work time

* merged updats

* refactoring code for the 4D model to avoid duplicate code

* Assign process in the api module was missing an atrribute QuantityIn Process

* Merged changes from master

* refactoring

* resolved conflicts

* refactoring code for IFC4D

* refactoring code for IFC4D

* refactoring code for IFC4D

* Reverted the changes to IfcRelAssignsToProcess usecase

* Changed the class to be more discriptive from Utils to ScheduleIfcGenerator

* reverted changes to add_work_plan and Makefile

* Added resource parsing support for Primavera XER

* added relative path for importing common as it was failing in BlenderBIM

* Initial support for Primavera XER files

* merged updats

* Assign process in the api module was missing an atrribute QuantityIn Process

* Merged changes from master

* refactoring

* resolved conflicts

* refactoring code for IFC4D

* refactoring code for IFC4D

* Reverted the changes to IfcRelAssignsToProcess usecase

* Changed the class to be more discriptive from Utils to ScheduleIfcGenerator

* reverted changes to add_work_plan and Makefile

* Added resource parsing support for Primavera XER

* deleted repetitive classes for P6XER import

* Resource Parsing added Primavera P6 XML module

* removed the testing code

Co-authored-by: Dion Moult <dion@thinkmoult.com>
2021-10-30 10:31:18 +11:00
Thomas Krijnen 93c5906ad7 #1153 temporary y offset fix 2021-10-29 14:16:10 +02:00
HassanEmam 6a06607f3f BlenderBIM: Import Resources for XER files (#1841)
* P6XML Parser failing on calendar type

* add work plan references a get_person which is not an attribute. modified to get_user to work

* Initial support for Primavera XER files

* IFC4D impl XER calendars exceptions and work time

* merged updats

* refactoring code for the 4D model to avoid duplicate code

* Assign process in the api module was missing an atrribute QuantityIn Process

* Merged changes from master

* refactoring

* resolved conflicts

* refactoring code for IFC4D

* refactoring code for IFC4D

* refactoring code for IFC4D

* Reverted the changes to IfcRelAssignsToProcess usecase

* Changed the class to be more discriptive from Utils to ScheduleIfcGenerator

* reverted changes to add_work_plan and Makefile

* Added resource parsing support for Primavera XER

* added relative path for importing common as it was failing in BlenderBIM

* Initial support for Primavera XER files

* merged updats

* Assign process in the api module was missing an atrribute QuantityIn Process

* Merged changes from master

* refactoring

* resolved conflicts

* refactoring code for IFC4D

* refactoring code for IFC4D

* Reverted the changes to IfcRelAssignsToProcess usecase

* Changed the class to be more discriptive from Utils to ScheduleIfcGenerator

* reverted changes to add_work_plan and Makefile

* Added resource parsing support for Primavera XER

* added relative path for importing common as it was failing in BlenderBIM

* deleted repetitive classes for P6XER import

Co-authored-by: Dion Moult <dion@thinkmoult.com>
2021-10-29 21:16:47 +11:00
Thomas Krijnen 9f73bd43b4 Don't wrap HdfSerializer 2021-10-29 10:47:40 +02:00
Thomas Krijnen 5f7d92aeb0 #1153 HdfSerializer.remove() for cache eviction 2021-10-29 10:41:21 +02:00
Thomas Krijnen c754d15da8 don't create empty HdfSerializer symbol 2021-10-29 10:40:42 +02:00
Thomas Krijnen 13f5d2d5de add hdf serializer to wrapper 2021-10-29 10:40:15 +02:00
Thomas Krijnen 280eac6127 parse_ifcxml now handled by swig config 2021-10-29 10:40:03 +02:00
Thomas Krijnen ea0e70ef0d populate more serializers if available 2021-10-29 10:39:32 +02:00
Thomas Krijnen a5af8807ee pass config to swig preproc 2021-10-29 10:39:02 +02:00
Chun 0bae861b6a IfcType Filter suggestions for better User Experience (#1840)
* 1.Added Selecting/Deselecting All function
2.Suggested an idea for UI improvment
3.Fixed an UX bug

* Improve the user experience of IfcType Filter
2021-10-29 19:29:43 +11:00
Dion Moult b32bbb89a1 Optimistically track all object edits whenever you toggle edit mode, not just meshes. 2021-10-29 18:04:47 +11:00
Dion Moult 109149a0be Optimise owner check depending on your implementation and no longer create default users on IFC4. Can result in 50% faster script running on long scripted operations. 2021-10-29 18:04:17 +11:00
Dion Moult 4bf4622498 You can now manually type in start/end dates for construction sequence visualisation instead of clicking the date picker hundreds of times. 2021-10-29 17:44:57 +11:00
Dion Moult a4c054ef62 Objects now sync spatial hierarchy more robustly on export, and clean the collection tree in case the user messed it up. 2021-10-29 17:43:38 +11:00
Dion Moult 1141fd5784 Fix bug where during import sometimes aggregation collections don't match object names 2021-10-29 17:39:59 +11:00
luzpaz 0aa8a19b6d Fix various typos (#1837)
Found via `codespell`
2021-10-29 08:56:02 +11:00
Vukas Pajic b46f1647d2 BlenderBIM: Add UI Preset to Preferences (#1825)
* inital commit

* update based on comments

* incorporate suggestion from @Gorgious56

* change curly brackets to square brackets ;)

* update based on comments from @Moult

* code suggestion from @Gorgious56
2021-10-28 23:41:56 +11:00
Dion Moult 72d9bc9256 Moving grid axes are now synchronised automatically 2021-10-28 19:35:40 +11:00
Dion Moult b67d56ada1 Fix bug where imported grids were not linked and didn't auto track edits 2021-10-28 19:35:40 +11:00
Dion Moult d013d6ed09 Resize object to storey now works for a variable number of storeys 2021-10-28 19:35:40 +11:00
HassanEmam 4bb549f3e9 IFC4D code refactoring (#1834)
* P6XML Parser failing on calendar type

* add work plan references a get_person which is not an attribute. modified to get_user to work

* Initial support for Primavera XER files

* IFC4D impl XER calendars exceptions and work time

* merged updats

* refactoring code for the 4D model to avoid duplicate code

* Assign process in the api module was missing an atrribute QuantityIn Process

* Merged changes from master

* refactoring

* resolved conflicts

* refactoring code for IFC4D

* refactoring code for IFC4D

* refactoring code for IFC4D

* Reverted the changes to IfcRelAssignsToProcess usecase

* Changed the class to be more discriptive from Utils to ScheduleIfcGenerator

* reverted changes to add_work_plan and Makefile

Co-authored-by: Dion Moult <dion@thinkmoult.com>
2021-10-28 18:49:02 +11:00
Dion Moult d772299871 Fix bug where auto geolocation offset didn't occur if placements were relative 2021-10-28 11:55:11 +11:00
Dion Moult 081ca81a7e Adding annotation now auto enables viewport decoration drawing. See #1153. 2021-10-27 22:11:05 +11:00
Dion Moult 2ad2a102cb First pass at connecting to new ifcopenshell.draw. See #1153. 2021-10-27 19:42:36 +11:00
Dion Moult 52e8ac386e New hacktastic IFC patch to fix ArchiCAD spaces being loaded into Revit. 2021-10-27 12:29:40 +11:00
Dion Moult f69de3324a You can now split objects using a drawn edge, especially useful when creating slab pour breaks and fixing lazily modeled walls and columns. Thanks Gorgious56! 2021-10-27 12:06:12 +11:00
HassanEmam 04b3b9b60e Ifc4D xer calendars (#1832)
* P6XML Parser failing on calendar type

* add work plan references a get_person which is not an attribute. modified to get_user to work

* Initial support for Primavera XER files

* IFC4D impl XER calendars exceptions and work time

Co-authored-by: Dion Moult <dion@thinkmoult.com>
2021-10-27 10:13:50 +11:00
Dion Moult f64e1dbd7f New feature to calculate side formwork, such as for columns. 2021-10-27 10:09:07 +11:00
Dion Moult 341451f59b Formwork area calculation now unions objects before remeshing to account for overlapping geometry 2021-10-27 09:58:29 +11:00
Dion Moult 80b8da629d Types in type dropdowns are now sorted alphabetically for convenience. 2021-10-27 09:35:31 +11:00
Dion Moult 78e5016619 Minor fix. 2021-10-26 21:05:48 +11:00
Dion Moult 3406c23f98 Fix bug where copying a type also copied its type relations which is invalid. 2021-10-26 20:51:27 +11:00
Dion Moult ee308d294f Spatial elements in the import filter will now be sorted by elevation for convenience. 2021-10-26 17:37:17 +11:00
Dion Moult 955a9af461 Users copying and pasting via ctrl-c/v will now also copy IFC objects. 2021-10-26 17:36:58 +11:00
Dion Moult 34e8d9563d Using duplicate move linked in a BIM project now also triggers IFC copying 2021-10-26 17:17:20 +11:00
Dion Moult e5b5403eaa Minor code review for #1804. 2021-10-26 09:29:15 +11:00
Laurens Oostwegel 47cf2ea29e BlenderBIM: import IFC PointCloud Representation (#1804)
* Implemented export of point cloud representation

* Do representation geometry data no-edge check before no-polygon check

* Initial implementation import of point clouds in BlenderBIM

* create point cloud elements on the appropriate place

* Updated import_ifc.py based on review PR

* delete is_pointcloud() because it is replaced by get_pointcloud_representation()
2021-10-26 09:04:49 +11:00
HassanEmam 9d8074f1fe Initial support for importing Primavera XER files (#1829)
* P6XML Parser failing on calendar type

* add work plan references a get_person which is not an attribute. modified to get_user to work

* Initial support for Primavera XER files

Co-authored-by: Dion Moult <dion@thinkmoult.com>
2021-10-26 08:51:17 +11:00
HassanEmam 3fd944a956 Fixing bugs for Scheduling Module IFC4D (#1826)
* P6XML Parser failing on calendar type

* add work plan references a get_person which is not an attribute. modified to get_user to work

Co-authored-by: Dion Moult <dion@thinkmoult.com>
2021-10-25 20:56:45 +11:00
Thomas Krijnen 566512f7bc #1807 argument_types() on schema entity 2021-10-25 11:52:05 +02:00
Dion Moult 1a84967b1b Code review for Ifc Type filter feature. See #1823. 2021-10-25 20:12:36 +11:00
Chun 41afb26d31 Added IfcTypeFilter into BlenderBIM Search module (#1823) 2021-10-25 19:33:57 +11:00
Dion Moult 818e8010a4 Minor fix. 2021-10-25 19:32:37 +11:00
Dion Moult 58ee049a8f New feature to show inherited element type psets. 2021-10-25 16:21:40 +11:00
Dion Moult 54fa45850a Get psets utility now supports getting material and profile psets 2021-10-25 16:09:04 +11:00
Dion Moult fbadcc017b Get psets utility can now filter only psets or qtos 2021-10-25 15:36:55 +11:00
Dion Moult 6337698494 Element get_psets utility now returns the pset ID for convenience. 2021-10-25 14:21:41 +11:00
Dion Moult 2e6a70b338 New feature to copy properties from one object to all selected objects 2021-10-25 14:21:41 +11:00
Thomas Krijnen e70f675f20 Make build-all a bit friendlier for python partial install success 2021-10-24 14:56:50 +02:00
Thomas Krijnen 13d0b27ef4 Hacky way to return distances in the tree with extend>0 2021-10-24 14:40:18 +02:00
Thomas Krijnen 40b112cd48 Add back platform.machine to build-all path 2021-10-24 12:10:15 +02:00
Thomas Krijnen 14c0400f73 Fix case after #1781 2021-10-24 10:08:06 +02:00
Thomas Krijnen 4733c82b82 Separate out consume_iterator() 2021-10-23 21:34:25 +02:00
Dion Moult 7bccf10fec Fix regression bug where you couldn't add a grid. 2021-10-23 21:19:10 +11:00
Dion Moult 918915aeae Reintroduced feature to copy attributes to selected objects. 2021-10-23 19:55:22 +11:00
Dion Moult 295568453c Minor code review for module visibility feature. See #1798. 2021-10-23 19:01:43 +11:00
Vukas Pajic 56ff0e7472 BlenderBIM: added dropdown in addon preferences to toggle module visibility (#1798)
* added dropdown in addon preferences to toggle module visibility

* Update ui.py

* updated ui to allow individual toggling of modules

* working

* code cleanup and update

* fix issue where non relevant operators shown

* update based on review comments

* add contingency for intermodule panel dependancy

* removed 'magic' operators :)

* clean up some leftover lines

* updated to only register/deregister panel classes
2021-10-23 18:16:11 +11:00
Dion Moult e779a57a09 Fix bug where imported types with representations but no instances were not fully linked. 2021-10-23 18:15:23 +11:00
Dion Moult 6d817703aa Copying parametric geometry from any source now attempts to preserve parametric geometry when possible. 2021-10-22 18:07:34 +11:00
Dion Moult c551275db2 Fix bug where switching to an object with modifiers applied would double the effect of modifiers. 2021-10-22 16:23:36 +11:00
Dion Moult 9ffe92eae5 Assigning a parametric layer type (for walls for now) now attempts to switch from non parametric to parametric geometry. 2021-10-22 15:43:04 +11:00
Dion Moult f19e334cd5 Fix bug where large resizing of walls could result in broken geometry. 2021-10-22 15:21:26 +11:00
Dion Moult a5b8728aec Fix bug where copying elements didn't ensure that material usages were unique 2021-10-22 12:09:57 +11:00
Dion Moult a8c99cebaf Fix terrible bug #1819 where duplicating mapped elements didn't actually copy the geometry mapping. 2021-10-22 11:16:03 +11:00
Dion Moult 9ae2c6352a Minor fix 2021-10-21 23:06:39 +11:00
Dion Moult 0bc849be5a New feature to conveniently add a default material in a blank project when none exists. 2021-10-21 19:40:10 +11:00
Dion Moult a99df36351 The type of mapped representations is now shown in the interface, with an asterisk denoting it is mapped. 2021-10-21 19:16:15 +11:00
Dion Moult 525e6debd5 When updating representations, the existing representation type is prioritised unless you tell it otherwise. See #1748. 2021-10-21 18:39:55 +11:00
Gorgious d6cb94c6f1 Port root, bimtester and sequence to feature tests 2021-10-21 09:33:45 +02:00
Dion Moult c437ba8f9d Assigning a parametric profile type now auto switches from non parametric geometry to parametric geometry 2021-10-21 16:28:55 +11:00
Dion Moult aee7fbcc79 Only show type selection buttons when a type is available. 2021-10-21 14:10:24 +11:00
Dion Moult 456adf453d New feature to calculate the radius of a polygonal circle 2021-10-21 12:43:16 +11:00
Dion Moult d2e9194636 Adding type empties now adds it to the scene collection for convenience in case your types collection is hidden (which is most of the time) 2021-10-21 11:37:44 +11:00
Dion Moult 9907651d4a Fix bug where you could reassign class to another product type and create invalid relationships 2021-10-21 11:28:49 +11:00
Massimo Fabbro bb2f59e2f8 Add tooltips. Finished the "Authoring" section in the n-panel. (See issue #1646) (#1818)
* Add first tooltip (#1646)

* Add tooltips for new project

* Small fix

* Added more tootips

* Add other tooltip to the n-panel

* Add other tooltips in N Panel
2021-10-21 10:10:54 +11:00
Gorgious56 fec295d021 Migration (#1817) 2021-10-21 10:10:22 +11:00
Dion Moult cb08c19942 Fix bug where BCF header file info was not loaded. See #1816. 2021-10-20 23:41:43 +11:00
Thomas Krijnen b1b121ebc8 #1814 check for the succesful creation of edge curves in IfcIndexedPolyCurve 2021-10-20 11:14:35 +02:00
Alexander Nitsch ba9c2a3280 Fix initialization of local variable "data" (#1813)
Co-authored-by: Nitsch Alexander <alexander.nitsch@scs.ch>
2021-10-20 19:57:13 +11:00
Dion Moult 575de12d79 Fix bug where auto assignment in the collection hierarchy failed if you had filtered out aggregates and you were assigning a subelement of an aggregate 2021-10-20 19:56:46 +11:00
Dion Moult 503f0399ff Fix bug where you couldn't bulk copy non-geometric elements to other spatial containers 2021-10-20 18:47:55 +11:00
Vukas Pajic 871ad1ab7f Added operator to add psets to all selected objects. (#1805)
* first attempt

* Accommodate object vs non-object psets, and directly fetch applicable pset names

Co-authored-by: Dion Moult <dion@thinkmoult.com>
2021-10-20 14:49:02 +11:00
Dion Moult 58859ef8c3 Fix bug where when deleting representations it could sometimes overreach and delete geometric, style, or layer data of other elements. See #1812. 2021-10-20 14:11:49 +11:00
Dion Moult 52062bc7e8 Experimental proposed deep removal. See #1812. 2021-10-20 11:44:19 +11:00
Gorgious56 8fdbe30dc5 Ux docs test (#1808)
* First draft

* Final Update
2021-10-19 10:55:25 +02:00
Dion Moult f438c48c23 Fix bug where copying objects may sometimes result in incorrect location in the Blender collection tree 2021-10-19 19:15:01 +11:00
Dion Moult 6478601f79 Fix bug where copying objects did not necessarily retain the existing context assignment 2021-10-19 19:15:01 +11:00
Thomas Krijnen 816059eefd Reapply 869cc25061 2021-10-19 08:49:46 +02:00
Dion Moult 3daf4203d1 When assigning types, your preference of dynamic voids are now preserved. 2021-10-19 15:43:14 +11:00
Dion Moult c1b53859ee WIP refactor of geometry for stabilisation and optimisation. Switch representation now done. 2021-10-19 12:57:29 +11:00
Thomas Krijnen 63f85beb7e #1803 Update include in wrapper code as well 2021-10-18 16:10:20 +02:00
Thomas Krijnen bdc6fcb66f #1803 Use abstract GeometrySerializer as cache to eliminate cyclic dependencies 2021-10-18 11:38:01 +02:00
Dion Moult 4577b7bf60 Potentially risky refactor of add_representation in preparation of stabilisation and optimization of geometric operators. 2021-10-18 19:23:40 +11:00
Dion Moult 33f4461a87 New feature to resize / squash / stretch objects to their storey height. Useful for remodeling IFCs for construction simulations. 2021-10-18 14:15:16 +11:00
Dion Moult c78e7b3ba8 Fix minor bug where Python warnings showed up when delete operations were called 2021-10-18 14:12:07 +11:00
Dion Moult 699ccddda3 New utility function to get storey elevations with elevation attribute fallback for more compliant sorting of building storey elevations. 2021-10-18 12:39:04 +11:00
Dion Moult a9a48bdd1d Refactor misc functions into new misc module for better organisation. 2021-10-18 11:21:49 +11:00
amoki 8ed395a29f build python only if needed 2021-10-17 10:15:08 +02:00
amoki 63bfe761bf use platform.system() instead of uname, install python module directly in current python 2021-10-17 10:15:08 +02:00
amoki f8c11e3af7 rollback boost version 2021-10-17 10:15:08 +02:00
amoki 993ae71968 upgrade dependencies, remove x86 build 2021-10-17 10:15:08 +02:00
amoki 81b8f7e39b remove last TAG 2021-10-17 10:15:08 +02:00
amoki b1e897197b more cleanup 2021-10-17 10:15:08 +02:00
amoki 46cf1362f6 more f-strings, add some env variables 2021-10-17 10:15:08 +02:00
amoki f03992dcde remove old python versions and use f-strings 2021-10-17 10:15:08 +02:00
Dion Moult aedc1751b6 Resolve #1802. Unlinking an object now also unlinks materials and styles too for convenience. 2021-10-16 16:55:06 +11:00
Dion Moult 26cbd82da1 New feature to unlink Blender materials and IFC materials 2021-10-16 16:53:37 +11:00
Dion Moult 516503b9ca Fix #1801. Fix bug where importing multiple assets sharing an already imported material fails. 2021-10-16 15:46:02 +11:00
Dion Moult 23be5858b5 Fix bug where the derived placements interface didn't take into account rotations 2021-10-15 17:30:33 +11:00
Dion Moult 776842a064 Spatial containers are now shown sorted by Z index order for convenience. 2021-10-15 17:07:23 +11:00
Dion Moult bb090bd9a0 Fix deep magick bug where copying elements without having one of them active didn't actually copy them in IFC too 2021-10-15 14:42:08 +11:00
Dion Moult 0abe9f0559 Building storeys now show a derived storey height so you don't need to do math 2021-10-15 13:23:18 +11:00
Dion Moult 07bfe296dd You can now copy a representation from one object to another 2021-10-15 13:23:18 +11:00
Dion Moult d666a687dc FMHEM classes now updated to latest draft 2021-10-15 13:23:18 +11:00
Dion Moult acde4144bc You can now toggle whether element filtering also affects spatial containers 2021-10-15 13:23:18 +11:00
Laurens Oostwegel b41e90ead6 IfcOpenShell-python: Implemented point cloud export functionalities (#1799)
* Implemented export of point cloud representation

* Do representation geometry data no-edge check before no-polygon check
2021-10-15 12:15:55 +11:00
Dion Moult 68fd0c0cf5 Fix bug where you couldn't filter imported subelements without also selecting their parents 2021-10-14 18:47:48 +11:00
Dion Moult 199be3363f Fix bug where copying elements shared a placement. This fix speeds up copying parent elements in the decomposition significantly, like when copying a storey. 2021-10-14 16:13:48 +11:00
Dion Moult dd3fc79fb9 Use context filters during import when possible to optimise import times 2021-10-14 15:53:25 +11:00
Dion Moult d384b3d7df Fix bug where copying a container also copied its contents 2021-10-14 15:52:38 +11:00
Dion Moult 04209c78b9 Minor revert - until I properly refactor and test this. 2021-10-14 13:06:26 +11:00
Dion Moult a7719cc4b1 Fix #1796. Fix bug where duplicating a collection messes up the collection tree. 2021-10-14 13:04:30 +11:00
Dion Moult 3d0d2ec8fb Fix #1795. New support for conversion based units with offsets, like fahrenheit. 2021-10-14 12:46:01 +11:00
Dion Moult 1fa1a9e3d3 Support auto detection of unit type when adding imperial units 2021-10-14 12:44:13 +11:00
Dion Moult 464b0c2ebc Unassigning the last unit now cleans up empty unit assignments 2021-10-14 12:42:33 +11:00
Dion Moult 179a970974 Utility to print out all maintainable assets for FM paper documentation 2021-10-14 11:36:46 +11:00
Dion Moult 7fb77a794e Fix #1793. Whoops. 2021-10-13 19:09:30 +11:00
Dion Moult 1d74306bc2 New recipe (and essay) to fix ArchiCAD IFC4 door swings showing up in Revit 2021-10-13 16:39:16 +11:00
Dion Moult 0c6a3eafd2 IfcCSV now supports setting properties to null values. Neat. 2021-10-13 12:18:29 +11:00
Dion Moult 292cbf55ad Resolve #1773. IfcCSV now supports editing properties that were previously null. 2021-10-13 12:17:20 +11:00
Dion Moult e17ae4ef61 Fix #1792. Regression caused by other improvements in editing object placement. 2021-10-13 11:55:03 +11:00
Dion Moult dba6a8d0b4 Fix #1786. Swap out sub-operators with refactored core calls. 2021-10-13 10:49:28 +11:00
Dion Moult 13725411c9 Migrate project operator tests to pytest-bdd 2021-10-13 09:48:35 +11:00
maxfb87 b0e9f389a5 Add first tooltip (#1646) (#1791) 2021-10-13 09:03:36 +11:00
Dion Moult f6128febe9 You can now clear the active user for ownership history. See #1790. 2021-10-12 19:12:15 +11:00
Dion Moult fc2e12bff6 New usecase for adding conversion based units 2021-10-12 18:34:26 +11:00
Gorgious56 e455f87305 First batch of Pset Templates tests (#1772) 2021-10-09 13:26:23 +02:00
Vukas Pajic 84fc198bbe added new functionality and adjusted UI (#1771) 2021-10-09 10:13:10 +11:00
Thomas Krijnen d39e97f6a7 Default HDF5 dynamic libs 2021-10-08 22:01:34 +02:00
Thomas Krijnen 96d509698b Prefix namespaces for swig 2021-10-08 21:35:41 +02:00
Thomas Krijnen cefa66cdf9 Link to TKBin 2021-10-08 21:07:56 +02:00
Thomas Krijnen 8198df3fa6 header 2021-10-08 20:39:16 +02:00
Thomas Krijnen 4ebdb5e339 header 2021-10-08 15:50:51 +02:00
Thomas Krijnen 8dca84dd07 Geometry caching 2021-10-08 15:38:46 +02:00
Thomas Krijnen 4cee76ddc3 Make preprocessor defs consistent in IfcConvert.cpp and util.h 2021-10-08 15:25:28 +02:00
Gorgious56 8b34eb41fd Fix and test importing style attributes twice (#1784)
* Test importing surface attributes twice

* Fix style attributes not cleared when re-importing
2021-10-08 22:10:24 +11:00
Dion Moult 170cd3b5f5 Minor fix again 2021-10-07 18:59:41 +11:00
Dion Moult b8c4dd971c Minor fix 2021-10-07 18:55:22 +11:00
Dion Moult f26d43e0cd Prioritise editing rendering surface styles over shading surface styles 2021-10-07 18:51:50 +11:00
Dion Moult 9508858fcb New unassign aggregate feature. 2021-10-06 14:11:54 +11:00
Dion Moult 3aa36dd4b7 Changing aggregation now ensures spatial containment is mutually exclusive to prevent invalid IFC data 2021-10-06 12:52:01 +11:00
Dion Moult f21005c29e Assigning aggregations now protects against various invalid aggregation combinations 2021-10-06 12:50:46 +11:00
1184 changed files with 77245 additions and 23525 deletions
+2 -2
View File
@@ -28,7 +28,7 @@ jobs:
sudo apt update
sudo apt-get install --no-install-recommends \
git cmake gcc g++ libboost-all-dev python3-all-dev swig libpcre3-dev libxml2-dev \
liboce-foundation-dev liboce-modeling-dev liboce-ocaf-dev liboce-visualization-dev liboce-ocaf-lite-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
-
@@ -47,7 +47,7 @@ jobs:
-DCMAKE_PREFIX_PATH=/usr \
-DCMAKE_SYSTEM_PREFIX_PATH=/usr \
-DBUILD_PACKAGE=On \
-DOCC_INCLUDE_DIR=/usr/include/oce \
-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 \
+2 -1
View File
@@ -23,12 +23,13 @@ jobs:
run: echo ok go
build:
needs: activate
name: ${{ matrix.config.name }}-${{ matrix.pyver }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
pyver: [py37, py39]
pyver: [py37, py39, py310]
config:
- {
name: "Windows Build",
+4 -3
View File
@@ -41,7 +41,7 @@ jobs:
python3-all-dev python3-pip \
swig libpcre3-dev libxml2-dev \
libtbb-dev nlohmann-json3-dev \
liboce-foundation-dev liboce-modeling-dev liboce-ocaf-dev liboce-visualization-dev liboce-ocaf-lite-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
@@ -53,11 +53,10 @@ jobs:
cmake \
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DCMAKE_INSTALL_PREFIX=$PWD/install/ \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_PREFIX_PATH=/usr \
-DCMAKE_SYSTEM_PREFIX_PATH=/usr \
-DOCC_INCLUDE_DIR=/usr/include/oce \
-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 \
@@ -83,6 +82,8 @@ jobs:
sudo /usr/bin/python -m pip install src/bcf
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
- name: Test
run: |
+4 -4
View File
@@ -18,14 +18,14 @@ jobs:
- name: Checkout
uses: actions/checkout@v2
with:
lfs: true
submodules: recursive
-
name: Update ubuntu
run: |
sudo apt update
sudo apt-get install git cmake gcc g++ libboost1.67-all-dev python3-all-dev swig libpcre3-dev libxml2-dev
sudo apt-get install liboce-foundation-dev liboce-modeling-dev liboce-ocaf-dev liboce-visualization-dev liboce-ocaf-lite-dev \
libhdf5-dev
sudo apt-get install 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: Build
env:
@@ -39,7 +39,7 @@ jobs:
-DCMAKE_PREFIX_PATH=/usr \
-DCMAKE_SYSTEM_PREFIX_PATH=/usr \
-DBUILD_PACKAGE=On \
-DOCC_INCLUDE_DIR=/usr/include/oce \
-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 \
+13
View File
@@ -5,6 +5,15 @@
/_installed-vs*-x*/
/build/
# 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
@@ -13,6 +22,7 @@ __pycache__
*.py.bak
# Visual Studio Code files
.vscode
.vs
# PyCharm files
.idea
@@ -57,3 +67,6 @@ htmlcov
# Blender
*.blend1
# Sphinx
_build/
+6
View File
@@ -8,3 +8,9 @@
[submodule "src/svgfill"]
path = src/svgfill
url = https://github.com/IfcOpenShell/svgfill
[submodule "src/ifcopenshell-python/ifcopenshell/Sample-BIM-Files"]
path = src/ifcopenshell-python/ifcopenshell/Sample-BIM-Files
url = https://github.com/atomczak/Sample-BIM-Files
[submodule "src/ifcopenshell-python/test/Sample-BIM-Files"]
path = src/ifcopenshell-python/test/Sample-BIM-Files
url = https://github.com/atomczak/Sample-BIM-Files
+31 -10
View File
@@ -1,7 +1,9 @@
IfcOpenShell
============
IfcOpenShell is an open source ([LGPL]) software library for working with the Industry Foundation Classes ([IFC])
file format. Currently supported IFC releases are [IFC2x3 TC1] and [IFC4 Add2 TC1].
file format. Extensive geometric support is implemented for the IFC releases [IFC2x3 TC1] and [IFC4 Add2 TC1].
Support for parsing is provided for IFC4x1, IFC4x2, and the IFC4x3 release candidates. Extending with support for
arbitrary IFC schemas is possible at compile-time when using C++ and at run-time when using Python.
For more information, see
* [http://ifcopenshell.org](http://ifcopenshell.org)
@@ -9,11 +11,13 @@ For more information, see
[![Build Status](https://travis-ci.org/IfcOpenShell/IfcOpenShell.svg?branch=v0.6.0)](https://travis-ci.org/IfcOpenShell/IfcOpenShell)
[![Financial Contributors](https://opencollective.com/opensourcebim/tiers/badge.svg)](https://opencollective.com/opensourcebim/)
Prerequisites
-------------
* Git
* CMake (2.6 or newer)
* Windows: [Visual Studio] 2008 or newer with C++ toolset (or [Visual C++ Build Tools]) or [MSYS2] + MinGW
* Windows: [Visual Studio] 2008 to 2019 (2022 not yet supported by dependency CMake) with C++ toolset (or [Visual C++ Build Tools]) or [MSYS2] + MinGW
* *nix: GCC 4.7 or newer, or Clang (any version)
Dependencies
@@ -33,7 +37,11 @@ Dependencies
Building IfcOpenShell
---------------------
**Note:** The path where the source code is cloned to can contain spaces but non-ASCII characters are very likely to cause problems with the build.
Note 1: The path where the source code is cloned to can contain spaces but non-ASCII characters are very likely to cause problems with the build.
Note 2: If you had not used `git clone --recursive https://github.com/IfcOpenShell/IfcOpenshell.git`, update the submodules by running `git submodule init & git submodule update`.
Note 3: Be careful with special characters is the path when using the nix or win build scripts, because the OpenCASCADE build will fail on paths containing ++ and likely other situations.
### Compiling on Windows
The preferred way to fetch and build this project's dependencies is to use the build scripts
@@ -81,17 +89,26 @@ The following instructions are for Ubuntu, modify as required for other operatin
can be experimented with and studied for pointers for other operating systems, but note that this script is not currently
meant to be used for a typical IfcOpenShell workspace setup.
Note: where `make -j` is written, add a number roughly equal to the amount of CPU cores + 1.
Note 1: It is recommeded to use OCCT for IfcOpenShell. You could use OCE as well, but sometimes it may lag behind OCCT and
therefore not compile with the latest IfcOpenShell.
Note 2: where `make -j` is written, add a number roughly equal to the amount of CPU cores + 1.
**1)** Install most of the prerequisites and dependencies:
$ sudo apt-get install git cmake gcc g++ libboost-all-dev
$ sudo apt-get install git cmake gcc g++ libboost-all-dev libcgal-dev
**2a)** Either use an OCE package from your operating system's software repository
**2a)** Either use an OCCT package from your operating system's software repository
$ sudo apt-get install libocct-data-exchange-dev libocct-draw-dev libocct-foundation-dev libocct-modeling-algorithms-dev libocct-modeling-data-dev libocct-ocaf-dev libocct-visualization-dev
**2b)** or, if OCCT is not available or the latest code is wanted, obtain and compile OCCT from https://dev.opencascade.org/release
**2c)** or, if you'd like to try using OCE, use the package from your operating system's software repository
$ sudo apt-get install liboce-foundation-dev liboce-modeling-dev liboce-ocaf-dev liboce-visualization-dev liboce-ocaf-lite-dev
**2b)** or (if not available, or the latest code is wanted) compile OCE yourself (note that the build takes a long time):
**2d)** or if OCE is not available, or the latest code is wanted, compile OCE yourself (note that the build takes a long time)
$ sudo apt-get install libftgl-dev libtbb2 libtbb-dev libgl1-mesa-dev libfreetype6-dev
$ git clone https://github.com/tpaviot/oce.git
@@ -101,8 +118,6 @@ Note: where `make -j` is written, add a number roughly equal to the amount of CP
$ make -j
$ sudo make install
**2c)** or obtain and compile OCCT from https://dev.opencascade.org/release
**3)** For building IfcConvert with COLLADA (.dae) support (on by default), OpenCOLLADA is needed:
$ sudo apt-get install libpcre3-dev libxml2-dev
@@ -128,7 +143,13 @@ dependencies' paths. `OCC_INCLUDE_DIR` might be needed to set also. `OPENCOLLADA
$ cmake ../cmake -DOCC_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu/ \
-DOPENCOLLADA_INCLUDE_DIR="/usr/local/include/opencollada" \
-DOPENCOLLADA_LIBRARY_DIR="/usr/local/lib/opencollada" \
-DPCRE_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu/
-DPCRE_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu/ \
-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_SUPPORT=Off
$ make -j
If all worked out correctly you can now use IfcOpenShell. See the examples below.
+41 -25
View File
@@ -60,9 +60,14 @@ if (${BUILD_CONVERT})
OPTION(GLTF_SUPPORT "Build IfcConvert with glTF support (requires json.hpp)." OFF)
endif()
OPTION(USERSPACE_PYTHON_PREFIX "Installs IfcPython for the current user only instead of system-wide." OFF)
OPTION(ADD_COMMIT_SHA "Add commit sha and branch in version number, warning results in many rebuilds, requires git" OFF)
# TODO QtViewer is deprecated ATM as it uses the 0.4 API
# OPTION(BUILD_QTVIEWER "Build IfcOpenShell Qt GUI Viewer (requires Qt 4 framework)." OFF)
IF(NOT CMAKE_BUILD_TYPE)
SET(CMAKE_BUILD_TYPE "Release")
ENDIF()
# QtViewer requires Qt5
OPTION(BUILD_QTVIEWER "Build IfcOpenShell Qt GUI Viewer" OFF)
include(GNUInstallDirs)
if((BUILD_CONVERT OR BUILD_GEOMSERVER OR BUILD_IFCPYTHON) AND (NOT BUILD_IFCGEOM))
message(STATUS "'IfcGeom' is required with current outputs")
@@ -144,6 +149,7 @@ ELSE()
MESSAGE(FATAL_ERROR "Unable to find JSON for Modern C++ header file, aborting")
ENDIF()
add_definitions(-DWITH_GLTF)
set(SWIG_DEFINES ${SWIG_DEFINES} -DWITH_GLTF)
endif()
# Set INSTALL_RPATH for target
@@ -200,6 +206,7 @@ find_package(LibXml2 REQUIRED)
if (IFCXML_SUPPORT)
add_definitions(-DWITH_IFCXML)
set(SWIG_DEFINES ${SWIG_DEFINES} -DWITH_IFCXML)
endif()
FIND_PACKAGE(Boost REQUIRED COMPONENTS ${BOOST_COMPONENTS})
@@ -270,6 +277,9 @@ ENDIF()
SET(OPENCASCADE_LIBRARY_NAMES
TKernel TKMath TKBRep TKGeomBase TKGeomAlgo TKG3d TKG2d TKShHealing TKTopAlgo TKMesh TKPrim TKBool TKBO
TKFillet TKSTEP TKSTEPBase TKSTEPAttr TKXSBase TKSTEP209 TKIGES TKOffset TKHLR
# @todo investigate the exact conditions when this is necessary
TKBin
)
IF("${OCC_LIBRARY_DIR}" STREQUAL "")
@@ -368,6 +378,8 @@ IF(COLLADA_SUPPORT)
IF(COLLADASWStreamWriter_h)
MESSAGE(STATUS "OpenCOLLADA header files found")
ADD_DEFINITIONS(-DWITH_OPENCOLLADA)
set(SWIG_DEFINES ${SWIG_DEFINES} -DWITH_OPENCOLLADA)
SET(OPENCOLLADA_LIBRARY_NAMES
GeneratedSaxParser MathMLSolver OpenCOLLADABaseUtils OpenCOLLADAFramework OpenCOLLADASaxFrameworkLoader
OpenCOLLADAStreamWriter UTF buffer ftoa
@@ -436,13 +448,17 @@ if(HDF5_SUPPORT)
else()
set(lib_ext a)
endif()
if ("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
set(debug_postfix "_debug")
endif()
SET(HDF5_LIBRARIES
"${HDF5_LIBRARY_DIR}/libhdf5_cpp.${lib_ext}"
"${HDF5_LIBRARY_DIR}/libhdf5.${lib_ext}"
"${HDF5_LIBRARY_DIR}/libz${zlib_post}.${lib_ext}"
"${HDF5_LIBRARY_DIR}/libsz.${lib_ext}"
"${HDF5_LIBRARY_DIR}/libaec.${lib_ext}"
"${HDF5_LIBRARY_DIR}/libhdf5_cpp${debug_postfix}.${lib_ext}"
"${HDF5_LIBRARY_DIR}/libhdf5${debug_postfix}.${lib_ext}"
"${HDF5_LIBRARY_DIR}/libz${zlib_post}${debug_postfix}.${lib_ext}"
"${HDF5_LIBRARY_DIR}/libsz${debug_postfix}.${lib_ext}"
"${HDF5_LIBRARY_DIR}/libaec${debug_postfix}.${lib_ext}"
)
endif()
@@ -450,21 +466,18 @@ if(HDF5_SUPPORT)
# debian default
SET(HDF5_LIBRARIES
/usr/lib/x86_64-linux-gnu/hdf5/serial/libhdf5_cpp.a
/usr/lib/x86_64-linux-gnu/hdf5/serial/libhdf5.a
/usr/lib/x86_64-linux-gnu/libsz.a
/usr/lib/x86_64-linux-gnu/libaec.a
/usr/lib/x86_64-linux-gnu/hdf5/serial/libhdf5_cpp.so
/usr/lib/x86_64-linux-gnu/hdf5/serial/libhdf5.so
/usr/lib/x86_64-linux-gnu/libsz.so
/usr/lib/x86_64-linux-gnu/libaec.so
z dl
)
endif()
add_definitions(-DWITH_HDF5)
set(SWIG_DEFINES ${SWIG_DEFINES} -DWITH_HDF5)
endif()
IF(NOT CMAKE_BUILD_TYPE)
SET(CMAKE_BUILD_TYPE "Release")
ENDIF()
if(ENABLE_BUILD_OPTIMIZATIONS)
if(MSVC)
# NOTE: RelWithDebInfo and Release use O2 (= /Ox /Gl /Gy/ = Og /Oi /Ot /Oy /Ob2 /Gs /GF /Gy) by default,
@@ -689,7 +702,7 @@ TARGET_LINK_LIBRARIES(IfcParse ${Boost_LIBRARIES} ${BCRYPT_LIBRARIES} ${LIBXML2_
if (BUILD_IFCGEOM)
# IfcGeom
file(GLOB IFCGEOM_H_FILES ../src/ifcgeom/*.h)
file(GLOB IFCGEOM_H_FILES ../src/ifcgeom/*.h ../src/ifcgeom/*.i)
file(GLOB IFCGEOM_CPP_FILES ../src/ifcgeom/*.cpp)
set(IFCGEOM_FILES ${IFCGEOM_CPP_FILES} ${IFCGEOM_H_FILES})
@@ -781,21 +794,20 @@ INSTALL(TARGETS IfcGeomServer
endif()
if (ADD_COMMIT_SHA)
find_package (Git)
if (GIT_FOUND)
message("git found: ${GIT_EXECUTABLE} with version ${GIT_VERSION_STRING}")
execute_process(
COMMAND ${GIT_EXECUTABLE} show -s --pretty=%D HEAD
COMMAND ${GIT_EXECUTABLE} branch --contains HEAD
OUTPUT_VARIABLE git_branches
OUTPUT_STRIP_TRAILING_WHITESPACE
)
string(REPLACE ", " ";" git_branch_list "${git_branches}")
string(REPLACE " -> " ";" git_branch_list "${git_branch_list}")
string(REPLACE "\n" ";" git_branch_list "${git_branches}")
foreach(git_branch_candidate IN ITEMS ${git_branch_list})
if (NOT git_branch_candidate MATCHES "/")
if (NOT git_branch_candidate MATCHES "^HEAD$")
set(git_branch ${git_branch_candidate})
endif()
string(STRIP "${git_branch_candidate}" git_branch_candidate_2)
if (NOT git_branch_candidate_2 MATCHES "^HEAD$")
set(git_branch ${git_branch_candidate_2})
endif()
endforeach()
execute_process(
@@ -803,11 +815,12 @@ if (GIT_FOUND)
OUTPUT_VARIABLE git_sha
OUTPUT_STRIP_TRAILING_WHITESPACE
)
message(STATUS "IfcOpenShell branch ${git_branch}")
message(STATUS "IfcOpenShell commit ${git_sha}")
message(STATUS "IfcOpenShell branch: \"${git_branch}\"")
message(STATUS "IfcOpenShell commit: \"${git_sha}\"")
add_definitions(-DIFCOPENSHELL_BRANCH=${git_branch})
add_definitions(-DIFCOPENSHELL_COMMIT=${git_sha})
endif()
endif()
# Documentation
IF(BUILD_DOCUMENTATION)
@@ -872,6 +885,9 @@ INSTALL(FILES ${SERIALIZERS_S_FILES}
)
endif()
IF(BUILD_QTVIEWER)
ADD_SUBDIRECTORY(../src/qtviewer qtviewer)
endif()
list(APPEND CPACK_SOURCE_IGNORE_FILES
.git
+7 -1
View File
@@ -24,13 +24,19 @@ requirements:
- swig >=3.0.9
- libboost
- icu
- xmlschema
- lxml
- numpy
run:
- libgcc # [osx]
- python
- oce ==0.18.3
- libboost
- icu
- xmlschema
- lxml
- numpy
about:
home: http://ifcopenshell.org
license: LGPL
+275 -294
View File
@@ -56,16 +56,10 @@ import subprocess as sp
import shutil
import tarfile
import multiprocessing
import platform
import sysconfig
PYTHON_MAJOR = sys.version_info[0]
if PYTHON_MAJOR >= 3:
from urllib.request import urlretrieve
else:
# Not Python 3 - today, it is most likely to be Python 2
# But note that this might need an update when Python 4
# might be around one day
from urllib import urlretrieve
from urllib.request import urlretrieve
logger = logging.getLogger(__name__)
@@ -74,145 +68,121 @@ ch = logging.StreamHandler()
ch.setLevel(logging.INFO)
logger.addHandler(ch)
PROJECT_NAME="IfcOpenShell"
PYTHON_VERSIONS=["2.7.18", "3.5.5", "3.6.13", "3.7.10", "3.8.8", "3.9.2"]
JSON_VERSION="v3.6.1"
OCE_VERSION="0.18"
OCCT_VERSION="7.5.3"
BOOST_VERSION="1.71.0"
PCRE_VERSION="8.41"
LIBXML2_VERSION="2.9.9"
SWIG_VERSION="3.0.12"
OPENCOLLADA_VERSION="v1.6.68"
HDF5_VERSION="1.8.22"
PROJECT_NAME = "IfcOpenShell"
USE_CURRENT_PYTHON_VERSION = os.getenv("USE_CURRENT_PYTHON_VERSION")
ADD_COMMIT_SHA = os.getenv("ADD_COMMIT_SHA")
GMP_VERSION="6.1.2"
MPFR_VERSION="3.1.5"
CGAL_VERSION="5.2"
PYTHON_VERSIONS = ["3.6.14", "3.7.12", "3.8.12", "3.9.7", "3.10.0"]
JSON_VERSION = "v3.6.1"
OCE_VERSION = "0.18.3"
OCCT_VERSION = "7.5.3"
BOOST_VERSION = "1.71.0"
PCRE_VERSION = "8.41"
LIBXML2_VERSION = "2.9.11"
SWIG_VERSION = "4.0.2"
OPENCOLLADA_VERSION = "v1.6.68"
HDF5_VERSION = "1.12.1"
GMP_VERSION = "6.1.2"
MPFR_VERSION = "3.1.5" # latest is 4.1.0
CGAL_VERSION = "5.3"
# binaries
cp="cp"
bash="bash"
uname="uname"
git="git"
bunzip2="bunzip2"
tar="tar"
cc="cc"
cplusplus="c++"
autoconf="autoconf"
automake="automake"
yacc="yacc"
make="make"
cp = "cp"
bash = "bash"
git = "git"
bunzip2 = "bunzip2"
tar = "tar"
cc = "cc"
cplusplus = "c++"
autoconf = "autoconf"
automake = "automake"
yacc = "yacc"
make = "make"
date = "date"
curl="curl"
wget="wget"
strip="strip"
curl = "curl"
wget = "wget"
strip = "strip"
# Helper function for coloured printing
NO_COLOR="\033[0m" # <ref>http://stackoverflow.com/questions/5947742/how-to-change-the-output-color-of-echo-in-linux</ref>
BLACK_ON_WHITE="\033[0;30;107m"
RED="\033[31m"
GREEN="\033[32m"
YELLOW="\033[33m"
MAGENTA="\033[35m"
NO_COLOR = "\033[0m" # <ref>http://stackoverflow.com/questions/5947742/how-to-change-the-output-color-of-echo-in-linux</ref>
BLACK_ON_WHITE = "\033[0;30;107m"
RED = "\033[31m"
GREEN = "\033[32m"
YELLOW = "\033[33m"
MAGENTA = "\033[35m"
def cecho(message, color=NO_COLOR):
"""Logs message `message` in color `color`."""
logger.info("%s%s\033[0m" % (color, message))
logger.info(f"{color}{message}\033[0m")
def which(cmd):
for path in os.environ["PATH"].split(":"):
for path in os.getenv("PATH").split(":"):
if os.path.exists(path) and cmd in os.listdir(path):
return cmd
return None
def get_os():
ret_value = sp.check_output([uname, "-s"]).strip()
return ret_value
def to_pystring(x):
""" Python 2 & 3 compatibility function for strings handling
(to solve TypeError "Can't mix strings and bytes in path components" for Python 3).
Reference https://github.com/hugsy/gef/issues/382 """
res = str(x, encoding="utf-8") if PYTHON_MAJOR == 3 else x
substs = [("\n","\\n"), ("\r","\\r"), ("\t","\\t"), ("\v","\\v"), ("\b","\\b"), ]
for x,y in substs: res = res.replace(x,y)
return res
# Set defaults for missing empty environment variables
USE_OCCT = os.environ.get("USE_OCCT", "true").lower() == "true"
TOOLSET = None
if get_os() == "Darwin":
if platform.system() == "Darwin":
# C++11 features used in OCCT 7+ need a more recent stdlib
TOOLSET = "10.9" if USE_OCCT else "10.6"
try:
IFCOS_NUM_BUILD_PROCS = os.environ["IFCOS_NUM_BUILD_PROCS"]
except KeyError:
IFCOS_NUM_BUILD_PROCS=multiprocessing.cpu_count() + 1
try:
TARGET_ARCH = os.environ["TARGET_ARCH"]
except KeyError:
TARGET_ARCH = sp.check_output([uname, "-m"]).strip()
IFCOS_NUM_BUILD_PROCS = os.getenv("IFCOS_NUM_BUILD_PROCS", multiprocessing.cpu_count() + 1)
CMAKE_DIR=os.path.realpath(os.path.join("..", "cmake"))
CMAKE_DIR = os.path.realpath(os.path.join("..", "cmake"))
try:
DEPS_DIR = os.environ["DEPS_DIR"]
except KeyError:
path = [b"..", b"build", sp.check_output(uname).strip(), TARGET_ARCH]
if TOOLSET:
path.append(TOOLSET)
DEPS_DIR = to_pystring(os.path.realpath(os.path.join(*path)))
path = ["..", "build", platform.system(), platform.machine()]
if TOOLSET:
path.append(TOOLSET)
DEFAULT_DEPS_DIR = os.path.realpath(os.path.join(*path))
DEPS_DIR = os.getenv("DEPS_DIR", DEFAULT_DEPS_DIR)
if not os.path.exists(DEPS_DIR):
os.makedirs(DEPS_DIR)
try:
BUILD_CFG=os.environ["BUILD_CFG"]
except KeyError:
BUILD_CFG="RelWithDebInfo"
BUILD_CFG = os.getenv("BUILD_CFG", "RelWithDebInfo")
# Print build configuration information
cecho ("""This script fetches and builds %s and its dependencies
""" % (PROJECT_NAME,), BLACK_ON_WHITE)
cecho (f"""This script fetches and builds {PROJECT_NAME} and its dependencies
""", BLACK_ON_WHITE)
cecho("""Script configuration:
""", GREEN)
cecho("""* Target Architecture = %s""" % (TARGET_ARCH,), MAGENTA)
cecho(" - Whether 32-bit (i686) or 64-bit (x86_64) will be built.")
cecho("""* USE_OCCT = %r""" % (USE_OCCT,), MAGENTA)
cecho(f"""* USE_OCCT = {USE_OCCT}""", MAGENTA)
if USE_OCCT:
cecho(" - Compiling against official Open Cascade")
else:
cecho(" - Compiling against Open Cascade Community Edition")
cecho("* Dependency Directory = %s" % (DEPS_DIR,), MAGENTA)
cecho(" - The directory where %s dependencies are installed." % (PROJECT_NAME,))
cecho("* Build Config Type = %s" % (BUILD_CFG,), MAGENTA)
cecho(f"* Dependency Directory = {DEPS_DIR}", MAGENTA)
cecho(f" - The directory where {PROJECT_NAME} dependencies are installed.")
cecho(f"* Build Config Type = {BUILD_CFG}", MAGENTA)
cecho(""" - The used build configuration type for the dependencies.
Defaults to RelWithDebInfo if not specified.""")
if BUILD_CFG == "MinSizeRel":
cecho(" WARNING: MinSizeRel build can suffer from a significant performance loss.", RED)
cecho("* IFCOS_NUM_BUILD_PROCS = %s" % (IFCOS_NUM_BUILD_PROCS,), MAGENTA)
cecho(f"* IFCOS_NUM_BUILD_PROCS = {IFCOS_NUM_BUILD_PROCS}", MAGENTA)
cecho(""" - How many compiler processes may be run in parallel.
""")
dependency_tree = {
'IfcParse': ('boost', 'libxml2', 'hdf5'),
'IfcGeom': ('IfcParse', 'occ', 'OpenCOLLADA', 'json', 'cgal'),
'IfcConvert': ('IfcGeom', ),
'OpenCOLLADA': ('libxml2', 'pcre'),
'IfcParse': ('boost', 'libxml2', 'hdf5'),
'IfcGeom': ('IfcParse', 'occ', 'json', 'cgal'),
'IfcConvert': ('IfcGeom',),
'OpenCOLLADA': ('libxml2', 'pcre'),
'IfcGeomServer': ('IfcGeom',),
'IfcOpenShell-Python': ('python', 'swig', 'IfcGeom'),
'IfcOpenShell-Python': ('python', 'swig', 'IfcGeom'),
'swig': ('pcre',),
'boost': (),
'libxml2': (),
@@ -252,7 +222,7 @@ print("Building:", *sorted(targets, key=lambda t: len(list(v(t)))))
for cmd in [git, bunzip2, tar, cc, cplusplus, autoconf, automake, yacc, make, "patch", "cmake"]:
if which(cmd) is None:
raise ValueError("Required tool '%s' not installed or not added to PATH" % (cmd,))
raise ValueError(f"Required tool '{cmd}' not installed or not added to PATH")
# identifiers for the download tool (could be less memory consuming as ints, but are more verbose as strings)
download_tool_default = download_tool_py = "py"
@@ -263,10 +233,10 @@ download_tool_git = "git"
log_dir = os.path.join(DEPS_DIR, "logs")
if not os.path.exists(log_dir):
os.makedirs(log_dir)
LOG_FILE="%s.log" % (os.path.join(log_dir, to_pystring(sp.check_output([date, "+%Y%m%d"]).strip())),)
LOG_FILE = os.path.join(log_dir, sp.check_output([date, "+%Y%m%d"], encoding="utf-8").strip()) + ".log"
if not os.path.exists(LOG_FILE):
open(LOG_FILE, "w").close()
logger.info("using command log file '%s'" % (LOG_FILE,))
logger.info(f"using command log file '{LOG_FILE}'")
def run(cmds, cwd=None):
@@ -276,43 +246,47 @@ def run(cmds, cwd=None):
with leading and trailing whitespace removed.
"""
logger.debug("running command %r in directory %r" % (" ".join(cmds), cwd))
log_file_handle = open(LOG_FILE, "ab")
proc = sp.Popen(cmds, cwd=cwd, stdout=sp.PIPE, stderr=sp.PIPE)
logger.debug(f"running command {' '.join(cmds)} in directory {cwd}")
log_file_handle = open(LOG_FILE, "a")
proc = sp.Popen(cmds, cwd=cwd, stdout=sp.PIPE, stderr=sp.PIPE, encoding="utf-8")
stdout, stderr = proc.communicate()
log_file_handle.write(stdout)
log_file_handle.write(stderr)
log_file_handle.close()
logger.debug("command returned %r" % proc.returncode)
logger.debug(f"command returned {proc.returncode}")
if proc.returncode != 0:
print("-" * 70)
print(stderr)
print("-" * 70)
raise Exception("Command `%s` returned exit code %d" % (" ".join(cmds), proc.returncode))
raise RuntimeError(f"Command `{' '.join(cmds)}` returned exit code {proc.returncode}")
return stdout.strip()
BOOST_VERSION_UNDERSCORE=BOOST_VERSION.replace(".", "_")
BOOST_VERSION_UNDERSCORE = BOOST_VERSION.replace(".", "_")
OCE_LOCATION="https://github.com/tpaviot/oce/archive/OCE-%s.tar.gz" % (OCE_VERSION,)
BOOST_LOCATION="https://boostorg.jfrog.io/artifactory/main/release/%s/source/" % (BOOST_VERSION,)
OCE_LOCATION = f"https://github.com/tpaviot/oce/archive/OCE-{OCE_VERSION}.tar.gz"
BOOST_LOCATION = f"https://boostorg.jfrog.io/artifactory/main/release/{BOOST_VERSION}/source/"
# Helper functions
def run_autoconf(arg1, configure_args, cwd):
configure_path = os.path.realpath(os.path.join(cwd, "..", "configure"))
if not os.path.exists(configure_path):
run([bash, "./autogen.sh"], cwd=os.path.realpath(os.path.join(cwd, ".."))) # only run autogen.sh in the directory it is located and use cwd to achieve that in order to not mess up things
# Using `sh` over `bash` fixes issues with building swig
run(["/bin/sh", "../configure"]+configure_args+["--prefix=%s" % (os.path.realpath("%s/install/%s" % (DEPS_DIR, arg1)),)], cwd=cwd)
prefix = os.path.realpath(f"{DEPS_DIR}/install/{arg1}")
run(["/bin/sh", "../configure"] + configure_args + [f"--prefix={prefix}"], cwd=cwd)
def run_cmake(arg1, cmake_args, cmake_dir=None, cwd=None):
if cmake_dir is None:
P=".."
P = ".."
else:
P=cmake_dir
run(["cmake", P]+cmake_args+["-DCMAKE_BUILD_TYPE=%s" % (BUILD_CFG,)], cwd=cwd)
P = cmake_dir
run(["cmake", P] + cmake_args + [f"-DCMAKE_BUILD_TYPE={BUILD_CFG}"], cwd=cwd)
def git_clone_or_pull_repository(clone_url, target_dir, revision=None):
"""Lazily clones the `git` repository denoted by `clone_url` into
@@ -321,10 +295,10 @@ def git_clone_or_pull_repository(clone_url, target_dir, revision=None):
`revision` after cloning or in the existing clone if `revision` is not
`None`."""
if not os.path.exists(target_dir):
logger.info("cloning '%s' into '%s'" % (clone_url, target_dir))
logger.info(f"cloning '{clone_url}' into '{target_dir}'")
run([git, "clone", "--recursive", clone_url, target_dir])
else:
logger.info("directory '%s' already cloned. Pulling latest changes." % (target_dir,))
logger.info(f"directory '{target_dir}' already cloned. Pulling latest changes.")
# detect whether we are on a branch and pull
if run([git, "rev-parse", "--abbrev-ref", "HEAD"], cwd=target_dir) != "HEAD":
@@ -341,13 +315,13 @@ def build_dependency(name, mode, build_tool_args, download_url, download_name, d
linker flags."""
check_dir = os.path.join(DEPS_DIR, "install", name)
if os.path.exists(check_dir):
logger.info( "Found existing %s, skipping" % (name,))
logger.info(f"Found existing {name}, skipping")
return
build_dir = os.path.join(DEPS_DIR, "build")
if not os.path.exists(build_dir):
os.makedirs(build_dir)
logger.info("\rFetching %s... " % (name,))
logger.info(f"\rFetching {name}... ")
if download_tool == download_tool_py:
if no_append_name:
@@ -359,16 +333,16 @@ def build_dependency(name, mode, build_tool_args, download_url, download_name, d
if not os.path.exists(download_path):
urlretrieve(url, os.path.join(build_dir, download_path))
else:
logger.info("Download '%s' already exists, assuming it's an undamaged download and that it has been extracted if possible, skipping" % (download_path,))
logger.info(f"Download '{download_path}' already exists, assuming it's an undamaged download and that it has been extracted if possible, skipping")
elif download_tool == download_tool_git:
logger.info("\rChecking %s... " % (name,))
logger.info(f"\rChecking {name}... ")
git_clone_or_pull_repository(download_url, target_dir=os.path.join(build_dir, download_name), revision=revision)
else:
raise ValueError("download tool '%s' is not supported" % (download_tool,))
raise ValueError(f"download tool '{download_tool}' is not supported")
download_dir = os.path.join(build_dir, download_name)
if os.path.isdir(download_dir):
extract_dir_name=download_name
extract_dir_name = download_name
extract_dir = os.path.join(build_dir, extract_dir_name)
else:
download_tarfile_path = os.path.join(build_dir, download_name)
@@ -378,12 +352,12 @@ def build_dependency(name, mode, build_tool_args, download_url, download_name, d
compr = "bz2"
else:
raise RuntimeError("fix source for new download type")
download_tarfile = tarfile.open(name=download_tarfile_path, mode="r:%s" % (compr,))
download_tarfile = tarfile.open(name=download_tarfile_path, mode=f"r:{compr}")
# tarfile seriously doesn't have a function to retrieve the root directory more easily
extract_dir_name= os.path.commonprefix([x for x in download_tarfile.getnames() if x != "."])
extract_dir_name = os.path.commonprefix([x for x in download_tarfile.getnames() if x != "."])
#run([tar, "--exclude=\"*/*\"", "-tf", download_name], cwd=build_dir).strip() no longer works
if extract_dir_name is None:
extract_dir_name= run([bash, "-c", "tar -tf %s 2> /dev/null | head -n 1 | cut -f1 -d /" % (download_name,)], cwd=build_dir)
extract_dir_name = run([bash, "-c", f"tar -tf {download_name} 2> /dev/null | head -n 1 | cut -f1 -d /"], cwd=build_dir)
extract_dir = os.path.join(build_dir, extract_dir_name)
if not os.path.exists(extract_dir):
run([tar, "-xf", download_name], cwd=build_dir)
@@ -413,71 +387,66 @@ def build_dependency(name, mode, build_tool_args, download_url, download_name, d
shutil.rmtree(extract_build_dir)
os.makedirs(extract_build_dir)
logger.info("\rConfiguring %s..." % (name,))
logger.info(f"\rConfiguring {name}...")
if mode == "autoconf":
run_autoconf(name, build_tool_args, cwd=extract_build_dir)
elif mode == "cmake":
run_cmake(name, build_tool_args, cwd=extract_build_dir)
else:
raise ValueError()
logger.info("\rBuilding %s... " % (name,))
run([make, "-j%s" % (IFCOS_NUM_BUILD_PROCS,), "VERBOSE=1"], cwd=extract_build_dir)
logger.info( "\rInstalling %s... " % (name,))
logger.info(f"\rBuilding {name}... ")
run([make, f"-j{IFCOS_NUM_BUILD_PROCS}", "VERBOSE=1"], cwd=extract_build_dir)
logger.info(f"\rInstalling {name}... ")
run([make, "install"], cwd=extract_build_dir)
logger.info( "\rInstalled %s \n" % (name,))
logger.info(f"\rInstalled {name} \n")
else:
logger.info( "\rConfiguring %s..." % (name,))
logger.info(f"\rConfiguring {name}...")
run([bash, "./bootstrap.sh"], cwd=extract_dir)
logger.info("\rBuilding %s... " % (name,))
run(["./b2", "-j%s" % (IFCOS_NUM_BUILD_PROCS,)]+build_tool_args, cwd=extract_dir)
logger.info("\rInstalling %s... " % (name,))
shutil.copytree(os.path.join(extract_dir, "boost"), os.path.join(DEPS_DIR, "install", "boost-%s" % BOOST_VERSION, "boost"))
logger.info("\rInstalled %s \n" % (name,))
logger.info(f"\rBuilding {name}... ")
run(["./b2", f"-j{IFCOS_NUM_BUILD_PROCS}"] + build_tool_args, cwd=extract_dir)
logger.info(f"\rInstalling {name}... ")
shutil.copytree(os.path.join(extract_dir, "boost"), os.path.join(DEPS_DIR, "install", f"boost-{BOOST_VERSION}", "boost"))
logger.info(f"\rInstalled {name} \n")
cecho("Collecting dependencies:", GREEN)
# Set compiler flags for 32bit builds on 64bit system
# TODO: This is untested
ADDITIONAL_ARGS=[]
BOOST_ADDRESS_MODEL=[]
if TARGET_ARCH == "i686" and run([uname, "-m"]).strip() == "x86_64":
if get_os() == "Darwin":
ADDITIONAL_ARGS=["-m32", "-arch i386"]
else:
ADDITIONAL_ARGS=["-m32"]
BOOST_ADDRESS_MODEL=["architecture=x86", "address-model=32"]
ADDITIONAL_ARGS = []
BOOST_ADDRESS_MODEL = []
if get_os() == "Darwin":
ADDITIONAL_ARGS=["-mmacosx-version-min=%s" % TOOLSET]+ADDITIONAL_ARGS
if platform.system() == "Darwin":
ADDITIONAL_ARGS = [f"-mmacosx-version-min={TOOLSET}"] + ADDITIONAL_ARGS
# If the linker supports GC sections, set it up to reduce binary file size
# -fPIC is required for the shared libraries to work
CXXFLAGS=os.environ.get("CXXFLAGS", "")
CFLAGS=os.environ.get("CFLAGS", "")
LDFLAGS=os.environ.get("LDFLAGS", "")
CXXFLAGS = os.environ.get("CXXFLAGS", "")
CFLAGS = os.environ.get("CFLAGS", "")
LDFLAGS = os.environ.get("LDFLAGS", "")
ADDITIONAL_ARGS_STR = " ".join(ADDITIONAL_ARGS)
if sp.call([bash, "-c", "ld --gc-sections 2>&1 | grep -- --gc-sections &> /dev/null"]) != 0:
CXXFLAGS_MINIMAL="%s %s %s" % (CXXFLAGS, PIC, str.join(" ", ADDITIONAL_ARGS))
CFLAGS_MINIMAL="%s %s %s" % (CFLAGS, PIC, str.join(" ", ADDITIONAL_ARGS))
CXXFLAGS_MINIMAL = f"{CXXFLAGS} {PIC} {ADDITIONAL_ARGS_STR}"
CFLAGS_MINIMAL = f"{CFLAGS} {PIC} {ADDITIONAL_ARGS_STR}"
if BUILD_STATIC:
CXXFLAGS="%s %s -fdata-sections -ffunction-sections -fvisibility=hidden -fvisibility-inlines-hidden %s" % (CXXFLAGS, PIC, str.join(" ", ADDITIONAL_ARGS))
CFLAGS="%s %s -fdata-sections -ffunction-sections -fvisibility=hidden %s"% (CFLAGS, PIC, str.join(" ", ADDITIONAL_ARGS))
CXXFLAGS = f"{CXXFLAGS} {PIC} -fdata-sections -ffunction-sections -fvisibility=hidden -fvisibility-inlines-hidden {ADDITIONAL_ARGS_STR}"
CFLAGS = f"{CFLAGS} {PIC} -fdata-sections -ffunction-sections -fvisibility=hidden {ADDITIONAL_ARGS_STR}"
else:
CXXFLAGS=CXXFLAGS_MINIMAL
CFLAGS=CFLAGS_MINIMAL
LDFLAGS="%s -Wl,--gc-sections %s" % (LDFLAGS, str.join(" ", ADDITIONAL_ARGS))
CXXFLAGS = CXXFLAGS_MINIMAL
CFLAGS = CFLAGS_MINIMAL
LDFLAGS = f"{LDFLAGS} -Wl,--gc-sections {ADDITIONAL_ARGS_STR}"
else:
CXXFLAGS_MINIMAL="%s %s %s" % (CXXFLAGS, PIC, str.join(" ", ADDITIONAL_ARGS))
CFLAGS_MINIMAL="%s %s %s" % (CFLAGS, PIC, str.join(" ", ADDITIONAL_ARGS))
CXXFLAGS_MINIMAL = f"{CXXFLAGS} {PIC} {ADDITIONAL_ARGS_STR}"
CFLAGS_MINIMAL = f"{CFLAGS} {PIC} {ADDITIONAL_ARGS_STR}"
if BUILD_STATIC:
CXXFLAGS="%s %s -fvisibility=hidden -fvisibility-inlines-hidden %s" % (CXXFLAGS, PIC, str.join(" ", ADDITIONAL_ARGS))
CFLAGS="%s %s -fvisibility=hidden -fvisibility-inlines-hidden %s" % (CFLAGS, PIC, str.join(" ", ADDITIONAL_ARGS))
CXXFLAGS = f"{CXXFLAGS} {PIC} -fvisibility=hidden -fvisibility-inlines-hidden {ADDITIONAL_ARGS_STR}"
CFLAGS = f"{CFLAGS} {PIC} -fvisibility=hidden -fvisibility-inlines-hidden {ADDITIONAL_ARGS_STR}"
else:
CXXFLAGS=CXXFLAGS_MINIMAL
CFLAGS=CFLAGS_MINIMAL
LDFLAGS="%s %s" % (LDFLAGS, str.join(" ", ADDITIONAL_ARGS))
LDFLAGS = f"{LDFLAGS} {ADDITIONAL_ARGS_STR}"
os.environ["CXXFLAGS"] = CXXFLAGS
os.environ["CFLAGS"] = CFLAGS
@@ -488,21 +457,20 @@ os.environ["LDFLAGS"] = LDFLAGS
# build_dependency(name="cmake-%s" % (CMAKE_VERSION,), mode="autoconf", build_tool_args=[], download_url="https://cmake.org/files/v%s" % (CMAKE_VERSION_2,), download_name="cmake-%s.tar.gz" % (CMAKE_VERSION,))
if 'hdf5' in targets:
HDF5_MAJOR=".".join(HDF5_VERSION.split(".")[:-1])
get_os_result = get_os()
HDF5_MAJOR = ".".join(HDF5_VERSION.split(".")[:-1])
build_dependency(
name="hdf5-{HDF5_VERSION}".format(**locals()),
name=f"hdf5-{HDF5_VERSION}",
mode="ctest",
build_tool_args=[],
download_url="https://support.hdfgroup.org/ftp/HDF5/releases/hdf5-{HDF5_MAJOR}/hdf5-{HDF5_VERSION}/src/".format(**locals()),
download_name="CMake-hdf5-{HDF5_VERSION}.tar.gz".format(**locals()),
ctest_result="HDF5-{HDF5_VERSION}-{get_os_result}".format(**locals()),
ctest_result_path="HDF_Group/HDF5/{HDF5_VERSION}".format(**locals())
download_url=f"https://support.hdfgroup.org/ftp/HDF5/releases/hdf5-{HDF5_MAJOR}/hdf5-{HDF5_VERSION}/src/",
download_name=f"CMake-hdf5-{HDF5_VERSION}.tar.gz",
ctest_result=f"HDF5-{HDF5_VERSION}-{platform.system()}",
ctest_result_path=f"HDF_Group/HDF5/{HDF5_VERSION}"
)
if "json" in targets:
json_url = "https://github.com/nlohmann/json/releases/download/{JSON_VERSION}/json.hpp".format(**locals())
json_install_path = "{DEPS_DIR}/install/json/nlohmann/json.hpp".format(**locals())
json_url = f"https://github.com/nlohmann/json/releases/download/{JSON_VERSION}/json.hpp"
json_install_path = f"{DEPS_DIR}/install/json/nlohmann/json.hpp"
if not os.path.exists(os.path.dirname(json_install_path)):
os.makedirs(os.path.dirname(json_install_path))
if not os.path.exists(json_install_path):
@@ -510,11 +478,11 @@ if "json" in targets:
if "pcre" in targets:
build_dependency(
name="pcre-{PCRE_VERSION}".format(**locals()),
name=f"pcre-{PCRE_VERSION}",
mode="autoconf",
build_tool_args=[DISABLE_FLAG],
download_url="https://downloads.sourceforge.net/project/pcre/pcre/{PCRE_VERSION}/".format(**locals()),
download_name="pcre-{PCRE_VERSION}.tar.bz2".format(**locals())
download_url=f"https://downloads.sourceforge.net/project/pcre/pcre/{PCRE_VERSION}/",
download_name=f"pcre-{PCRE_VERSION}.tar.bz2"
)
# An issue exists with swig-1.3 and python >= 3.2
@@ -523,20 +491,20 @@ if "swig" in targets:
build_dependency(
name="swig",
mode="autoconf",
build_tool_args=["--disable-ccache", "--with-pcre-prefix={DEPS_DIR}/install/pcre-{PCRE_VERSION}".format(**locals())],
build_tool_args=["--disable-ccache", f"--with-pcre-prefix={DEPS_DIR}/install/pcre-{PCRE_VERSION}"],
download_url="https://github.com/swig/swig.git",
download_name="swig",
download_tool=download_tool_git,
revision="rel-{SWIG_VERSION}".format(**locals())
revision=f"rel-{SWIG_VERSION}"
)
if USE_OCCT and "occ" in targets:
build_dependency(
name="occt-{OCCT_VERSION}".format(**locals()),
name=f"occt-{OCCT_VERSION}",
mode="cmake",
build_tool_args=[
"-DINSTALL_DIR={DEPS_DIR}/install/occt-{OCCT_VERSION}".format(**locals()),
"-DBUILD_LIBRARY_TYPE={LINK_TYPE_UCFIRST}".format(**locals()),
f"-DINSTALL_DIR={DEPS_DIR}/install/occt-{OCCT_VERSION}",
f"-DBUILD_LIBRARY_TYPE={LINK_TYPE_UCFIRST}",
"-DBUILD_MODULE_Draw=0",
"-DBUILD_RELEASE_DISABLE_EXCEPTIONS=Off"
],
@@ -548,7 +516,7 @@ if USE_OCCT and "occ" in targets:
)
elif "occ" in targets:
build_dependency(
name="oce-{OCE_VERSION}".format(**locals()),
name=f"oce-{OCE_VERSION}",
mode="cmake",
build_tool_args=[
"-DOCE_DISABLE_TKSERVICE_FONT=ON",
@@ -557,15 +525,15 @@ elif "occ" in targets:
"-DOCE_DISABLE_X11=ON",
"-DOCE_VISUALISATION=OFF",
"-DOCE_OCAF=OFF",
"-DOCE_INSTALL_PREFIX={DEPS_DIR}/install/oce-{OCE_VERSION}".format(**locals())
f"-DOCE_INSTALL_PREFIX={DEPS_DIR}/install/oce-{OCE_VERSION}"
],
download_url="https://github.com/tpaviot/oce/archive/",
download_name="OCE-{OCE_VERSION}.tar.gz".format(**locals())
download_name=f"OCE-{OCE_VERSION}.tar.gz"
)
if "libxml2" in targets:
build_dependency(
"libxml2-{LIBXML2_VERSION}".format(**locals()),
f"libxml2-{LIBXML2_VERSION}",
"autoconf",
build_tool_args=[
"--without-python",
@@ -576,7 +544,7 @@ if "libxml2" in targets:
"--without-lzma"
],
download_url="ftp://xmlsoft.org/libxml2/",
download_name="libxml2-{LIBXML2_VERSION}.tar.gz".format(**locals())
download_name=f"libxml2-{LIBXML2_VERSION}.tar.gz"
)
if "OpenCOLLADA" in targets:
@@ -584,12 +552,12 @@ if "OpenCOLLADA" in targets:
"OpenCOLLADA",
"cmake",
build_tool_args=[
"-DLIBXML2_INCLUDE_DIR={DEPS_DIR}/install/libxml2-{LIBXML2_VERSION}/include/libxml2".format(**locals()),
"-DLIBXML2_LIBRARIES={DEPS_DIR}/install/libxml2-{LIBXML2_VERSION}/lib/libxml2.{LIBRARY_EXT}".format(**locals()),
"-DPCRE_INCLUDE_DIR={DEPS_DIR}/install/pcre-{PCRE_VERSION}/include".format(**locals()),
"-DPCRE_PCREPOSIX_LIBRARY={DEPS_DIR}/install/pcre-{PCRE_VERSION}/lib/libpcreposix.{LIBRARY_EXT}".format(**locals()),
"-DPCRE_PCRE_LIBRARY={DEPS_DIR}/install/pcre-{PCRE_VERSION}/lib/libpcre.{LIBRARY_EXT}".format(**locals()),
"-DCMAKE_INSTALL_PREFIX={DEPS_DIR}/install/OpenCOLLADA/".format(**locals())
f"-DLIBXML2_INCLUDE_DIR={DEPS_DIR}/install/libxml2-{LIBXML2_VERSION}/include/libxml2",
f"-DLIBXML2_LIBRARIES={DEPS_DIR}/install/libxml2-{LIBXML2_VERSION}/lib/libxml2.{LIBRARY_EXT}",
f"-DPCRE_INCLUDE_DIR={DEPS_DIR}/install/pcre-{PCRE_VERSION}/include",
f"-DPCRE_PCREPOSIX_LIBRARY={DEPS_DIR}/install/pcre-{PCRE_VERSION}/lib/libpcreposix.{LIBRARY_EXT}",
f"-DPCRE_PCRE_LIBRARY={DEPS_DIR}/install/pcre-{PCRE_VERSION}/lib/libpcre.{LIBRARY_EXT}",
f"-DCMAKE_INSTALL_PREFIX={DEPS_DIR}/install/OpenCOLLADA/"
],
download_url="https://github.com/KhronosGroup/OpenCOLLADA.git",
download_name="OpenCOLLADA",
@@ -598,158 +566,155 @@ if "OpenCOLLADA" in targets:
revision=OPENCOLLADA_VERSION
)
if "python" in targets:
if "python" in targets and not USE_CURRENT_PYTHON_VERSION:
# Python should not be built with -fvisibility=hidden, from experience that introduces segfaults
OLD_CXX_FLAGS=os.environ["CXXFLAGS"]
OLD_C_FLAGS=os.environ["CFLAGS"]
os.environ["CXXFLAGS"]=CXXFLAGS_MINIMAL
os.environ["CFLAGS"]=CFLAGS_MINIMAL
OLD_CXX_FLAGS = os.environ["CXXFLAGS"]
OLD_C_FLAGS = os.environ["CFLAGS"]
os.environ["CXXFLAGS"] = CXXFLAGS_MINIMAL
os.environ["CFLAGS"] = CFLAGS_MINIMAL
# On OSX a dynamic python library is built or it would not be compatible
# with the system python because of some threading initialization
PYTHON_CONFIGURE_ARGS=[]
if get_os() == "Darwin":
PYTHON_CONFIGURE_ARGS=["--disable-static", "--enable-shared"]
PYTHON_CONFIGURE_ARGS = []
if platform.system() == "Darwin":
PYTHON_CONFIGURE_ARGS = ["--disable-static", "--enable-shared"]
def get_python_unicode_confs(py_ver):
if py_ver < "3.3":
return [("--enable-unicode=ucs2",""), ("--enable-unicode=ucs4","u")]
else: return [("","")]
def PYTHON_VERSION_CONFS():
for v in PYTHON_VERSIONS:
for unicode_conf, abi_tag in get_python_unicode_confs(v):
yield v, unicode_conf, abi_tag
for PYTHON_VERSION, unicode_conf, abi_tag in PYTHON_VERSION_CONFS():
for PYTHON_VERSION in PYTHON_VERSIONS:
try:
build_dependency(
"python-{PYTHON_VERSION}{abi_tag}".format(**locals()),
f"python-{PYTHON_VERSION}",
"autoconf",
PYTHON_CONFIGURE_ARGS + [unicode_conf],
"http://www.python.org/ftp/python/{PYTHON_VERSION}/".format(**locals()),
"Python-{PYTHON_VERSION}.tgz".format(**locals())
PYTHON_CONFIGURE_ARGS,
f"http://www.python.org/ftp/python/{PYTHON_VERSION}/",
f"Python-{PYTHON_VERSION}.tgz"
)
except Exception as e:
pyver2 = PYTHON_VERSION[:PYTHON_VERSION.rfind('.')]
if os.path.exists("{DEPS_DIR}/install/python-{PYTHON_VERSION}{abi_tag}/bin/python{pyver2}".format(**locals())):
print("Installation partially failed")
else: raise e
except RuntimeError as e:
# Sometimes setting up modules such as pip/lzma can cause
# the python installer script to return a non zero exit
# code where actually the headers and dynamic libraries
# are installed correctly. This is all we need so we catch
# the exception and only reraise if a partially successful
# install is not detected.
if not os.path.exists(
os.path.join(DEPS_DIR, "install", f"python-{PYTHON_VERSION}")
):
raise e
os.environ["CXXFLAGS"]=OLD_CXX_FLAGS
os.environ["CFLAGS"]=OLD_C_FLAGS
os.environ["CXXFLAGS"] = OLD_CXX_FLAGS
os.environ["CFLAGS"] = OLD_C_FLAGS
if "boost" in targets:
str_concat = lambda prefix: lambda postfix: "" if postfix.strip() == "" else "=".join((prefix, postfix.strip()))
build_dependency(
"boost-{BOOST_VERSION}".format(**locals()),
f"boost-{BOOST_VERSION}",
mode="bjam",
build_tool_args=[
"--stagedir={DEPS_DIR}/install/boost-{BOOST_VERSION}".format(**locals()),
f"--stagedir={DEPS_DIR}/install/boost-{BOOST_VERSION}",
"--with-system",
"--with-program_options",
"--with-regex",
"--with-thread",
"--with-date_time",
"--with-iostreams",
"link={LINK_TYPE}".format(**locals())
f"link={LINK_TYPE}"
] + \
BOOST_ADDRESS_MODEL + \
list(map(str_concat("cxxflags"), CXXFLAGS.strip().split(' '))) + \
list(map(str_concat("linkflags"), LDFLAGS.strip().split(' '))) + \
["stage", "-s", "NO_BZIP2=1"],
download_url=BOOST_LOCATION,
download_name="boost_{BOOST_VERSION_UNDERSCORE}.tar.bz2".format(**locals())
download_name=f"boost_{BOOST_VERSION_UNDERSCORE}.tar.bz2"
)
if "cgal" in targets:
build_dependency(
name="gmp-%s" % (GMP_VERSION,),
name=f"gmp-{GMP_VERSION}",
mode="autoconf",
build_tool_args=["--disable-shared", "--with-pic"],
download_url="https://ftp.gnu.org/gnu/gmp/",
download_name="gmp-{GMP_VERSION}.tar.bz2".format(**locals())
download_name=f"gmp-{GMP_VERSION}.tar.bz2"
)
build_dependency(
name="mpfr-%s" % (MPFR_VERSION,),
name=f"mpfr-{MPFR_VERSION}",
mode="autoconf",
build_tool_args=["--disable-shared", "--with-gmp={DEPS_DIR}/install/gmp-{GMP_VERSION}".format(**locals())],
download_url="http://www.mpfr.org/mpfr-{MPFR_VERSION}/".format(**locals()),
download_name="mpfr-{MPFR_VERSION}.tar.bz2".format(**locals())
build_tool_args=["--disable-shared", f"--with-gmp={DEPS_DIR}/install/gmp-{GMP_VERSION}"],
download_url=f"http://www.mpfr.org/mpfr-{MPFR_VERSION}/",
download_name=f"mpfr-{MPFR_VERSION}.tar.bz2"
)
build_dependency(
name="cgal-{CGAL_VERSION}".format(**locals()),
name=f"cgal-{CGAL_VERSION}",
mode="cmake",
build_tool_args=[
"-DGMP_LIBRARIES=%s/install/gmp-%s/lib/libgmp.a" % (DEPS_DIR, GMP_VERSION),
"-DGMP_INCLUDE_DIR=%s/install/gmp-%s/include" % (DEPS_DIR, GMP_VERSION),
"-DMPFR_LIBRARIES=%s/install/mpfr-%s/lib/libmpfr.a" % (DEPS_DIR, MPFR_VERSION),
"-DMPFR_INCLUDE_DIR=%s/install/mpfr-%s/include" % (DEPS_DIR, MPFR_VERSION),
"-DBoost_INCLUDE_DIR=%s/install/boost-%s" % (DEPS_DIR, BOOST_VERSION),
"-DCMAKE_INSTALL_PREFIX=%s/install/cgal-%s/" % (DEPS_DIR, CGAL_VERSION),
f"-DGMP_LIBRARIES={DEPS_DIR}/install/gmp-{GMP_VERSION}/lib/libgmp.a",
f"-DGMP_INCLUDE_DIR={DEPS_DIR}/install/gmp-{GMP_VERSION}/include",
f"-DMPFR_LIBRARIES={DEPS_DIR}/install/mpfr-{MPFR_VERSION}/lib/libmpfr.a" ,
f"-DMPFR_INCLUDE_DIR={DEPS_DIR}/install/mpfr-{MPFR_VERSION}/include",
f"-DBoost_INCLUDE_DIR={DEPS_DIR}/install/boost-{BOOST_VERSION}",
f"-DCMAKE_INSTALL_PREFIX={DEPS_DIR}/install/cgal-{CGAL_VERSION}/",
"-DCGAL_HEADER_ONLY=On",
"-DBUILD_SHARED_LIBS=Off"
],
download_url="https://github.com/CGAL/cgal.git",
download_name="cgal",
download_tool=download_tool_git,
revision="v{CGAL_VERSION}".format(**locals())
revision=f"v{CGAL_VERSION}"
)
cecho("Building IfcOpenShell:", GREEN)
IFCOS_DIR=os.path.join(DEPS_DIR, "build", "ifcopenshell")
if os.path.exists(IFCOS_DIR):
shutil.rmtree(IFCOS_DIR)
os.makedirs(IFCOS_DIR)
IFCOS_DIR = os.path.join(DEPS_DIR, "build", "ifcopenshell")
if os.environ.get("NO_CLEAN", "").lower() not in {"1", "on", "true"}:
if os.path.exists(IFCOS_DIR):
shutil.rmtree(IFCOS_DIR)
os.makedirs(IFCOS_DIR, exist_ok=True)
executables_dir = os.path.join(IFCOS_DIR, "executables")
if not os.path.exists(executables_dir):
os.makedirs(executables_dir)
os.makedirs(executables_dir, exist_ok=True)
logger.info("\rConfiguring executables...")
OFF_ON = ["OFF", "ON"]
exec_args=[
"-DBUILD_IFCGEOM=" +OFF_ON["IfcGeom" in targets],
"-DBUILD_GEOMSERVER=" +OFF_ON["IfcGeomServer" in targets],
"-DBUILD_CONVERT=" +OFF_ON["IfcConvert" in targets],
exec_args = [
"-DBUILD_IFCGEOM=" +OFF_ON["IfcGeom" in targets],
"-DBUILD_GEOMSERVER=" +OFF_ON["IfcGeomServer" in targets],
"-DBUILD_CONVERT=" +OFF_ON["IfcConvert" in targets],
"-DBUILD_IFCPYTHON=" "OFF",
"-DCMAKE_INSTALL_PREFIX=" "{DEPS_DIR}/install/ifcopenshell".format(**locals()),
"-DCMAKE_INSTALL_PREFIX=" f"{DEPS_DIR}/install/ifcopenshell",
]
cmake_args=[
cmake_args = [
"-DUSE_MMAP=" "OFF",
"-DBUILD_EXAMPLES=" "OFF",
"-DBUILD_SHARED_LIBS=" +OFF_ON[not BUILD_STATIC],
"-DBOOST_ROOT=" "{DEPS_DIR}/install/boost-{BOOST_VERSION}".format(**locals()),
"-DBUILD_SHARED_LIBS=" +OFF_ON[not BUILD_STATIC],
"-DBOOST_ROOT=" f"{DEPS_DIR}/install/boost-{BOOST_VERSION}",
"-DGLTF_SUPPORT=" "ON",
"-DJSON_INCLUDE_DIR=" "{DEPS_DIR}/install/json".format(**locals()),
"-DJSON_INCLUDE_DIR=" f"{DEPS_DIR}/install/json",
"-DBoost_NO_BOOST_CMAKE=" "On",
"-DADD_COMMIT_SHA=" +("On" if ADD_COMMIT_SHA else "Off")
]
if "cgal" in targets:
cmake_args.extend([
"-DCGAL_INCLUDE_DIR=" "{DEPS_DIR}/install/cgal-{CGAL_VERSION}/include".format(**locals()),
"-DGMP_INCLUDE_DIR=" "{DEPS_DIR}/install/gmp-{GMP_VERSION}/include".format(**locals()),
"-DGMP_LIBRARY_DIR=" "{DEPS_DIR}/install/gmp-{GMP_VERSION}/lib".format(**locals()),
"-DMPFR_INCLUDE_DIR=" "{DEPS_DIR}/install/mpfr-{MPFR_VERSION}/include".format(**locals()),
"-DMPFR_LIBRARY_DIR=" "{DEPS_DIR}/install/mpfr-{MPFR_VERSION}/lib".format(**locals()),
"-DCGAL_INCLUDE_DIR=" f"{DEPS_DIR}/install/cgal-{CGAL_VERSION}/include",
"-DGMP_INCLUDE_DIR=" f"{DEPS_DIR}/install/gmp-{GMP_VERSION}/include",
"-DGMP_LIBRARY_DIR=" f"{DEPS_DIR}/install/gmp-{GMP_VERSION}/lib",
"-DMPFR_INCLUDE_DIR=" f"{DEPS_DIR}/install/mpfr-{MPFR_VERSION}/include",
"-DMPFR_LIBRARY_DIR=" f"{DEPS_DIR}/install/mpfr-{MPFR_VERSION}/lib",
])
if "occ" in targets and USE_OCCT:
occ_include_dir = "{DEPS_DIR}/install/occt-{OCCT_VERSION}/include/opencascade".format(**locals())
occ_library_dir = "{DEPS_DIR}/install/occt-{OCCT_VERSION}/lib".format(**locals())
occ_include_dir = f"{DEPS_DIR}/install/occt-{OCCT_VERSION}/include/opencascade"
occ_library_dir = f"{DEPS_DIR}/install/occt-{OCCT_VERSION}/lib"
cmake_args.extend([
"-DOCC_INCLUDE_DIR=" +occ_include_dir,
"-DOCC_LIBRARY_DIR=" +occ_library_dir
])
elif "occ" in targets:
occ_include_dir = "{DEPS_DIR}/install/oce-{OCE_VERSION}/include/oce".format(**locals())
occ_library_dir = "{DEPS_DIR}/install/oce-{OCE_VERSION}/lib"
occ_include_dir = f"{DEPS_DIR}/install/oce-{OCE_VERSION}/include/oce"
occ_library_dir = f"{DEPS_DIR}/install/oce-{OCE_VERSION}/lib"
cmake_args.extend([
"-DOCC_INCLUDE_DIR=" +occ_include_dir,
"-DOCC_LIBRARY_DIR=" +occ_library_dir
@@ -757,82 +722,98 @@ elif "occ" in targets:
if "OpenCOLLADA" in targets:
cmake_args.extend([
"-DOPENCOLLADA_INCLUDE_DIR=" "{DEPS_DIR}/install/OpenCOLLADA/include/opencollada".format(**locals()),
"-DOPENCOLLADA_LIBRARY_DIR=" "{DEPS_DIR}/install/OpenCOLLADA/lib/opencollada".format(**locals())
"-DOPENCOLLADA_INCLUDE_DIR=" f"{DEPS_DIR}/install/OpenCOLLADA/include/opencollada",
"-DOPENCOLLADA_LIBRARY_DIR=" f"{DEPS_DIR}/install/OpenCOLLADA/lib/opencollada"
])
else:
cmake_args.extend([
"-DCOLLADA_SUPPORT=" "Off",
])
if "pcre" in targets:
cmake_args.append(
"-DPCRE_LIBRARY_DIR=" "{DEPS_DIR}/install/pcre-{PCRE_VERSION}/lib".format(**locals())
"-DPCRE_LIBRARY_DIR=" f"{DEPS_DIR}/install/pcre-{PCRE_VERSION}/lib"
)
if "libxml2" in targets:
cmake_args.extend([
"-DLIBXML2_INCLUDE_DIR=" "{DEPS_DIR}/install/libxml2-{LIBXML2_VERSION}/include/libxml2".format(**locals()),
"-DLIBXML2_LIBRARIES=" "{DEPS_DIR}/install/libxml2-{LIBXML2_VERSION}/lib/libxml2.{LIBRARY_EXT}".format(**locals())
"-DLIBXML2_INCLUDE_DIR=" f"{DEPS_DIR}/install/libxml2-{LIBXML2_VERSION}/include/libxml2",
"-DLIBXML2_LIBRARIES=" f"{DEPS_DIR}/install/libxml2-{LIBXML2_VERSION}/lib/libxml2.{LIBRARY_EXT}"
])
if "hdf5" in targets:
cmake_args.extend([
"-DHDF5_INCLUDE_DIR=" "{DEPS_DIR}/install/hdf5-{HDF5_VERSION}/include".format(**locals()),
"-DHDF5_LIBRARY_DIR=" "{DEPS_DIR}/install/hdf5-{HDF5_VERSION}/lib".format(**locals())
"-DHDF5_INCLUDE_DIR=" f"{DEPS_DIR}/install/hdf5-{HDF5_VERSION}/include",
"-DHDF5_LIBRARY_DIR=" f"{DEPS_DIR}/install/hdf5-{HDF5_VERSION}/lib"
])
run_cmake("", exec_args + cmake_args, cmake_dir=CMAKE_DIR, cwd=executables_dir)
logger.info("\rBuilding executables... ")
run([make, "-j{IFCOS_NUM_BUILD_PROCS}".format(**locals())], cwd=executables_dir)
run([make, f"-j{IFCOS_NUM_BUILD_PROCS}"], cwd=executables_dir)
run([make, "install/strip" if BUILD_CFG == "Release" else "install"], cwd=executables_dir)
if "IfcOpenShell-Python" in targets:
# On OSX the actual Python library is not linked against.
ADDITIONAL_ARGS=""
if get_os() == "Darwin":
ADDITIONAL_ARGS="-Wl,-flat_namespace,-undefined,suppress"
ADDITIONAL_ARGS = ""
if platform.system() == "Darwin":
ADDITIONAL_ARGS = "-Wl,-flat_namespace,-undefined,suppress"
os.environ["CXXFLAGS"]="%s %s" % (CXXFLAGS_MINIMAL, ADDITIONAL_ARGS)
os.environ["CFLAGS"]="%s %s" % (CFLAGS_MINIMAL, ADDITIONAL_ARGS)
os.environ["LDFLAGS"]="%s %s" % (LDFLAGS, ADDITIONAL_ARGS)
os.environ["CXXFLAGS"] = f"{CXXFLAGS_MINIMAL} {ADDITIONAL_ARGS}"
os.environ["CFLAGS"] = f"{CFLAGS_MINIMAL} {ADDITIONAL_ARGS}"
os.environ["LDFLAGS"] = f"{LDFLAGS} {ADDITIONAL_ARGS}"
for PYTHON_VERSION, _, TAG in PYTHON_VERSION_CONFS():
logger.info("\rConfiguring python {PYTHON_VERSION}{TAG} wrapper...".format(**locals()))
python_dir = os.path.join(IFCOS_DIR, "pythonwrapper")
os.makedirs(python_dir, exist_ok=True)
# python_dir = os.path.join(IFCOS_DIR, "python-{PYTHON_VERSION}{TAG}".format(**locals()))
python_dir = os.path.join(IFCOS_DIR, "pythonwrapper")
if not os.path.exists(python_dir):
os.makedirs(python_dir)
def compile_python_wrapper(python_version, python_library, python_include, python_executable):
logger.info(f"\rConfiguring python {python_version} wrapper...")
cache_path = os.path.join(python_dir, "CMakeCache.txt")
if os.path.exists(cache_path):
os.unlink(cache_path)
os.remove(cache_path)
PYTHON_LIBRARY=run([bash, "-c", "ls {DEPS_DIR}/install/python-{PYTHON_VERSION}{TAG}/lib/libpython*.*".format(**locals())])
PYTHON_INCLUDE=run([bash, "-c", "ls -d {DEPS_DIR}/install/python-{PYTHON_VERSION}{TAG}/include/python*".format(**locals())])
PYTHON_EXECUTABLE=os.path.join(DEPS_DIR, "install", "python-{PYTHON_VERSION}{TAG}".format(**locals()), "bin", "python{PYTHON_VERSION[0]}".format(**locals()))
os.environ["PYTHON_LIBRARY_BASENAME"]=os.path.basename(PYTHON_LIBRARY)
os.environ["PYTHON_LIBRARY_BASENAME"] = os.path.basename(python_library)
run_cmake("",
cmake_args + [
"-DPYTHON_LIBRARY=" +PYTHON_LIBRARY,
"-DPYTHON_EXECUTABLE=" +PYTHON_EXECUTABLE,
"-DPYTHON_INCLUDE_DIR=" +PYTHON_INCLUDE,
"-DSWIG_EXECUTABLE=" "{DEPS_DIR}/install/swig/bin/swig".format(**locals()),
"-DCMAKE_INSTALL_PREFIX=" "{DEPS_DIR}/install/ifcopenshell/tmp".format(**locals()),
"-DPYTHON_LIBRARY=" +python_library,
"-DPYTHON_EXECUTABLE=" +python_executable,
"-DPYTHON_INCLUDE_DIR=" +python_include,
"-DSWIG_EXECUTABLE=" f"{DEPS_DIR}/install/swig/bin/swig",
"-DCMAKE_INSTALL_PREFIX=" f"{DEPS_DIR}/install/ifcopenshell/tmp",
"-DUSERSPACE_PYTHON_PREFIX=" +["Off", "On"][os.environ.get("PYTHON_USER_SITE", "").lower() in {"1", "on", "true"}]
], cmake_dir=CMAKE_DIR, cwd=python_dir)
logger.info("\rBuilding python %s%s wrapper... " % (PYTHON_VERSION, TAG))
run([make, "-j%s" % (IFCOS_NUM_BUILD_PROCS,), "_ifcopenshell_wrapper"], cwd=python_dir)
logger.info(f"\rBuilding python {python_version} wrapper... ")
run([make, f"-j{IFCOS_NUM_BUILD_PROCS}", "_ifcopenshell_wrapper"], cwd=python_dir)
run([make, "install/local"], cwd=os.path.join(python_dir, "ifcwrap"))
module_dir = os.path.dirname(run([PYTHON_EXECUTABLE, "-c", "import inspect, ifcopenshell; print(inspect.getfile(ifcopenshell))"]))
module_dir = os.path.dirname(run([python_executable, "-c", "import inspect, ifcopenshell; print(inspect.getfile(ifcopenshell))"]))
if get_os() != "Darwin":
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)
return module_dir
run([cp, "-R", module_dir, os.path.join(DEPS_DIR, "install", "ifcopenshell", "python-%s%s" % (PYTHON_VERSION, TAG))])
if USE_CURRENT_PYTHON_VERSION:
python_info = sysconfig.get_paths()
python_lib = os.path.join(
sysconfig.get_config_var('LIBDIR'),
sysconfig.get_config_var('multiarchsubdir').replace("/", ""),
sysconfig.get_config_var("INSTSONAME")
)
compile_python_wrapper(platform.python_version(), python_lib, python_info["include"], sys.executable)
else:
for python_version in PYTHON_VERSIONS:
python_library = run([bash, "-c", f"ls {DEPS_DIR}/install/python-{python_version}/lib/libpython*.*"])
python_include = run([bash, "-c", f"ls -d {DEPS_DIR}/install/python-{python_version}/include/python*"])
python_executable = os.path.join(DEPS_DIR, "install", f"python-{python_version}", "bin", f"python{python_version[0]}")
module_dir = compile_python_wrapper(python_version, python_library, python_include, python_executable)
run([cp, "-R", module_dir, os.path.join(DEPS_DIR, "install", "ifcopenshell", f"python-{python_version}")])
logger.info("\rBuilt IfcOpenShell...\n\n")
+3 -2
View File
@@ -645,9 +645,10 @@ class BcfXml:
def get_comments(self, guid):
comments = {}
data = self._read_xml(os.path.join(guid, "markup.bcf"), "markup.xsd")
if "Comments" not in data["Topics"]:
return comments
data = self._read_xml(os.path.join(guid, "markup.bcf"), "markup.xsd")
for item in data["Topic"]["Comments"].get("Comment", []):
comment = bcf.v3.data.Comment()
mandatory_keys = {
@@ -675,9 +676,9 @@ class BcfXml:
def get_viewpoints(self, guid):
viewpoints = {}
data = self._read_xml(os.path.join(guid, "markup.bcf"), "markup.xsd")
if "Viewpoints" not in data["Topic"]:
return viewpoints
data = self._read_xml(os.path.join(guid, "markup.bcf"), "markup.xsd")
for item in data["Topic"]["Viewpoints"]:
viewpoint = self.get_viewpoint(item, guid)
viewpoints[viewpoint.guid] = viewpoint
+1 -1
View File
@@ -46,7 +46,7 @@ class RelatedTopic:
class HeaderFile:
def __init__(self):
self.file_name = ""
self.filename = ""
self.date = None
self.reference = ""
self.ifc_project = None
+102 -5
View File
@@ -27,6 +27,10 @@ ifeq ($(PYVERSION), py39)
PYLIBDIR:=python3.9
PYNUMBER:=39
endif
ifeq ($(PYVERSION), py310)
PYLIBDIR:=python3.10
PYNUMBER:=31
endif
ifeq ($(PLATFORM), linux)
ifeq ($(PYVERSION), py37)
@@ -41,6 +45,12 @@ EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/2.6.5/download/linux-64/ei
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.74.0/download/linux-64/boost-1.74.0-py39h5472131_3.tar.bz2
LXML_URL:=https://files.pythonhosted.org/packages/19/d9/a69c6aff5673554df48120565a14a50eaa41d29ae03b02faa0b023666318/lxml-4.6.3-cp39-cp39-manylinux2014_x86_64.whl
endif
ifeq ($(PYVERSION), py310)
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/1.8.0/download/linux-64/hpp-fcl-1.8.0-py310hdaf7e41_1.tar.bz2
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/2.6.11/download/linux-64/eigenpy-2.6.11-py310hf3e5c9c_0.tar.bz2
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.74.0/download/linux-64/boost-1.74.0-py310h7c3ba0c_5.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
endif
ASSIMP_URL:=https://anaconda.org/conda-forge/assimp/5.0.1/download/linux-64/assimp-5.0.1-hedfc422_6.tar.bz2
OCTOMAP_URL:=https://anaconda.org/conda-forge/octomap/1.9.7/download/linux-64/octomap-1.9.7-h4bd325d_0.tar.bz2
ZLIB_URL:=https://anaconda.org/conda-forge/zlib/1.2.11/download/linux-64/zlib-1.2.11-h516909a_1010.tar.bz2
@@ -59,6 +69,12 @@ EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/2.6.5/download/osx-64/eige
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.74.0/download/osx-64/boost-1.74.0-py39ha641261_3.tar.bz2
LXML_URL:=https://files.pythonhosted.org/packages/b8/74/a71f7ad72e8db54ce899efab84507b801660750cbbfa6a39e6717557d36a/lxml-4.6.3-cp39-cp39-macosx_10_9_x86_64.whl
endif
ifeq ($(PYVERSION), py310)
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/1.8.0/download/osx-64/hpp-fcl-1.8.0-py310h651ac30_1.tar.bz2
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/2.6.11/download/osx-64/eigenpy-2.6.11-py310hc03097c_0.tar.bz2
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.74.0/download/osx-64/boost-1.74.0-py310h509978a_5.tar.bz2
LXML_URL:=https://files.pythonhosted.org/packages/a1/44/17b7dac7a18807d30e2fe10c3328c152808f5464565e230bfd0e77f178c6/lxml-4.8.0-cp310-cp310-macosx_10_15_x86_64.whl
endif
ASSIMP_URL:=https://anaconda.org/conda-forge/assimp/5.0.1/download/osx-64/assimp-5.0.1-h1224e73_6.tar.bz2
OCTOMAP_URL:=https://anaconda.org/conda-forge/octomap/1.9.7/download/osx-64/octomap-1.9.7-h940c156_0.tar.bz2
ZLIB_URL:=https://anaconda.org/conda-forge/zlib/1.2.11/download/osx-64/zlib-1.2.11-h7795811_1010.tar.bz2
@@ -77,6 +93,12 @@ EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/2.6.5/download/win-64/eige
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.74.0/download/win-64/boost-1.74.0-py39hefe7e4c_3.tar.bz2
LXML_URL:=https://files.pythonhosted.org/packages/72/d4/426ecb8849c47c3e370c87aa0ac05d85768df917ffea27fcd6686a5e6495/lxml-4.6.3-cp39-cp39-win_amd64.whl
endif
ifeq ($(PYVERSION), py310)
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/1.8.0/download/win-64/hpp-fcl-1.8.0-py310hc5a3c62_1.tar.bz2
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/2.6.11/download/win-64/eigenpy-2.6.11-py310hbd43d28_0.tar.bz2
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.74.0/download/win-64/boost-1.74.0-py310hc781a3c_5.tar.bz2
LXML_URL:=https://files.pythonhosted.org/packages/f6/71/65c80a4caa1617a4c6e8fe1500cebb179db96232e2f623bfe6a1f4294e39/lxml-4.8.0-cp310-cp310-win_amd64.whl
endif
ASSIMP_URL:=https://anaconda.org/conda-forge/assimp/5.0.1/download/win-64/assimp-5.0.1-hc2aa0de_6.tar.bz2
OCTOMAP_URL:=https://anaconda.org/conda-forge/octomap/1.9.7/download/win-64/octomap-1.9.7-h5362a0b_0.tar.bz2
ZLIB_URL:=https://anaconda.org/conda-forge/zlib/1.2.11/download/win-64/zlib-1.2.11-h62dcd97_1010.tar.bz2
@@ -93,9 +115,9 @@ endif
cp -r blenderbim/* dist/blenderbim/
# Provides IfcOpenShell Python functionality
cd dist/working && wget https://s3.amazonaws.com/ifcopenshell-builds/ifcblender-python-$(PYNUMBER)-v0.7.0-9853d3d-$(PLATFORM)64.zip
cd dist/working && unzip ifcblender*
cp -r dist/working/io_import_scene_ifc/ifcopenshell dist/blenderbim/libs/site/packages/
cd dist/working && wget https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-$(PYNUMBER)-v0.7.0-1b1fd1e-$(PLATFORM)64.zip
cd dist/working && unzip ifcopenshell-python*
cp -r dist/working/ifcopenshell dist/blenderbim/libs/site/packages/
# See bug #812
cd dist/working && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.7.0/src/blenderbim/occ_utils.py
@@ -133,6 +155,8 @@ endif
cp -r dist/working/IfcOpenShell-0.7.0/src/ifc4d/ifc4d dist/blenderbim/libs/site/packages/
# Provides IFC5D functionality
cp -r dist/working/IfcOpenShell-0.7.0/src/ifc5d/ifc5d dist/blenderbim/libs/site/packages/
# Provides IFCCityJSON functionality
cp -r dist/working/IfcOpenShell-0.7.0/src/ifccityjson dist/blenderbim/libs/site/packages/
rm -rf dist/working
# Provides Mustache templating in construction documentation
@@ -240,6 +264,30 @@ endif
cp -r dist/working/lark-parser-0.8.5/lark dist/blenderbim/libs/site/packages/
rm -rf dist/working
# Required by IFC4D
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/b0/bb/9c4dddd3ca173cb56241cfb2eddfae24690dc676d357ac4cab17d0a36d9d/PyP6Xer-1.13.0.tar.gz
cd dist/working && tar -xzvf PyP6Xer*
cp -r dist/working/PyP6Xer-1.13.0/xerparser dist/blenderbim/libs/site/packages/
rm -rf dist/working
# Required by xerparser and IFC4D
# TODO: remove this dependency. It's only used to show a progress bar.
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/9f/7b/76c4e5ef1a1b528fcaada4133f972e77d33c252831676cf414119ca6093d/tqdm-4.50.0.tar.gz
cd dist/working && tar -xzvf tqdm*
cp -r dist/working/tqdm-4.50.0/tqdm dist/blenderbim/libs/site/packages/
rm -rf dist/working
# Required by IFCCityJSON
mkdir dist/working
cd dist/working && python -m venv env
cd dist/working && mkdir site-packages
cd dist/working && . env/bin/activate && pip install cjio --target=./site-packages
cd dist/working/site-packages/ && rm -r *dist-info*
cp -r dist/working/site-packages/* dist/blenderbim/libs/site/packages/
rm -rf dist/working
# Required by IFCClash
mkdir dist/working
cd dist/working && wget $(HPPFCL_URL)
@@ -345,7 +393,7 @@ endif
cd dist/working/ && patch ../blenderbim/libs/site/packages/behave/runner_util.py < runner_util.patch
rm -rf dist/working
# Required by ids
# Required by ids - uh, not any more, but I'm trying it out in the drawing module
mkdir dist/working
cd dist/working && wget $(LXML_URL)
cd dist/working && cp *.whl lxml.zip && unzip lxml.zip
@@ -429,6 +477,47 @@ endif
cd dist/working/olca-ipc-0.0.10/ && cp -r olca ../../blenderbim/libs/site/packages/
rm -rf dist/working
# Provides Brickschema fucntionality
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/ca/37/8309da9a72407d2f5eb5489c197ac3cfe96ee3ccba1902d2a0ecc7843f92/brickschema-0.5.1.tar.gz
cd dist/working && tar -xzvf brickschema*
# This is an evil hack because we don't want to bundle flask
echo "" > dist/working/brickschema-0.5.1/brickschema/web.py
cd dist/working/brickschema-0.5.1/ && cp -r brickschema ../../blenderbim/libs/site/packages/
# For now lets bundle the latest nightly schema
cd dist/working && wget https://github.com/BrickSchema/Brick/releases/download/nightly/Brick.ttl
cd dist/working && cp Brick.ttl ../../blenderbim/bim/schema/Brick.ttl
rm -rf dist/working
# Required by brickschema
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/c7/22/37bb938be8e5c20d443b0c9ba0a243573b671b01739efb0a19f81bc5b470/pyshacl-0.17.2.tar.gz
cd dist/working && tar -xzvf pyshacl*
cd dist/working/pyshacl-0.17.2/ && cp -r pyshacl ../../blenderbim/libs/site/packages/
rm -rf dist/working
# Required by brickschema
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/d2/a7/be8244688bfcee37c23733ab4fe8e6afa6d4403bd2674a3ae7bd2cecc77b/rdflib-6.0.2.tar.gz
cd dist/working && tar -xzvf rdflib*
cd dist/working/rdflib-6.0.2/ && cp -r rdflib ../../blenderbim/libs/site/packages/
rm -rf dist/working
# Required by brickschema
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/7d/c7/208aece36279e4f1236e437119358786e39530ecc1719d4e1afeddba5288/owlrl-6.0.2.tar.gz
cd dist/working && tar -xzvf owlrl*
cd dist/working/owlrl-6.0.2/ && cp -r owlrl ../../blenderbim/libs/site/packages/
rm -rf dist/working
# Required by brickschema
# This is a bit of a dodgy one, it should be provided by setuptools which Blender doesn't ship.
mkdir dist/working
cd dist/working && wget https://blenderbim.org/builds/pkg_resources.tar.gz
cd dist/working && tar -xzvf pkg_resources*
cd dist/working/ && cp -r pkg_resources ../blenderbim/libs/site/packages/
rm -rf dist/working
cd dist/blenderbim && sed -i "s/999999/$(VERSION)/" __init__.py
cd dist && zip -r blenderbim-$(VERSION)-$(PYVERSION)-$(PLATFORM).zip ./*
rm -rf dist/blenderbim
@@ -445,11 +534,19 @@ test-core:
.PHONY: test-bim
test-bim:
ifndef MODULE
pytest test/bim
else
pytest test/bim -m "$(MODULE)" ./ --maxfail=1
endif
.PHONY: test-tool
test-tool:
ifndef MODULE
pytest test/tool
else
pytest test/tool/test_$(MODULE).py
endif
.PHONY: qa
qa:
@@ -464,7 +561,7 @@ coverage:
.PHONY: license
license:
copyright-header --license GPL3 --copyright-holder "Dion Moult <dion@thinkmoult.com>" --copyright-year "2021" --copyright-software "BlenderBIM Add-on" --copyright-software-description "OpenBIM Blender Add-on" -a ./ -o ./
copyright-header --license GPL3 --copyright-holder "Dion Moult <dion@thinkmoult.com>" --copyright-year "2022" --copyright-software "BlenderBIM Add-on" --copyright-software-description "OpenBIM Blender Add-on" -a ./ -o ./
.PHONY: clean
clean:
+3 -125
View File
@@ -1,127 +1,5 @@
# Blender BIM IFC
# BlenderBIM Add-on
_Under heavy development!_
An add-on to Blender to allow BIM functionality.
Let's use Blender to create BIM models, because why not?
## Features
- IFC4 is a first-class citizen. IFC4 has been an ISO standard since 2013, with
the latest published in 2018. It contains many more features than IFC2X3,
published back in 2005.
- Available for Windows, Mac, and Linux!
- Use open-source Blender 2.8 to do digital modeling! Used by NASA, Hollywood,
and Ubisoft, Blender allows real-time modeling in rendered view, animations,
file linking, proxy objects, Blender Cloud collaboration, lighting analysis,
CFD, energy modeling, and a huge ecosystem of add-ons with a thriving
community. No licensing, and with a strong Python API to integrate into your
custom workflow.
- Create complex spatial structures with multiple sites, buildings, facilities,
bridges, and building parts. Sites can be broken down into sub-sites and
multiple buildings per site. You can categorise different floors to different
buildings.
- Classify any geometry as any IFC type. Just because it is a wall doesn't
mean you should be limited to modeling it in a certain way. Easily change
from one type to another, switch relationships and aggregations, reuse or
even remove geometry altogether, without damaging your geometry or breaking
your BIM model.
- Use collections to create element compositions and decompositions with full
control over the classifications of their parts. Most BIM programs don't give
this flexibility. Create collection instances to efficiently duplicate
composite objects.
- Allow BIM objects to instance a shared geometric representations. This allows
you to place the same shape in multiple objects without drastically
increasing your filesize.
- Provide a custom value to any simple IFC attribute of an IFC product.
- Create product types that regular IFC products can inherit attributes from.
- Define your own property sets with control over exactly how the data in the
property sets are stored using property set template definitions which you
can download or create yourself. Property sets can apply to both types and
objects, and object property sets can selectively override type property
sets.
- Applies array modifiers to create instances.
- Basic quantity take-off support for costing.
- Settings to toggle export of quantities or geometric representations.
Exporting IFC data without geometric representations is much faster and a
fraction of the filesize.
- Maintain `GlobalId` between exports, so that you can manipulate the IFC
reliably in other software while making changes.
- Not all objects need full solid geometry. Create representations as 2D or 3D
curves / wireframes for rapid and lightweight BIM development.
- Create map conversions and coordinate reference system definitions to link
BIM to GIS.
- Export basic materials with surface and diffuse colours for representation.
You can also link materials to an externally defined file, such as a `.blend`
Cycles material, V-Ray material, or even Radiance material for lighting
simulation.
- Switch between different types of metric units when modeling.
- Model multiple representations for different purposes for a single BIM
object or type, such as footprints, axis, reference, or clearance geometry.
You can export just the type of representation required for your usecase, or
export multiple for the user to choose from.
- Record qualitative objectives such as design intentions, health and safety
strategies, or code compliance, performance solutions, and more, in the BIM
model and directly associate them to objects or types.
- Classify objects and types to different classification systems such as
Uniclass, Omniclass, or your custom system.
- Associate external document files such as plans, brochures, specifications,
warranties, or anything to objects and types.
- Specify swept solid geometries, allowing you to round-trip geometry
manipulation with other BIM software which have more limited geometric tools,
like Revit.
- Create material layer sets, for objects which have materials in predefined
layers with thicknesses.
- Create material constituent sets, for objects which are made up of
arbitrarily mixed ingredients.
- Specify nested and hosted element relationships.
- Specify predefined door attributes as defined in the IFC4 specification.
- Specify predefined window attributes as defined in the IFC4 specification.
- Specify surface styles either defined as part of a material or assigned
directly to an object's representation.
- Colour coding by IFC class to visually spot mistakes
Some more experimental features:
- Cut sections for documentation with OpenCascade
- Export gbXML files that are linked to your IFC file to do energy analysis
- QA module to generate Gherkin unit tests to validate your BIM models
- HTML formatter to generate reports from QA results
- COBie spreadsheet extraction to import into CAFM systems
## Demo
[See demo video](https://www.youtube.com/watch?v=iD3v3eu2AjY)
It's only just started, so everything can and will change. But if you want to
check it out:
1. Install [Blender](https://www.blender.org/) 64-bit version.
2. Download the latest packaged version of Blender BIM from
[here](https://blenderbim.org/builds/).
3. Launch Blender. Go to `Edit->Preferences->Add-ons->Install...`. Browse to
the downloaded `.zip` file from step 2.
4. Enable the checkbox next to the Import-Export IFC Blender plugin which will
show up.
5. All done! You will find an import and export IFC option in the `File` menu.
By default, Blender BIM will export whatever you have selected. The objects you
have selected _must_ belong in an `IfcProject` and spatially contained, such as
witihn an `IfcBuilding`. It must also follow the naming convention. Please see
the screenshot below for an example scene.
A naming convention of `IfcClass/IfcName` is enforced. If you name your object
`IfcWall/Foo`, it will turn into an `IfcWall` with the name attribute set to
`Foo`. You can override object attributes using Blender's `Custom Properties`.
Your objects must live in a top level `Collection` defined as a context class,
usually something like `IfcProject/Foo`. You can then nest spatial structures
underneath to create a hierarchy.
## Pictures
![UI](https://aws1.discourse-cdn.com/business6/uploads/buildingsmart1/original/1X/ce820c3ee22adcffae59b40f98ff23379f7e3547.png)
![Blender screenshot](blender-screenshot.png)
... this Blender files creates this IFC file ...
![IfcOpenShell screenshot](ifcopenshell-screenshot.png)
More information on the [BlenderBIM Add-on website](https://blenderbim.org).
Binary file not shown.

Before

Width:  |  Height:  |  Size: 399 KiB

+41 -28
View File
@@ -18,7 +18,7 @@
import bpy
import importlib
from . import handler, ui, prop, operator
from . import handler, ui, prop, operator, helper
modules = {
"project": None,
@@ -27,9 +27,11 @@ modules = {
"root": None,
"unit": None,
"model": None,
"cad": None,
"georeference": None,
"context": None,
"drawing": None,
"misc": None,
"attribute": None,
"type": None,
"spatial": None,
@@ -42,6 +44,7 @@ modules = {
"sequence": None,
"group": None,
"system": None,
"brick": None,
"structural": None,
"boundary": None,
"profile": None,
@@ -52,6 +55,7 @@ modules = {
"pset": None,
"qto": None,
"classification": None,
"library": None,
"constraint": None,
"document": None,
"pset_template": None,
@@ -61,33 +65,37 @@ modules = {
"bimtester": None,
"diff": None,
"patch": None,
"gis": None,
"covetool": None,
"augin": None,
"debug": None,
# Uncomment this line to enable loading of the demo module. Happy hacking!
# The name "demo" must correlate to a folder name in `bim/module/`.
# "demo": None,
}
for name in modules.keys():
modules[name] = importlib.import_module(f"blenderbim.bim.module.{name}")
classes = [
operator.OpenUri,
operator.SelectDataDir,
operator.SelectSchemaDir,
operator.SelectIfcFile,
operator.ExportIFC,
operator.ImportIFC,
operator.OpenUpstream,
operator.AddSectionPlane,
operator.RemoveSectionPlane,
operator.ReloadIfcFile,
operator.AddIfcFile,
operator.BIM_OT_add_section_plane,
operator.BIM_OT_remove_section_plane,
operator.ConfigureVisibility,
operator.OpenUpstream,
operator.OpenUri,
operator.ReloadIfcFile,
operator.RemoveIfcFile,
operator.SetOverrideColour,
operator.SetViewportShadowFromSun,
operator.SnapSpacesTogether,
operator.OverrideDelete,
operator.SelectDataDir,
operator.SelectIfcFile,
operator.SelectSchemaDir,
operator.SelectURIAttribute,
prop.StrProperty,
prop.ObjProperty,
prop.Attribute,
prop.ModuleVisibility,
prop.BIMProperties,
prop.IfcParameter,
prop.PsetQto,
@@ -99,23 +107,32 @@ classes = [
ui.BIM_UL_generic,
ui.BIM_UL_topics,
ui.BIM_ADDON_preferences,
# Scene panel groups
ui.BIM_PT_project_info,
ui.BIM_PT_project_setup,
ui.BIM_PT_collaboration,
ui.BIM_PT_geometry,
ui.BIM_PT_services,
ui.BIM_PT_structural,
ui.BIM_PT_4D5D,
ui.BIM_PT_quality_control,
ui.BIM_PT_integrations,
# Object panel groups
ui.BIM_PT_object_metadata,
ui.BIM_PT_geometry_object,
ui.BIM_PT_services_object,
ui.BIM_PT_utilities_object,
ui.BIM_PT_misc_object,
]
for mod in modules.values():
classes.extend(mod.classes)
def menu_func_export(self, context):
self.layout.operator(operator.ExportIFC.bl_idname, text="Industry Foundation Classes (.ifc/.ifczip/.ifcjson)")
def menu_func_import(self, context):
self.layout.operator(operator.ImportIFC.bl_idname, text="Industry Foundation Classes (.ifc/.ifczip/.ifcxml)")
def on_register(scene):
handler.setDefaultProperties(scene)
bpy.app.handlers.depsgraph_update_post.remove(on_register)
if not bpy.app.background:
bpy.app.handlers.depsgraph_update_post.remove(on_register)
def register():
@@ -129,8 +146,6 @@ def register():
bpy.app.handlers.load_post.append(handler.setDefaultProperties)
bpy.app.handlers.load_post.append(handler.loadIfcStore)
bpy.app.handlers.save_pre.append(handler.ensureIfcExported)
bpy.types.TOPBAR_MT_file_export.append(menu_func_export)
bpy.types.TOPBAR_MT_file_import.append(menu_func_import)
bpy.types.Scene.BIMProperties = bpy.props.PointerProperty(type=prop.BIMProperties)
bpy.types.Object.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties)
bpy.types.Material.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties)
@@ -151,8 +166,6 @@ def unregister():
bpy.app.handlers.load_post.remove(handler.setDefaultProperties)
bpy.app.handlers.load_post.remove(handler.loadIfcStore)
bpy.app.handlers.save_pre.remove(handler.ensureIfcExported)
bpy.types.TOPBAR_MT_file_export.remove(menu_func_export)
bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)
del bpy.types.Scene.BIMProperties
del bpy.types.Object.BIMObjectProperties
del bpy.types.Material.BIMObjectProperties
@@ -1,10 +1,44 @@
<link href="jsgantt.css" rel="stylesheet" type="text/css"/>
<script src="jsgantt.js" type="text/javascript"></script>
<style>
.gtaskcellwkend,
.gtaskcellcurrent,
.gminorheadingwkend {
background-color: #e1e1e1;
}
.gitemhighlight td {
background-color: #ffdaaa;
}
.gtaskblue {
background: #4281A4;
}
.gtaskred {
background: #C1666B;
}
.gtaskgreen {
background: #48A9A6;
}
.gtaskyellow {
background: #D4B483;
}
.gmainleft {
overflow: visible;
flex: 0 1 auto;
}
</style>
<a href="#print" id="print">Print Mode</a>
<div style="position:relative" class="gantt" id="GanttChartDIV"></div>
<script type="text/javascript">
var g = new JSGantt.GanttChart(document.getElementById('GanttChartDIV'), 'day');
g.setOptions({
vCaptionType: 'Complete', // Set to Show Caption : None,Caption,Resource,Duration,Complete,
vCaptionType: 'Caption', // Set to Show Caption : None,Caption,Resource,Duration,Complete,
vQuarterColWidth: 36,
vDateTaskDisplayFormat: 'day dd month yyyy', // Shown in tool tip box
vDayMajorDateDisplayFormat: 'mon yyyy - Week ww',// Set format to dates in the "Major" header of the "Day" view
@@ -14,7 +48,16 @@ g.setOptions({
vShowEndWeekDate: 0, // Show/Hide the date for the last day of the week in header for daily
vUseSingleCell: 10000, // Set the threshold cell per table row (Helps performance for large data.
vFormatArr: ['Day', 'Week', 'Month', 'Quarter'], // Even with setUseSingleCell using Hour format on such a large chart can cause issues in some browsers,
vTotalHeight: 1000,
vShowRes: false, // Disable the resource column.
vShowComp: false, // Disable the completion column.
vShowDur: false, // Disable the duration column, because jsgantt doesn't calculate durations the way we want.
vAdditionalHeaders: {ifcduration: {title: 'Duration'}},
vUseToolTip: false, // Disable tooltips.
vTotalHeight: 900,
});
document.getElementById('print').addEventListener('click', function() {
g.setTotalHeight("");
g.Draw();
});
var json_data = `
{{{json_data}}}
File diff suppressed because one or more lines are too long
@@ -0,0 +1,13 @@
ISO-10303-21;
HEADER;
FILE_DESCRIPTION((),'2;1');
FILE_NAME('EPset_Annotation.ifc','2020-01-01T00:00:00',(),(),'EPset_Annotation','EPset_Annotation',$);
FILE_SCHEMA(('IFC4'));
ENDSEC;
DATA;
#1=IFCPROPERTYSETTEMPLATE('3VuPUwdCD2Qx3XDDRs0R1N',$,'EPset_Annotation','',.PSET_OCCURRENCEDRIVEN.,'IfcAnnotation',(#2,#3));
#2=IFCSIMPLEPROPERTYTEMPLATE('2P7JN79n96Q9pElZ83LKe4',$,'ZIndex','',.P_SINGLEVALUE.,'IfcInteger',$,$,$,$,$,.READWRITE.);
#3=IFCSIMPLEPROPERTYTEMPLATE('1Wpx_r2xj1_9w5JpI0QRJy',$,'Symbol','',.P_ENUMERATEDVALUE.,'IfcLabel',$,#4,$,$,$,.READWRITE.);
#4=IFCPROPERTYENUMERATION('Symbol',(IFCLABEL('rectangle-tag'), IFCLABEL('door-tag')),$);
ENDSEC;
END-ISO-10303-21;
@@ -0,0 +1,24 @@
ISO-10303-21;
HEADER;
FILE_DESCRIPTION((),'2;1');
FILE_NAME('EPset_Drawing.ifc','2020-01-01T00:00:00',(),(),'EPset_Drawing','EPset_Drawing',$);
FILE_SCHEMA(('IFC4'));
ENDSEC;
DATA;
#1=IFCPROPERTYSETTEMPLATE('2JhNIvqZrFnAgxfhK0XVQX',$,'EPset_Drawing','',.PSET_OCCURRENCEDRIVEN.,'IfcAnnotation',(#2,#3,#4,#5,#6,#7,#8,#9,#10,#11,#12,#13,#14,#15));
#2=IFCSIMPLEPROPERTYTEMPLATE('23JavTMk98ZxXhrUEnjAcf',$,'TargetView','',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#3=IFCSIMPLEPROPERTYTEMPLATE('1yVWUt5H9DAOuu0OaMMLpe',$,'Scale','',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#4=IFCSIMPLEPROPERTYTEMPLATE('3gsuPBtU93b8f0gg1pjkq6',$,'HumanScale','',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#5=IFCSIMPLEPROPERTYTEMPLATE('0AK5C2UpL4$eaac2LszAx$',$,'HasUnderlay','',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
#6=IFCSIMPLEPROPERTYTEMPLATE('2j2ZEZR8X5tONm7kli5hM6',$,'HasLinework','',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
#7=IFCSIMPLEPROPERTYTEMPLATE('1ttChRysH9UuEX2FeMj5Hu',$,'HasAnnotation','',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
#8=IFCSIMPLEPROPERTYTEMPLATE('2NPPxuABv1huDTVh32TFgw',$,'GlobalReferencing','',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
#9=IFCSIMPLEPROPERTYTEMPLATE('10hT_1zrzEbRRKMXYAWvtD',$,'Metadata','',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
#10=IFCSIMPLEPROPERTYTEMPLATE('3Z0BXPSG5CWgtI33ioV7aj',$,'Include','',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
#11=IFCSIMPLEPROPERTYTEMPLATE('1RVts_g3PAw98PJA2yL3bO',$,'Exclude','',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
#12=IFCSIMPLEPROPERTYTEMPLATE('0c1$8NpYDEaBiJrj16jHIo',$,'Stylesheet','',.P_SINGLEVALUE.,'IfcURIReference',$,$,$,$,$,.READWRITE.);
#13=IFCSIMPLEPROPERTYTEMPLATE('3mRF52q81FQB$h4oTh7M45',$,'Markers','',.P_SINGLEVALUE.,'IfcURIReference',$,$,$,$,$,.READWRITE.);
#14=IFCSIMPLEPROPERTYTEMPLATE('1rhr_0N3LDtuORcEJP0KXM',$,'Symbols','',.P_SINGLEVALUE.,'IfcURIReference',$,$,$,$,$,.READWRITE.);
#15=IFCSIMPLEPROPERTYTEMPLATE('2sHDBuW7P4TROy$hL2w7ct',$,'Patterns','',.P_SINGLEVALUE.,'IfcURIReference',$,$,$,$,$,.READWRITE.);
ENDSEC;
END-ISO-10303-21;
@@ -19,15 +19,31 @@
*/
* { stroke-linecap: round; stroke-linejoin: round; }
*[id] { fill: black; stroke: black; stroke-linecap: 'round'; stroke-width: 0.35; }
text { fill: black; stroke: none; }
.cut { fill: black; stroke: black; stroke-linecap: 'round'; stroke-width: 0.35; fill-rule: evenodd; }
.projection { fill: white; stroke: black; stroke-linecap: 'round'; stroke-width: 0.25; }
.annotation { fill: none; stroke: black; stroke-linecap: 'round'; stroke-width: 0.25; }
.hidden { stroke-dasharray: 3, 2; }
.solid {}
.leader { marker-end: url(#leader-marker); }
.stair { marker-start: url(#stair-marker-start); marker-end: url(#stair-marker-end); }
.break { fill: white; }
.breakline { fill: none; stroke: black; stroke-linecap: 'round'; stroke-width: 0.25; marker-mid: url(#breakline-marker); }
.IfcAnnotation { fill: none; stroke: black; stroke-linecap: 'round'; stroke-width: 0.25; }
.PredefinedType-LINEWORK { stroke: black; stroke-width: 0.25; }
.PredefinedType-BACKGROUND { stroke: black; stroke-width: 0.18; }
.PredefinedType-GRID { marker-start: url(#grid-marker); marker-end: url(#grid-marker); }
.PredefinedType-SECTION { stroke-dasharray: 12.5, 3, 3, 3; }
.PredefinedType-SECTIONLEVEL { marker-start: url(#section-level-marker); stroke-dasharray: 12.5, 3, 3, 3; }
.PredefinedType-PLANLEVEL { marker-end: url(#plan-level-marker); }
.PredefinedType-DIMENSION { marker-start: url(#dimension-marker-start); marker-end: url(#dimension-marker-end); }
.PredefinedType-ANGLE { marker-start: url(#angle-marker-start); marker-end: url(#angle-marker-end); }
.PredefinedType-RADIUS { marker-end: url(#radius-marker-end); }
.PredefinedType-DIAMETER { marker-start: url(#diameter-marker-start); marker-end: url(#diameter-marker-end); }
.PredefinedType-HIDDENLINE { stroke-dasharray: 3, 2; }
.PredefinedType-STAIRARROW { marker-start: url(#stair-marker-start); marker-end: url(#stair-marker-end); }
.PredefinedType-BOUNDARY { fill: none; stroke: red; stroke-width: 1; stroke-dasharray: 12, 4, 3, 4, 3, 4; }
.PredefinedType-BATTING { marker-start: url(#batting); stroke: none; }
.PredefinedType-SEALANT { fill: url(#crosshatch1); stroke-width: 0.25; }
.PredefinedType-FILLAREA { fill: white; stroke: none; }
.PredefinedType-BREAKLINE { fill: none; stroke: black; stroke-width: 0.25; marker-mid: url(#breakline-marker); }
.PredefinedType-TEXT { fill: black; stroke: none; }
path.PredefinedType-TEXTLEADER { marker-end: url(#leader-marker); }
text.PredefinedType-TEXTLEADER { fill: black; stroke: none; }
.material-blank { fill: white; }
.material-diagonal1 { fill: url(#diagonal1); }
.material-diagonal2 { fill: url(#diagonal2); }
@@ -43,5 +59,9 @@
.material-honeycomb { fill: url(#honeycomb); }
.material-sand { fill: url(#sand); }
.material-concrete { fill: url(#concrete); stroke-width: 0.5; }
.material-boundary { fill: none; stroke: red; stroke-width: 1; stroke-dasharray: 12,4,3,4,3,4; }
.IfcSpace { fill: none; stroke: none; }
.PredefinedType-STUD { stroke: black; stroke-width: 1; }
.PredefinedType-WOOD { fill: url(#wood); stroke: black; stroke-width: 0.5; }
.PredefinedType-STEEL { fill: url(#steel); stroke: black; stroke-width: 0.5; }
.PredefinedType-CONCRETE { fill: url(#concrete); stroke: black; stroke-width: 0.5; }
.PredefinedType-PLASTERBOARD { fill: url(#sand); stroke: black; stroke-width: 0.25; }
@@ -1,5 +1,9 @@
<?xml version="1.0" encoding="utf-8" ?>
<svg baseProfile="full" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink">
<marker id="batting" markerHeight="10" markerWidth="4" orient="auto" refX="0" refY="5" markerUnits="userSpaceOnUse">
<!-- Tip: https://svg-path-visualizer.netlify.app/ -->
<path style="fill: none; stroke:black; stroke-width:0.18" d="M 0 2 A 2 2 0 0 1 4 2 L 2 8 M 0 2 L 2 8 A 2 2 0 0 0 4 10 M 2 8 A 2 2 0 0 1 0 10" />
</marker>
<marker id="eng-dimension-marker-start" markerHeight="14" markerWidth="11" orient="auto" refX="1" refY="7">
<g>
<path d="M 11 3.5 L 11 10.5 L 1 7" class="annotation" style="fill:black;" />
@@ -12,6 +16,35 @@
<path d="M 10 0 L 10 14" class="annotation" style="stroke-width:1;" />
</g>
</marker>
<marker id="radius-marker-end" markerHeight="14" markerWidth="11" orient="auto" refX="10" refY="7">
<g>
<path d="M 0 3.5 L 0 10.5 L 10 7" class="annotation" style="fill:black;" />
</g>
</marker>
<marker id="angle-marker-start" markerHeight="14" markerWidth="11" orient="auto" refX="1" refY="7">
<g>
<path d="M 11 3.5 L 11 10.5 L 1 7" class="annotation" style="fill:black;" />
<path d="M 1 0 L 1 14" class="annotation" style="stroke-width:1;" />
</g>
</marker>
<marker id="angle-marker-end" markerHeight="14" markerWidth="11" orient="auto" refX="10" refY="7">
<g>
<path d="M 0 3.5 L 0 10.5 L 10 7" class="annotation" style="fill:black;" />
<path d="M 10 0 L 10 14" class="annotation" style="stroke-width:1;" />
</g>
</marker>
<marker id="diameter-marker-start" markerHeight="14" markerWidth="11" orient="auto" refX="1" refY="7">
<g>
<path d="M 11 3.5 L 11 10.5 L 1 7" class="annotation" style="fill:black;" />
<path d="M 1 0 L 1 14" class="annotation" style="stroke-width:1;" />
</g>
</marker>
<marker id="diameter-marker-end" markerHeight="14" markerWidth="11" orient="auto" refX="10" refY="7">
<g>
<path d="M 0 3.5 L 0 10.5 L 10 7" class="annotation" style="fill:black;" />
<path d="M 10 0 L 10 14" class="annotation" style="stroke-width:1;" />
</g>
</marker>
<marker id="dimension-marker-start" markerHeight="30" markerWidth="20" refX="10" refY="15" orient="auto">
<g>
<path d="M 0 15 L 20 15" class="annotation" style="stroke-width:1;" />
@@ -53,7 +86,7 @@
<path d="M 0 1 L 90 1" class="annotation" style="stroke-width:1;" />
</g>
</marker>
<marker id="breakline-marker" markerHeight="20" markerWidth="15" refX="7.5" refY="10">
<marker id="breakline-marker" markerHeight="20" markerWidth="15" refX="7.5" refY="10" orient="auto">
<g>
<path d="M 0 10 L 5 20 L 7.5 10" class="annotation" style="stroke-width:1; fill: white;" />
<path d="M 7.5 10 L 10 0 L 15 10" class="annotation" style="stroke-width:1; fill: white;" />

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

@@ -3,6 +3,10 @@
<pattern id="demolish" width="1" height="1" patternTransform="rotate(45 0 0)" patternUnits="userSpaceOnUse">
<line x1="0" y1="0" x2="0" y2="1" style="stroke:red; stroke-width:0.25" />
</pattern>
<pattern id="steel" width="3" height="1" patternTransform="rotate(45 0 0)" patternUnits="userSpaceOnUse">
<line x1="0" y1="0" x2="0" y2="1" style="stroke:black; stroke-width:0.25" />
<line x1="1" y1="0" x2="1" y2="1" style="stroke:black; stroke-width:0.25" />
</pattern>
<pattern id="diagonal1" width="1" height="1" patternTransform="rotate(45 0 0)" patternUnits="userSpaceOnUse">
<line x1="0" y1="0" x2="0" y2="1" style="stroke:black; stroke-width:0.25" />
</pattern>
@@ -80,6 +84,9 @@
<path d="M 1.25,2.165 L 2.5,4.33 L 5,4.33 L 6.25,2.165 L 5,0 L 2.5,0 L 1.25,2.165" style="stroke:black;stroke-width:0.25;stroke-linecap:round;fill:none" />
<path d="M 6.25,2.165 L 7.5,2.165" style="stroke:black;stroke-width:0.25;stroke-linecap:round" />
</pattern>
<pattern id="wood" width="50" height="50" patternUnits="userSpaceOnUse">
<path style="fill:none; stroke: black; stroke-width:0.25;" d="M 49.649999,1.3460737 A 69.019995,69.019995 0 0 0 48.653938,0.34999949 M 49.649999,4.9389583 A 66.554994,66.554994 0 0 0 45.06105,0.34999949 M 49.649999,8.6983527 A 64.089999,64.089999 0 0 0 41.301656,0.34999949 M 49.649999,12.675003 A 61.624998,61.624998 0 0 0 37.325,0.34999949 M 49.649999,16.948067 A 59.159997,59.159997 0 0 0 33.05192,0.34999949 M 49.649999,21.652972 A 56.694997,56.694997 0 0 0 28.347017,0.34999949 M 49.649999,27.057906 A 54.229999,54.229999 0 0 0 22.942096,0.34999949 M 49.649999,33.866298 A 51.764998,51.764998 0 0 0 16.1337,0.34999949 M 49.649999,49.649989 A 49.299997,49.299997 0 0 0 0.34999949,0.34999949 M 47.185,49.649989 A 46.834999,46.834999 0 0 0 0.34999949,2.8149951 M 44.72,49.649989 A 44.369998,44.369998 0 0 0 0.34999949,5.2799906 M 42.254999,49.649989 A 41.904997,41.904997 0 0 0 0.34999949,7.7450123 M 39.790001,49.649989 A 39.439999,39.439999 0 0 0 0.34999949,10.210008 M 37.325,49.649989 A 36.974998,36.974998 0 0 0 0.34999949,12.675003 M 34.859999,49.649989 A 34.509998,34.509998 0 0 0 0.34999949,15.139998 M 32.395001,49.649989 A 32.044999,32.044999 0 0 0 0.34999949,17.604994 M 29.93,49.649989 A 29.579999,29.579999 0 0 0 0.34999949,20.069989 M 27.465,49.649989 A 27.114998,27.114998 0 0 0 0.34999949,22.535011 M 24.999997,49.649989 A 24.649999,24.649999 0 0 0 0.34999949,25.000007 M 22.534998,49.649989 A 22.184999,22.184999 0 0 0 0.34999949,27.465003 M 20.069998,49.649989 A 19.719999,19.719999 0 0 0 0.34999949,29.929999 M 17.604998,49.649989 A 17.254999,17.254999 0 0 0 0.34999949,32.394994 M 15.139998,49.649989 A 14.789999,14.789999 0 0 0 0.34999949,34.85999 M 12.674999,49.649989 A 12.325,12.325 0 0 0 0.34999949,37.325011 M 10.209999,49.649989 A 9.8599994,9.8599994 0 0 0 0.34999949,39.790007 M 7.7449994,49.649989 A 7.3949999,7.3949999 0 0 0 0.34999949,42.255002 M 5.2799992,49.649989 A 4.9299997,4.9299997 0 0 0 0.34999949,44.719998 M 2.8149995,49.649989 A 2.4649999,2.4649999 0 0 0 0.34999949,47.184993 M 0.34999949,49.649989 H 3.8010003 M 0.34999949,49.649989 H 3.8010003 M 0.34999949,49.649989 H 3.8010003" />
</pattern>
<pattern id="sand" width="50" height="50" patternUnits="userSpaceOnUse">
<path style="fill: white;" d="M 0 0 50 0 50 50 0 50" />
<path

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 67 KiB

@@ -5,6 +5,20 @@
</g>
<g id="door-tag">
<circle cy="1.75" r="5" fill="white" stroke="black" style="stroke-width: 0.25;" />
<line x1="-5" y1="1.75" x2="5" y2="1.75" style="stroke:black; stroke-width: 0.25;" />
<line x1="-5" y1="1.75" x2="5" y2="1.75" style="stroke: black; stroke-width: 0.25;" />
</g>
<g id="elevation-arrow">
<path style="fill: black;" d="M -7.07106 0 0 -7.07106 7.07106 0" />
</g>
<g id="elevation-tag">
<circle r="5" fill="white" stroke="black" style="stroke-width: 0.25;" />
<line x1="-5" y1="0" x2="5" y2="0" style="stroke: black; stroke-width: 0.25;" />
</g>
<g id="section-arrow">
<path style="fill: black;" d="M -7.07106 0 0 -7.07106 7.07106 0" />
</g>
<g id="section-tag">
<circle r="5" fill="white" stroke="black" style="stroke-width: 0.25;" />
<line x1="-5" y1="0" x2="5" y2="0" style="stroke: black; stroke-width: 0.25;" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 572 B

After

Width:  |  Height:  |  Size: 1.2 KiB

@@ -173,7 +173,7 @@
style="stroke-width:0.999999px"
y="1036.1166"
x="3030.9653"
id="tspan2527">{{revision}}</tspan></text>
id="tspan2527">{{Revision}}</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13.3918px;line-height:125%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.999999px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
@@ -183,7 +183,7 @@
id="tspan2531"
x="2804.7021"
y="1036.1166"
style="stroke-width:0.999999px">{{number}}</tspan></text>
style="stroke-width:0.999999px">{{Identification}} - {{Name}}</tspan></text>
</g>
<g
transform="matrix(0.26458333,0,0,0.26458334,-3.180917e-5,297.00003)"

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

@@ -169,7 +169,7 @@
style="stroke-width:0.264583px"
y="397.13919"
x="554.94287"
id="tspan2527">{{revision}}</tspan></text>
id="tspan2527">{{Revision}}</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.54325px;line-height:125%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
@@ -179,7 +179,7 @@
id="tspan2531"
x="495.07742"
y="397.13919"
style="stroke-width:0.264583px">{{number}}</tspan></text>
style="stroke-width:0.264583px">{{Identification}} - {{Name}}</tspan></text>
<g
style="stroke-width:3.77953"
id="g90669"

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

@@ -172,7 +172,7 @@
id="tspan2527"
x="544.94287"
y="284.13919"
style="stroke-width:0.264583px">{{revision}}</tspan></text>
style="stroke-width:0.264583px">{{Revision}}</tspan></text>
<text
id="text2533"
y="284.13919"
@@ -182,7 +182,7 @@
style="stroke-width:0.264583px"
y="284.13919"
x="485.07742"
id="tspan2531">{{number}}</tspan></text>
id="tspan2531">{{Identification}} - {{Name}}</tspan></text>
</g>
<g
style="stroke-width:3.77953"

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

@@ -70,7 +70,7 @@
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.96055365px;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.26458332px"
y="103.10933"
x="32.822662"
id="tspan857">{{no}}</tspan></text>
id="tspan857">{{Identification}}</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:17.71626091px;line-height:125%;font-family:Arial;-inkscape-font-specification:Arial;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458329px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
@@ -80,7 +80,7 @@
id="tspan861"
x="39.12896"
y="100.19173"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.54325247px;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:start;text-anchor:start;stroke-width:0.26458329px">{{name}}</tspan></text>
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.54325247px;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:start;text-anchor:start;stroke-width:0.26458329px">{{Name}}</tspan></text>
<text
id="text867"
y="105.0553"
@@ -90,6 +90,6 @@
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.54325247px;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:start;text-anchor:start;stroke-width:0.26458332px"
y="105.0553"
x="39.285267"
id="tspan865">{{scale}}</tspan></text>
id="tspan865">{{Scale}}</tspan></text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

@@ -0,0 +1,214 @@
# 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/>.
presets = {
"Basic": ["project", "search", "bcf", "attribute", "spatial", "pset", "qto"],
"Admin": [
"project",
"search",
"bcf",
"root",
"unit",
"model",
"georeference",
"context",
"drawing",
"misc",
"attribute",
"type",
"spatial",
"void",
"aggregate",
"geometry",
"cobie",
"resource",
"cost",
"sequence",
"group",
"system",
"structural",
"boundary",
"profile",
"material",
"style",
"layer",
"owner",
"pset",
"qto",
"classification",
"constraint",
"document",
"pset_template",
"clash",
"lca",
"csv",
"bimtester",
"diff",
"patch",
"covetool",
"augin",
"debug",
],
"BIM Coordination": [
"project",
"search",
"bcf",
"root",
"unit",
"georeference",
"attribute",
"type",
"spatial",
"void",
"aggregate",
"owner",
"pset",
"qto",
"clash",
"csv",
"bimtester",
"diff",
"patch",
"debug",
],
"Architecture": [
"project",
"search",
"bcf",
"root",
"unit",
"model",
"georeference",
"context",
"drawing",
"misc",
"attribute",
"type",
"spatial",
"void",
"aggregate",
"geometry",
"group",
"system",
"boundary",
"profile",
"material",
"style",
"layer",
"owner",
"pset",
"qto",
"classification",
"pset_template",
"clash",
"csv",
],
"Structural Engineering": [
"project",
"search",
"bcf",
"root",
"unit",
"model",
"georeference",
"context",
"drawing",
"misc",
"attribute",
"type",
"spatial",
"void",
"aggregate",
"geometry",
"group",
"system",
"structural",
"boundary",
"profile",
"material",
"style",
"layer",
"owner",
"pset",
"qto",
"classification",
"pset_template",
"clash",
"csv",
],
"MEP": [
"project",
"search",
"bcf",
"root",
"unit",
"model",
"georeference",
"context",
"drawing",
"misc",
"attribute",
"type",
"spatial",
"void",
"aggregate",
"geometry",
"group",
"system",
"boundary",
"profile",
"material",
"style",
"layer",
"owner",
"pset",
"qto",
"classification",
"pset_template",
"clash",
"csv",
],
"Quantity Surveying": ["project", "search", "attribute", "spatial", "void", "pset", "qto", "classification", "csv"],
"Model Enrichment": [
"project",
"search",
"root",
"attribute",
"spatial",
"pset",
"qto",
"pset_template",
"csv",
"patch",
],
"3D Visualisation": ["project", "search", "bcf", "attribute", "spatial", "pset", "qto"],
"4D Scheduling": ["project", "search", "attribute", "spatial", "resource", "cost", "sequence", "pset", "qto"],
"5D Cost Management": [
"project",
"search",
"attribute",
"spatial",
"resource",
"cost",
"sequence",
"pset",
"qto",
"lca",
],
"Facility Management": ["project", "search", "attribute", "spatial", "cobie", "pset", "qto", "lca"],
}
+72 -77
View File
@@ -27,7 +27,13 @@ import addon_utils
import ifcopenshell
import ifcopenshell.api
import ifcopenshell.util.placement
import blenderbim.tool as tool
import blenderbim.core.geometry
import blenderbim.core.aggregate
import blenderbim.core.spatial
import blenderbim.core.style
from blenderbim.bim.ifc import IfcStore
from mathutils import Vector
class IfcExporter:
@@ -37,20 +43,22 @@ class IfcExporter:
def export(self):
self.file = IfcStore.get_file()
self.set_header()
IfcStore.update_cache()
if bpy.context.scene.BIMProjectProperties.is_authoring:
self.sync_object_placements_and_deletions()
self.sync_deletions()
self.sync_all_objects()
self.sync_edited_objects()
extension = self.ifc_export_settings.output_file.split(".")[-1]
extension = self.ifc_export_settings.output_file.split(".")[-1].lower()
if extension == "ifczip":
with tempfile.TemporaryDirectory() as unzipped_path:
filename, ext = os.path.splitext(os.path.basename(self.ifc_export_settings.output_file))
tmp_name = "{}.ifc".format(filename)
tmp_file = os.path.join(unzipped_path, tmp_name)
tmp_filename = f"{filename}.ifc"
tmp_file = os.path.join(unzipped_path, tmp_filename)
self.file.write(tmp_file)
with zipfile.ZipFile(
self.ifc_export_settings.output_file, mode="w", compression=zipfile.ZIP_DEFLATED, compresslevel=9
) as zf:
zf.write(tmp_file)
zf.write(tmp_file, arcname=tmp_filename)
elif extension == "ifc":
self.file.write(self.ifc_export_settings.output_file)
elif extension == "ifcjson":
@@ -79,103 +87,90 @@ class IfcExporter:
self.get_application_name(), self.get_application_version()
)
def sync_object_placements_and_deletions(self):
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(self.file)
to_delete = []
def sync_deletions(self):
results = []
for ifc_definition_id in IfcStore.deleted_ids:
try:
product = self.file.by_id(ifc_definition_id)
if hasattr(product, "GlobalId"):
results.append(product.GlobalId)
except:
continue
ifcopenshell.api.run("root.remove_product", self.file, **{"product": product})
IfcStore.deleted_ids.clear()
return results
for ifc_definition_id, obj in IfcStore.id_map.items():
def sync_all_objects(self):
results = []
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(self.file)
for ifc_definition_id in list(IfcStore.id_map.keys()):
obj = IfcStore.id_map[ifc_definition_id]
try:
if isinstance(obj, bpy.types.Material):
continue
self.sync_object_placement(obj)
self.sync_object_container(ifc_definition_id, obj)
tool.Collector.sync(obj)
result = self.sync_object_placement(obj)
if result:
results.append(result)
result = self.sync_object_material(obj)
if result:
results.append(result)
except ReferenceError:
pass # The object is likely deleted
if self.should_delete(obj):
to_delete.append(ifc_definition_id)
for ifc_definition_id in to_delete:
product = self.file.by_id(ifc_definition_id)
IfcStore.unlink_element(product)
ifcopenshell.api.run("root.remove_product", self.file, **{"product": product})
return results
def sync_edited_objects(self):
results = []
for obj in IfcStore.edited_objs.copy():
if not obj:
continue
try:
if isinstance(obj, bpy.types.Material):
bpy.ops.bim.update_style_colours(material=obj.name)
blenderbim.core.style.update_style_colours(tool.Ifc, tool.Style, obj=obj)
else:
element = tool.Ifc.get_entity(obj)
if element:
results.append(element)
bpy.ops.bim.update_representation(obj=obj.name)
except ReferenceError:
pass # The object is likely deleted
IfcStore.edited_objs.clear()
return results
def sync_object_material(self, obj):
if not obj.data or not isinstance(obj.data, bpy.types.Mesh):
return
if not self.has_changed_materials(obj):
return
bpy.ops.bim.update_representation(obj=obj.name)
def has_changed_materials(self, obj):
checksum = obj.data.BIMMeshProperties.material_checksum
return checksum != str([s.id() for s in tool.Geometry.get_styles(obj) if s])
def sync_object_placement(self, obj):
if not tool.Ifc.is_moved(obj):
return
blender_matrix = np.array(obj.matrix_world)
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
if (obj.scale - Vector((1.0, 1.0, 1.0))).length > 1e-4:
bpy.ops.bim.update_representation(obj=obj.name)
return element
if element.is_a("IfcGridAxis"):
return self.sync_grid_axis_object_placement(obj, element)
if not hasattr(element, "ObjectPlacement"):
return
ifc_matrix = ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement)
ifc_matrix[0][3] *= self.unit_scale
ifc_matrix[1][3] *= self.unit_scale
ifc_matrix[2][3] *= self.unit_scale
blenderbim.core.geometry.edit_object_placement(tool.Ifc, tool.Geometry, tool.Surveyor, obj=obj)
return element
props = bpy.context.scene.BIMGeoreferenceProperties
if props.has_blender_offset and obj.BIMObjectProperties.blender_offset_type == "OBJECT_PLACEMENT":
ifc_matrix = ifcopenshell.util.geolocation.global2local(
ifc_matrix,
float(props.blender_eastings) * self.unit_scale,
float(props.blender_northings) * self.unit_scale,
float(props.blender_orthogonal_height) * self.unit_scale,
float(props.blender_x_axis_abscissa),
float(props.blender_x_axis_ordinate),
)
if not np.allclose(ifc_matrix, blender_matrix, atol=0.0001):
bpy.ops.bim.edit_object_placement(obj=obj.name)
def sync_object_container(self, guid, obj):
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
element_collection = bpy.data.collections.get(obj.name)
if self.file.schema == "IFC2X3":
if element.is_a("IfcProject"):
return
elif element.is_a("IfcContext"):
return
if (
(element.is_a("IfcElement") and element_collection)
or element.is_a("IfcSpatialStructureElement")
or element.is_a("IfcGrid")
):
try:
parent_collection = [c for c in bpy.data.collections if c.children.get(element_collection.name)][0]
except:
return # Out of the spatial tree
else:
parent_collection = obj.users_collection[0]
parent_obj = bpy.data.objects.get(parent_collection.name)
if not parent_obj or not parent_obj.BIMObjectProperties.ifc_definition_id:
return
parent = self.file.by_id(parent_obj.BIMObjectProperties.ifc_definition_id)
if parent.is_a("IfcSpatialStructureElement") and not element.is_a("IfcSpatialStructureElement"):
if parent != ifcopenshell.util.element.get_container(element):
bpy.ops.bim.assign_container(relating_structure=parent.id(), related_element=obj.name)
elif parent != ifcopenshell.util.element.get_aggregate(element):
bpy.ops.bim.assign_object(relating_object=parent_obj.name, related_object=obj.name)
def should_delete(self, obj):
try:
# This will throw an exception if the Blender object no longer exists
foo = obj.name
foo
return False
except:
return True
def sync_grid_axis_object_placement(self, obj, element):
grid = (element.PartOfU or element.PartOfV or element.PartOfW)[0]
grid_obj = tool.Ifc.get_object(grid)
if grid_obj:
self.sync_object_placement(grid_obj)
if grid_obj.matrix_world != obj.matrix_world:
bpy.ops.bim.update_representation(obj=obj.name)
tool.Geometry.record_object_position(obj)
def get_application_name(self):
return "BlenderBIM"
+14 -24
View File
@@ -26,9 +26,7 @@ from blenderbim.bim.module.drawing.prop import RasterStyleProperty
from bpy.app.handlers import persistent
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.module.owner.prop import get_user_person, get_user_organisation
from ifcopenshell.api.attribute.data import Data as AttributeData
from ifcopenshell.api.material.data import Data as MaterialData
from ifcopenshell.api.style.data import Data as StyleData
from ifcopenshell.api.type.data import Data as TypeData
@@ -50,9 +48,7 @@ def mode_callback(obj, data):
):
continue
if obj.data.BIMMeshProperties.ifc_definition_id:
representation = IfcStore.get_file().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
if representation.RepresentationType in ["Tessellation", "Brep", "Annotation2D"]:
IfcStore.edited_objs.add(obj)
IfcStore.edited_objs.add(obj)
elif IfcStore.get_file().by_id(obj.BIMObjectProperties.ifc_definition_id).is_a("IfcGridAxis"):
IfcStore.edited_objs.add(obj)
@@ -70,11 +66,10 @@ def name_callback(obj, data):
if isinstance(obj, bpy.types.Material):
if obj.BIMObjectProperties.ifc_definition_id:
IfcStore.get_file().by_id(obj.BIMObjectProperties.ifc_definition_id).Name = obj.name
AttributeData.load(IfcStore.get_file(), obj.BIMObjectProperties.ifc_definition_id)
MaterialData.load_materials()
if obj.BIMMaterialProperties.ifc_style_id:
IfcStore.get_file().by_id(obj.BIMMaterialProperties.ifc_style_id).Name = obj.name
StyleData.load(IfcStore.get_file(), obj.BIMMaterialProperties.ifc_style_id)
refresh_ui_data()
return
if not obj.BIMObjectProperties.ifc_definition_id or "/" not in obj.name:
@@ -98,7 +93,7 @@ def name_callback(obj, data):
if element.is_a("IfcTypeProduct"):
TypeData.purge()
element.Name = "/".join(obj.name.split("/")[1:])
AttributeData.load(IfcStore.get_file(), obj.BIMObjectProperties.ifc_definition_id)
refresh_ui_data()
def color_callback(obj, data):
@@ -107,13 +102,6 @@ def color_callback(obj, data):
def active_object_callback():
obj = bpy.context.active_object
for obj in bpy.context.selected_objects:
if not obj.BIMObjectProperties.ifc_definition_id:
continue
stored_obj = IfcStore.get_element(obj.BIMObjectProperties.ifc_definition_id)
if stored_obj and stored_obj != obj:
bpy.ops.bim.copy_class(obj=obj.name)
refresh_ui_data()
@@ -169,12 +157,12 @@ def loadIfcStore(scene):
if not IfcStore.get_file():
return
IfcStore.get_schema()
IfcStore.reload_linked_elements()
IfcStore.relink_all_objects()
@persistent
def undo_pre(scene):
IfcStore.update_undo_redo_stack_objects()
IfcStore.track_undo_redo_stack_object_map()
@persistent
@@ -183,13 +171,13 @@ def undo_post(scene):
IfcStore.last_transaction = bpy.context.scene.BIMProperties.last_transaction
IfcStore.undo()
purge_module_data()
IfcStore.update_undo_redo_stack_objects()
IfcStore.reload_linked_elements(objects=[bpy.data.objects.get(o) for o in IfcStore.undo_redo_stack_objects])
IfcStore.track_undo_redo_stack_selected_objects()
IfcStore.reload_undo_redo_stack_objects()
@persistent
def redo_pre(scene):
IfcStore.update_undo_redo_stack_objects()
IfcStore.track_undo_redo_stack_object_map()
@persistent
@@ -198,8 +186,8 @@ def redo_post(scene):
IfcStore.last_transaction = bpy.context.scene.BIMProperties.last_transaction
IfcStore.redo()
purge_module_data()
IfcStore.update_undo_redo_stack_objects()
IfcStore.reload_linked_elements(objects=[bpy.data.objects.get(o) for o in IfcStore.undo_redo_stack_objects])
IfcStore.track_undo_redo_stack_selected_objects()
IfcStore.reload_undo_redo_stack_objects()
@persistent
@@ -209,6 +197,7 @@ def ensureIfcExported(scene):
def get_application(ifc):
# TODO: cache this for even faster application retrieval. It honestly makes a difference on long scripts.
version = get_application_version()
for element in ifc.by_type("IfcApplication"):
if element.ApplicationIdentifier == "BlenderBIM" and element.Version == version:
@@ -216,7 +205,7 @@ def get_application(ifc):
return ifcopenshell.api.run(
"owner.add_application",
ifc,
version=get_application_version(),
version=version,
application_full_name="BlenderBIM Add-on",
application_identifier="BlenderBIM",
)
@@ -244,6 +233,7 @@ def setDefaultProperties(scene):
)
ifcopenshell.api.owner.settings.get_user = lambda ifc: core_owner.get_user(tool.Owner)
ifcopenshell.api.owner.settings.get_application = get_application
# TODO: Move to drawing module
if len(bpy.context.scene.DocProperties.drawing_styles) == 0:
drawing_style = bpy.context.scene.DocProperties.drawing_styles.add()
drawing_style.name = "Technical"
@@ -297,7 +287,7 @@ def setDefaultProperties(scene):
RasterStyleProperty.SHADING_SHADOW_INTENSITY.value: 0.5,
RasterStyleProperty.DISPLAY_LIGHT_DIRECTION.value: (0.5, 0.5, 0.5),
RasterStyleProperty.VIEW_USE_CURVE_MAPPING.value: False,
RasterStyleProperty.OVERLAY_SHOW_WIREFRAMES.value: True,
RasterStyleProperty.OVERLAY_SHOW_WIREFRAMES.value: False,
RasterStyleProperty.OVERLAY_WIREFRAME_THRESHOLD.value: 0,
RasterStyleProperty.OVERLAY_SHOW_FLOOR.value: False,
RasterStyleProperty.OVERLAY_SHOW_AXIS_X.value: False,
+85 -27
View File
@@ -19,6 +19,7 @@
import bpy
import json
import math
import zipfile
import ifcopenshell
import ifcopenshell.util.attribute
from mathutils import geometry
@@ -46,37 +47,52 @@ def draw_attribute(attribute, layout, copy_operator=None):
layout.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
if copy_operator:
op = layout.operator(f"{copy_operator}", text="", icon="COPYDOWN")
op.data = json.dumps({"name": attribute.name, "value": attribute.get_value(), "is_null": attribute.is_null})
op.name = attribute.name
if attribute.is_uri:
op = layout.operator("bim.select_uri_attribute", text="", icon="FILE_FOLDER")
op.data_path = attribute.path_from_id("string_value")
def import_attributes(ifc_class, props, data, callback=None):
for attribute in IfcStore.get_schema().declaration_by_name(ifc_class).all_attributes():
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
if isinstance(data_type, tuple) or data_type == "entity":
callback(attribute.name(), None, data) if callback else None
continue
new = props.add()
new.name = attribute.name()
new.is_null = data[attribute.name()] is None
new.is_optional = attribute.optional()
new.data_type = data_type if isinstance(data_type, str) else ""
is_handled_by_callback = callback(attribute.name(), new, data) if callback else None
if is_handled_by_callback:
pass # Our job is done
elif is_handled_by_callback is False:
props.remove(len(props) - 1)
elif data_type == "string":
new.string_value = "" if new.is_null else data[attribute.name()]
elif data_type == "boolean":
new.bool_value = False if new.is_null else data[attribute.name()]
elif data_type == "integer":
new.int_value = 0 if new.is_null else data[attribute.name()]
elif data_type == "float":
new.float_value = 0.0 if new.is_null else data[attribute.name()]
elif data_type == "enum":
new.enum_items = json.dumps(ifcopenshell.util.attribute.get_enum_items(attribute))
if data[attribute.name()]:
new.enum_value = data[attribute.name()]
import_attribute(attribute, props, data, callback=callback)
# A more elegant attribute importer signature, intended to supersede import_attributes
def import_attributes2(element, props, callback=None):
for attribute in element.wrapped_data.declaration().as_entity().all_attributes():
import_attribute(attribute, props, element.get_info(), callback=callback)
def import_attribute(attribute, props, data, callback=None):
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
if isinstance(data_type, tuple) or data_type == "entity":
callback(attribute.name(), None, data) if callback else None
return
new = props.add()
new.name = attribute.name()
new.is_null = data[attribute.name()] is None
new.is_optional = attribute.optional()
new.data_type = data_type if isinstance(data_type, str) else ""
is_handled_by_callback = callback(attribute.name(), new, data) if callback else None
if is_handled_by_callback:
pass # Our job is done
elif is_handled_by_callback is False:
props.remove(len(props) - 1)
elif data_type == "string":
new.string_value = "" if new.is_null else data[attribute.name()]
if attribute.type_of_attribute().declared_type().name() == "IfcURIReference":
new.is_uri = True
elif data_type == "boolean":
new.bool_value = False if new.is_null else data[attribute.name()]
elif data_type == "integer":
new.int_value = 0 if new.is_null else data[attribute.name()]
elif data_type == "float":
new.float_value = 0.0 if new.is_null else data[attribute.name()]
elif data_type == "enum":
new.enum_items = json.dumps(ifcopenshell.util.attribute.get_enum_items(attribute))
if data[attribute.name()]:
new.enum_value = data[attribute.name()]
def export_attributes(props, callback=None):
@@ -87,3 +103,45 @@ def export_attributes(props, callback=None):
continue # Our job is done
attributes[prop.name] = prop.get_value()
return attributes
class IfcHeaderExtractor:
def __init__(self, filepath: str):
self.filepath = filepath
def extract(self):
extension = self.filepath.split(".")[-1]
if extension.lower() == "ifc":
with open(self.filepath) as ifc_file:
return self.extract_ifc_spf(ifc_file)
elif extension.lower() == "ifczip":
return self.extract_ifc_zip()
def extract_ifc_spf(self, ifc_file):
# https://www.steptools.com/stds/step/IS_final_p21e3.html#clause-8
data = {}
max_lines_to_parse = 50
for _ in range(max_lines_to_parse):
line = next(ifc_file)
if isinstance(line, bytes):
line = line.decode("utf-8")
if line.startswith("FILE_DESCRIPTION"):
for i, part in enumerate(line.split("'")):
if i == 1:
data["description"] = part
elif i == 3:
data["implementation_level"] = part
elif line.startswith("FILE_NAME"):
for i, part in enumerate(line.split("'")):
if i == 1:
data["name"] = part
elif i == 3:
data["time_stamp"] = part
elif line.startswith("FILE_SCHEMA"):
data["schema_name"] = line.split("'")[1]
break
return data
def extract_ifc_zip(self):
archive = zipfile.ZipFile(self.filepath, "r")
return self.extract_ifc_spf(archive.open(archive.filelist[0]))
+139 -16
View File
@@ -1,5 +1,5 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
# Copyright (C) 2020, 2021, 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of BlenderBIM Add-on.
#
@@ -16,8 +16,10 @@
# 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 uuid
import hashlib
import zipfile
import tempfile
import ifcopenshell
@@ -29,8 +31,11 @@ class IfcStore:
path = ""
file = None
schema = None
cache = None
cache_path = None
id_map = {}
guid_map = {}
deleted_ids = set()
edited_objs = set()
pset_template_path = ""
pset_template_file = None
@@ -38,28 +43,36 @@ class IfcStore:
library_file = None
element_listeners = set()
undo_redo_stack_objects = set()
undo_redo_stack_object_names = {}
current_transaction = ""
last_transaction = ""
history = []
future = []
schema_identifiers = ["IFC4", "IFC2X3"]
@staticmethod
def purge():
IfcStore.path = ""
IfcStore.file = None
IfcStore.schema = None
IfcStore.cache = None
IfcStore.cache_path = None
IfcStore.id_map = {}
IfcStore.guid_map = {}
IfcStore.deleted_ids = set()
IfcStore.edited_objs = set()
IfcStore.pset_template_path = ""
IfcStore.pset_template_file = None
IfcStore.library_path = ""
IfcStore.library_file = None
IfcStore.schema_identifiers = ["IFC4", "IFC2X3"]
@staticmethod
def get_file():
if IfcStore.file is None:
IfcStore.path = bpy.context.scene.BIMProperties.ifc_file
if not os.path.isabs(IfcStore.path):
IfcStore.path = os.path.abspath(os.path.join(bpy.path.abspath("//"), IfcStore.path))
if IfcStore.path:
try:
IfcStore.load_file(IfcStore.path)
@@ -67,6 +80,31 @@ class IfcStore:
pass
return IfcStore.file
@staticmethod
def get_cache():
if IfcStore.cache is None and IfcStore.path:
ifc_key = IfcStore.path + IfcStore.file.wrapped_data.header.file_name.time_stamp
ifc_hash = hashlib.md5(ifc_key.encode("utf-8")).hexdigest()
IfcStore.cache_path = os.path.join(bpy.context.scene.BIMProperties.data_dir, "cache", f"{ifc_hash}.h5")
cache_settings = ifcopenshell.geom.settings()
cache_settings.set(cache_settings.STRICT_TOLERANCE, True)
try:
IfcStore.cache = ifcopenshell.geom.serializers.hdf5(IfcStore.cache_path, cache_settings)
except:
return
return IfcStore.cache
@staticmethod
def update_cache():
if not IfcStore.cache:
return
ifc_key = IfcStore.path + IfcStore.file.wrapped_data.header.file_name.time_stamp
ifc_hash = hashlib.md5(ifc_key.encode("utf-8")).hexdigest()
new_cache_path = os.path.join(bpy.context.scene.BIMProperties.data_dir, "cache", f"{ifc_hash}.h5")
IfcStore.cache = None
os.replace(IfcStore.cache_path, new_cache_path)
IfcStore.get_cache()
@staticmethod
def load_file(path):
extension = path.split(".")[-1]
@@ -85,7 +123,9 @@ class IfcStore:
@staticmethod
def get_schema():
if IfcStore.file is None:
return
IfcStore.schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(
bpy.context.scene.BIMProjectProperties.export_schema
)
elif IfcStore.schema is None:
IfcStore.schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(IfcStore.file.schema)
return IfcStore.schema
@@ -108,14 +148,32 @@ class IfcStore:
IfcStore.element_listeners.add(callback)
@staticmethod
def update_undo_redo_stack_objects():
def track_undo_redo_stack_object_map():
"""Keeps track of currently mapped object names, typically during undo and redo
When any Blender object is stored outside a Blender PointerProperty, such as
in a regular Python list, there is the likely probability that the object
will be invalidated when undo or redo occurs. Object invalidation seems to
occur whenever an object is affected during an operation.
For example, if an operator deletes a modifier on o1, then o1 will be invalidated.
"""
for key, value in IfcStore.id_map.items():
try:
IfcStore.undo_redo_stack_object_names[key] = value.name
except:
continue
@staticmethod
def track_undo_redo_stack_selected_objects():
"""Keeps track of selected object names, typically during undo and redo
When any Blender object is stored outside a Blender PointerProperty, such as
in a regular Python list, there is the likely probability that the object
will be invalidated when undo or redo occurs. Object invalidation seems to
only occur for selected objects either pre/post undo/redo event, including
selected objects for consecutive undo/redos, and all children.
occur for selected objects either pre/post undo/redo event, including
selected objects for consecutive undo/redos, and all children. This is
important because selected objects are often deleted from the scene.
So if I first select o1, then o2, then o3, then press undo, o3 will be
invalidated. If instead I press undo twice, o3 and o2 will be invalidated.
@@ -130,14 +188,23 @@ class IfcStore:
IfcStore.undo_redo_stack_objects |= objects
@staticmethod
def reload_linked_elements(objects=None):
def reload_undo_redo_stack_objects():
"""Reloads any invalidated objects after undo or redo
After an undo or redo operation, objects may have been invalidated in
our id_map and guid_map. Invalidated objects are typically those that
have been manipulated or deleted. This checks the cache of mapped and
selected objects prior to the operation and ensures that if the object
is invalidated, they are reloaded based on the object name that was
tracked prior to the undo / redo.
"""
file = IfcStore.get_file()
if not file:
return
if objects is None:
objects = bpy.data.objects
for obj in objects:
# First, reload objects that were selected or active
for name in IfcStore.undo_redo_stack_objects:
obj = bpy.data.objects.get(name)
if not obj:
continue
if not obj.BIMObjectProperties.ifc_definition_id:
@@ -148,6 +215,62 @@ class IfcStore:
data["guid"] = element.GlobalId
IfcStore.commit_link_element(data)
# Scan for any straggling invalidated objects which were indirectly affected and reload them too.
for key, value in IfcStore.id_map.items():
try:
value.name
except:
obj = bpy.data.objects.get(IfcStore.undo_redo_stack_object_names[key])
if not obj or not obj.BIMObjectProperties.ifc_definition_id:
continue
element = file.by_id(obj.BIMObjectProperties.ifc_definition_id)
data = {"id": element.id(), "obj": obj.name}
if hasattr(element, "GlobalId"):
data["guid"] = element.GlobalId
IfcStore.commit_link_element(data)
@staticmethod
def relink_all_objects():
if not IfcStore.get_file():
return
for obj in bpy.data.objects:
IfcStore.relink_object(obj)
for obj in bpy.data.materials:
IfcStore.relink_object(obj)
@staticmethod
def relink_object(obj):
if not obj:
return
if obj.BIMObjectProperties.ifc_definition_id:
element = IfcStore.get_file().by_id(obj.BIMObjectProperties.ifc_definition_id)
data = {"id": element.id(), "obj": obj.name}
if hasattr(element, "GlobalId"):
data["guid"] = element.GlobalId
IfcStore.commit_link_element(data)
if hasattr(obj, "BIMMaterialProperties") and obj.BIMMaterialProperties.ifc_style_id:
element = IfcStore.get_file().by_id(obj.BIMMaterialProperties.ifc_style_id)
data = {"id": element.id(), "obj": obj.name}
IfcStore.commit_link_element(data)
@staticmethod
def delete_element(element):
IfcStore.deleted_ids.add(element.id())
if IfcStore.history:
data = {"id": element.id()}
IfcStore.history[-1]["operations"].append(
{"rollback": IfcStore.rollback_delete_element, "commit": IfcStore.commit_delete_element, "data": data}
)
@staticmethod
def rollback_delete_element(data):
IfcStore.deleted_ids.remove(data["id"])
@staticmethod
def commit_delete_element(data):
IfcStore.deleted_ids.add(data["id"])
@staticmethod
def link_element(element, obj):
IfcStore.id_map[element.id()] = obj
@@ -184,6 +307,8 @@ class IfcStore:
@staticmethod
def commit_link_element(data):
obj = bpy.data.objects.get(data["obj"])
if not obj:
obj = bpy.data.materials.get(data["obj"])
IfcStore.id_map[data["id"]] = obj
if "guid" in data:
IfcStore.guid_map[data["guid"]] = obj
@@ -224,7 +349,9 @@ class IfcStore:
except:
pass
if obj:
if element and element.is_a("IfcSurfaceStyle"):
obj.BIMMaterialProperties.ifc_style_id = 0
elif obj:
obj.BIMObjectProperties.ifc_definition_id = 0
@staticmethod
@@ -232,7 +359,6 @@ class IfcStore:
is_top_level_operator = not bool(IfcStore.current_transaction)
if is_top_level_operator:
active_object = context.active_object
IfcStore.begin_transaction(operator)
IfcStore.get_file().begin_transaction()
# This empty transaction ensures that each operator has at least one transaction
@@ -248,17 +374,14 @@ class IfcStore:
operator, rollback=lambda d: IfcStore.get_file().undo(), commit=lambda d: IfcStore.get_file().redo()
)
IfcStore.end_transaction(operator)
try:
active_object.name
context.view_layer.objects.active = active_object
except:
pass
blenderbim.bim.handler.refresh_ui_data()
return result
@staticmethod
def begin_transaction(operator):
IfcStore.undo_redo_stack_objects = set()
IfcStore.undo_redo_stack_object_names = {}
IfcStore.current_transaction = str(uuid.uuid4())
operator.transaction_key = IfcStore.current_transaction
File diff suppressed because it is too large Load Diff
@@ -17,20 +17,25 @@
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import bpy
from . import ui, operator
from . import ui, prop, operator
classes = (
operator.AssignObject,
operator.EnableEditingAggregate,
operator.DisableEditingAggregate,
operator.AddAggregate,
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,
prop.BIMObjectAggregateProperties,
ui.BIM_PT_aggregate,
)
def register():
pass
bpy.types.Object.BIMObjectAggregateProperties = bpy.props.PointerProperty(type=prop.BIMObjectAggregateProperties)
def unregister():
pass
del bpy.types.Object.BIMObjectAggregateProperties
@@ -0,0 +1,81 @@
# 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 blenderbim.tool as tool
import ifcopenshell.util.element
def refresh():
AggregateData.is_loaded = False
class AggregateData:
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(),
"relating_object_id": cls.get_relating_object_id(),
"has_related_objects": cls.has_related_objects(),
"related_objects_amount": cls.get_related_objects_amount(),
"ifc_class": cls.ifc_class(),
}
cls.is_loaded = True
@classmethod
def get_relating_object(cls):
return ifcopenshell.util.element.get_aggregate(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:
aggregate = cls.get_relating_object()
if aggregate:
return f"{aggregate.is_a()}/{aggregate.Name or ''}"
@classmethod
def get_relating_object_id(cls) -> int:
aggregate = cls.get_relating_object()
if aggregate:
return aggregate.id()
@classmethod
def get_related_objects(cls):
return ifcopenshell.util.element.get_parts(tool.Ifc.get_entity(bpy.context.active_object))
@classmethod
def get_related_objects_amount(cls):
parts = cls.get_related_objects()
return len(parts) if parts else 0
@classmethod
def has_related_objects(cls) -> bool:
return bool(cls.get_related_objects())
@classmethod
def ifc_class(cls) -> str:
element = tool.Ifc.get_entity(bpy.context.active_object)
if element:
return element.is_a()
@@ -17,92 +17,84 @@
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import bpy
import ifcopenshell.api
import ifcopenshell
import ifcopenshell.util.element
import blenderbim.tool as tool
import blenderbim.core.aggregate as core
import blenderbim.core.spatial
import blenderbim.bim.handler
from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.aggregate.data import Data
class AssignObject(bpy.types.Operator):
class Operator:
def execute(self, context):
IfcStore.execute_ifc_operator(self, context)
blenderbim.bim.handler.refresh_ui_data()
return {"FINISHED"}
class BIM_OT_assign_object(bpy.types.Operator, Operator):
"""Create aggregation relationship between two ifc elements"""
bl_idname = "bim.assign_object"
bl_label = "Assign Object"
bl_options = {"REGISTER", "UNDO"}
relating_object: bpy.props.StringProperty()
related_object: bpy.props.StringProperty()
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
relating_object: bpy.props.IntProperty()
related_object: bpy.props.IntProperty()
def _execute(self, context):
self.file = IfcStore.get_file()
related_objects = (
[bpy.data.objects.get(self.related_object)] if self.related_object else context.selected_objects
core.assign_object(
tool.Ifc,
tool.Aggregate,
tool.Collector,
relating_obj=tool.Ifc.get_object(tool.Ifc.get().by_id(self.relating_object)),
related_obj=tool.Ifc.get_object(tool.Ifc.get().by_id(self.related_object)),
)
relating_object = bpy.data.objects.get(self.relating_object)
if not relating_object or not relating_object.BIMObjectProperties.ifc_definition_id:
return {"FINISHED"}
for related_object in related_objects:
oprops = related_object.BIMObjectProperties
product = self.file.by_id(oprops.ifc_definition_id)
ifcopenshell.api.run(
"aggregate.assign_object",
self.file,
**{
"product": product,
"relating_object": self.file.by_id(relating_object.BIMObjectProperties.ifc_definition_id),
},
)
bpy.ops.bim.edit_object_placement(obj=related_object.name)
Data.load(IfcStore.get_file(), oprops.ifc_definition_id)
bpy.ops.bim.disable_editing_aggregate(obj=related_object.name)
spatial_collection = bpy.data.collections.get(related_object.name)
relating_collection = bpy.data.collections.get(relating_object.name)
if spatial_collection:
self.remove_collection(context.scene.collection, spatial_collection)
for collection in bpy.data.collections:
if collection == relating_collection:
if not collection.children.get(spatial_collection.name):
collection.children.link(spatial_collection)
continue
self.remove_collection(collection, spatial_collection)
else:
for collection in related_object.users_collection:
if collection.name.startswith("Ifc"):
collection.objects.unlink(related_object)
relating_collection.objects.link(related_object)
return {"FINISHED"}
def remove_collection(self, parent, child):
try:
parent.children.unlink(child)
except:
pass
class EnableEditingAggregate(bpy.types.Operator):
class BIM_OT_unassign_object(bpy.types.Operator, Operator):
"""Remove aggregation relationship between two ifc elements"""
bl_idname = "bim.unassign_object"
bl_label = "Unassign Object"
bl_options = {"REGISTER", "UNDO"}
relating_object: bpy.props.IntProperty()
related_object: bpy.props.IntProperty()
def _execute(self, context):
core.unassign_object(
tool.Ifc,
tool.Collector,
relating_obj=tool.Ifc.get_object(tool.Ifc.get().by_id(self.relating_object)),
related_obj=tool.Ifc.get_object(tool.Ifc.get().by_id(self.related_object)),
)
class BIM_OT_enable_editing_aggregate(bpy.types.Operator, Operator):
"""Enable editing aggregation relationship"""
bl_idname = "bim.enable_editing_aggregate"
bl_label = "Enable Editing Aggregate"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
context.active_object.BIMObjectProperties.relating_object = None
context.active_object.BIMObjectProperties.is_editing_aggregate = True
return {"FINISHED"}
def _execute(self, context):
core.enable_editing_aggregate(tool.Aggregate, obj=context.active_object)
class DisableEditingAggregate(bpy.types.Operator):
class BIM_OT_disable_editing_aggregate(bpy.types.Operator, Operator):
"""Disable editing aggregation relationship"""
bl_idname = "bim.disable_editing_aggregate"
bl_label = "Disable Editing Aggregate"
obj: bpy.props.StringProperty()
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
obj.BIMObjectProperties.is_editing_aggregate = False
return {"FINISHED"}
def _execute(self, context):
core.disable_editing_aggregate(tool.Aggregate, obj=context.active_object)
class AddAggregate(bpy.types.Operator):
class BIM_OT_add_aggregate(bpy.types.Operator):
"""Add aggregate to IFC element"""
bl_idname = "bim.add_aggregate"
bl_label = "Add Aggregate"
bl_options = {"REGISTER", "UNDO"}
@@ -113,10 +105,99 @@ class AddAggregate(bpy.types.Operator):
def _execute(self, context):
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
element = tool.Ifc.get_entity(obj)
if not element:
return {"FINISHED"}
aggregate = self.create_aggregate(context)
tool.Collector.sync(obj)
current_aggregate = ifcopenshell.util.element.get_aggregate(element)
current_container = ifcopenshell.util.element.get_container(element)
if current_aggregate:
core.assign_object(
tool.Ifc,
tool.Aggregate,
tool.Collector,
relating_obj=tool.Ifc.get_object(current_aggregate),
related_obj=aggregate,
)
elif current_container:
blenderbim.core.spatial.assign_container(
tool.Ifc,
tool.Collector,
tool.Spatial,
structure_obj=tool.Ifc.get_object(current_container),
element_obj=aggregate,
)
core.assign_object(tool.Ifc, tool.Aggregate, tool.Collector, relating_obj=aggregate, related_obj=obj)
return {"FINISHED"}
def create_aggregate(self, context):
aggregate_collection = bpy.data.collections.new("IfcElementAssembly/Assembly")
context.scene.collection.children.link(aggregate_collection)
aggregate = bpy.data.objects.new("Assembly", None)
aggregate_collection.objects.link(aggregate)
bpy.ops.bim.assign_class(obj=aggregate.name, ifc_class="IfcElementAssembly")
bpy.ops.bim.assign_object(related_object=obj.name, relating_object=aggregate.name)
return aggregate
class BIM_OT_select_parts(bpy.types.Operator):
"""Select Parts"""
bl_idname = "bim.select_parts"
bl_label = "Select Parts"
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
parts = ifcopenshell.util.element.get_parts(tool.Ifc.get_entity(obj))
parts_objs = set(tool.Ifc.get_object(part) for part in parts)
selectable_parts_objs = set(context.selectable_objects).intersection(parts_objs)
for selectable_part_obj in selectable_parts_objs:
selectable_part_obj.select_set(True)
return {"FINISHED"}
class BIM_OT_select_aggregate(bpy.types.Operator):
"""Select Aggregate"""
bl_idname = "bim.select_aggregate"
bl_label = "Select Aggregate"
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
aggregate = ifcopenshell.util.element.get_aggregate(tool.Ifc.get_entity(obj))
aggregate_obj = tool.Ifc.get_object(aggregate)
if aggregate_obj in context.selectable_objects:
aggregate_obj.select_set(True)
return {"FINISHED"}
class BIM_OT_add_part_to_object(bpy.types.Operator, Operator):
bl_idname = "bim.add_part_to_object"
bl_label = "Add Part to Object"
bl_options = {"REGISTER", "UNDO"}
part_class: bpy.props.StringProperty(name="Class", options={"HIDDEN"})
part_name: bpy.props.StringProperty(name="Name")
obj: bpy.props.StringProperty(options={"HIDDEN"})
def invoke(self, context, event):
self.part_name = "My " + self.part_class.lstrip("Ifc")
return context.window_manager.invoke_props_dialog(self)
def _execute(self, context):
obj = bpy.data.objects.get(self.obj) or context.active_object
core.add_part_to_object(
tool.Ifc,
tool.Aggregate,
tool.Collector,
tool.Blender,
obj=obj,
part_class=self.part_class,
part_name=self.part_name,
)
@@ -0,0 +1,48 @@
# 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
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,
StringProperty,
EnumProperty,
BoolProperty,
IntProperty,
FloatProperty,
FloatVectorProperty,
CollectionProperty,
)
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 BIMObjectAggregateProperties(PropertyGroup):
is_editing: BoolProperty(name="Is Editing")
relating_object: PointerProperty(name="Aggregate", type=bpy.types.Object, update=update_relating_object)
@@ -17,7 +17,7 @@
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
from bpy.types import Panel
from ifcopenshell.api.aggregate.data import Data
from blenderbim.bim.module.aggregate.data import AggregateData
from blenderbim.bim.ifc import IfcStore
@@ -28,6 +28,7 @@ class BIM_PT_aggregate(Panel):
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "object"
bl_parent_id = "BIM_PT_object_metadata"
@classmethod
def poll(cls, context):
@@ -40,34 +41,55 @@ class BIM_PT_aggregate(Panel):
return False
if not IfcStore.get_file().by_id(props.ifc_definition_id).is_a("IfcObjectDefinition"):
return False
if props.ifc_definition_id not in Data.products:
Data.load(IfcStore.get_file(), props.ifc_definition_id)
if not Data.products[props.ifc_definition_id]:
return False
return True
def draw(self, context):
props = context.active_object.BIMObjectProperties
if not props.ifc_definition_id:
return
if props.ifc_definition_id not in Data.products:
Data.load(IfcStore.get_file(), props.ifc_definition_id)
layout = self.layout
if not AggregateData.is_loaded:
AggregateData.load()
if props.is_editing_aggregate:
row = self.layout.row(align=True)
props = context.active_object.BIMObjectAggregateProperties
if props.is_editing:
row = layout.row(align=True)
row.prop(props, "relating_object", text="")
if props.relating_object:
row.operator(
"bim.assign_object", icon="CHECKMARK", text=""
).relating_object = props.relating_object.name
op = row.operator("bim.assign_object", icon="CHECKMARK", text="")
op.relating_object = props.relating_object.BIMObjectProperties.ifc_definition_id
op.related_object = context.active_object.BIMObjectProperties.ifc_definition_id
row.operator("bim.disable_editing_aggregate", icon="CANCEL", text="")
else:
row = self.layout.row(align=True)
name = "{}/{}".format(
Data.products[props.ifc_definition_id]["type"], Data.products[props.ifc_definition_id]["Name"]
)
if name == "None/None":
name = "No Aggregate Found"
row.label(text=name)
row.operator("bim.enable_editing_aggregate", icon="GREASEPENCIL", text="")
row.operator("bim.add_aggregate", icon="ADD", text="")
row = layout.row(align=True)
if AggregateData.data["has_relating_object"]:
row.label(text=AggregateData.data["relating_object_label"], icon="TRIA_UP")
op = row.operator("bim.select_aggregate", icon="RESTRICT_SELECT_OFF", text="")
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.relating_object = AggregateData.data["relating_object_id"]
op.related_object = context.active_object.BIMObjectProperties.ifc_definition_id
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")
if AggregateData.data["has_related_objects"]:
op = row.operator("bim.select_parts", icon="RESTRICT_SELECT_OFF", text="")
op.obj = context.active_object.name
ifc_class = AggregateData.data["ifc_class"]
part_class = ""
if ifc_class == "IfcBuilding":
part_class = "IfcBuildingStorey"
elif ifc_class == "IfcSite":
part_class = "IfcBuilding"
elif ifc_class == "IfcProject":
part_class = "IfcSite"
if part_class != "":
op = layout.operator("bim.add_part_to_object", text="Add " + part_class.lstrip("Ifc"))
op.part_class = part_class
op.obj = context.active_object.name
@@ -24,6 +24,7 @@ classes = (
operator.DisableEditingAttributes,
operator.EditAttributes,
operator.GenerateGlobalId,
operator.CopyAttributeToSelection,
prop.BIMAttributeProperties,
ui.BIM_PT_object_attributes,
ui.BIM_PT_material_attributes,
@@ -0,0 +1,74 @@
# 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 ifcopenshell
import blenderbim.tool as tool
def refresh():
AttributesData.is_loaded = False
MaterialAttributesData.is_loaded = False
class AttributesData:
data = {}
is_loaded = False
@classmethod
def load(cls):
cls.data = {"attributes": cls.attributes()}
cls.is_loaded = True
@classmethod
def attributes(cls):
results = []
element = tool.Ifc.get_entity(bpy.context.active_object)
data = element.get_info()
if hasattr(element, "GlobalId"):
excluded_keys = ["id", "type"]
else:
excluded_keys = ["type"]
for key, value in data.items():
if value is None or isinstance(value, ifcopenshell.entity_instance) or key in excluded_keys:
continue
if key == "id":
key = "STEP ID"
results.append({"name": key, "value": str(value)})
return results
class MaterialAttributesData:
data = {}
is_loaded = False
@classmethod
def load(cls):
cls.data = {"attributes": cls.attributes()}
cls.is_loaded = True
@classmethod
def attributes(cls):
results = []
element = tool.Ifc.get_entity(bpy.context.active_object.active_material)
data = element.get_info()
for key, value in data.items():
if value is None or isinstance(value, ifcopenshell.entity_instance) or key in ["id", "type"]:
continue
results.append({"name": key, "value": str(value)})
return results
@@ -20,10 +20,21 @@ import bpy
import json
import ifcopenshell
import ifcopenshell.api
import blenderbim.bim.helper
import blenderbim.bim.handler
import blenderbim.tool as tool
import blenderbim.core.attribute as core
from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.attribute.data import Data
class Operator:
def execute(self, context):
IfcStore.execute_ifc_operator(self, context)
blenderbim.bim.handler.refresh_ui_data()
return {"FINISHED"}
class EnableEditingAttributes(bpy.types.Operator):
bl_idname = "bim.enable_editing_attributes"
bl_label = "Enable Editing Attributes"
@@ -40,22 +51,9 @@ class EnableEditingAttributes(bpy.types.Operator):
oprops = obj.BIMObjectProperties
props = obj.BIMAttributeProperties
props.attributes.clear()
for attribute in Data.products[oprops.ifc_definition_id]:
new = props.attributes.add()
if attribute["type"] == "entity" or (attribute["type"] == "list" and attribute["list_type"] == "entity"):
continue
new.name = attribute["name"]
new.is_null = attribute["is_null"]
if attribute["type"] == "string" or attribute["type"] == "list":
new.string_value = attribute["value"] or ""
elif attribute["type"] == "integer":
new.int_value = attribute["value"] or 0
elif attribute["type"] == "float":
new.float_value = attribute["value"] or 0.0
elif attribute["type"] == "enum":
new.enum_items = json.dumps(attribute["enum_items"])
if attribute["value"]:
new.enum_value = attribute["value"]
if oprops.ifc_definition_id not in Data.products:
Data.load(IfcStore.get_file(), oprops.ifc_definition_id)
blenderbim.bim.helper.import_attributes2(tool.Ifc.get().by_id(oprops.ifc_definition_id), props.attributes)
props.is_editing_attributes = True
return {"FINISHED"}
@@ -77,16 +75,13 @@ class DisableEditingAttributes(bpy.types.Operator):
return {"FINISHED"}
class EditAttributes(bpy.types.Operator):
class EditAttributes(bpy.types.Operator, Operator):
bl_idname = "bim.edit_attributes"
bl_label = "Edit Attributes"
bl_options = {"REGISTER", "UNDO"}
obj: bpy.props.StringProperty()
obj_type: bpy.props.StringProperty()
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context):
self.file = IfcStore.get_file()
if self.obj_type == "Object":
@@ -139,3 +134,14 @@ class GenerateGlobalId(bpy.types.Operator):
global_id.data_type = "string"
global_id.string_value = ifcopenshell.guid.new()
return {"FINISHED"}
class CopyAttributeToSelection(bpy.types.Operator, Operator):
bl_idname = "bim.copy_attribute_to_selection"
bl_label = "Copy Attribute To Selection"
name: bpy.props.StringProperty()
def _execute(self, context):
value = context.active_object.BIMAttributeProperties.attributes.get(self.name).get_value()
for obj in context.selected_objects:
core.copy_attribute_to_selection(tool.Ifc, name=self.name, value=value, obj=obj)
@@ -16,17 +16,16 @@
# 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 blenderbim.bim.helper
from bpy.types import Panel
from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.attribute.data import Data
from blenderbim.bim.module.attribute.data import AttributesData, MaterialAttributesData
def draw_ui(context, layout, obj_type):
def draw_ui(context, layout, obj_type, attributes):
obj = context.active_object if obj_type == "Object" else context.active_object.active_material
oprops = obj.BIMObjectProperties
props = obj.BIMAttributeProperties
if oprops.ifc_definition_id not in Data.products:
Data.load(IfcStore.get_file(), oprops.ifc_definition_id)
if props.is_editing_attributes:
row = layout.row(align=True)
@@ -37,46 +36,17 @@ def draw_ui(context, layout, obj_type):
op.obj_type = obj_type
op.obj = obj.name
for attribute in Data.products[oprops.ifc_definition_id]:
if attribute["type"] == "entity":
continue
row = layout.row(align=True)
blender_attribute = props.attributes.get(attribute["name"])
if attribute["type"] == "string" or attribute["type"] == "list":
row.prop(blender_attribute, "string_value", text=attribute["name"])
elif attribute["type"] == "integer":
row.prop(blender_attribute, "int_value", text=attribute["name"])
elif attribute["type"] == "float":
row.prop(blender_attribute, "float_value", text=attribute["name"])
elif attribute["type"] == "enum":
row.prop(blender_attribute, "enum_value", text=attribute["name"])
if attribute["name"] == "GlobalId":
row.operator("bim.generate_global_id", icon="FILE_REFRESH", text="")
if attribute["is_optional"]:
row.prop(
blender_attribute,
"is_null",
icon="RADIOBUT_OFF" if blender_attribute.is_null else "RADIOBUT_ON",
text="",
)
blenderbim.bim.helper.draw_attributes(props.attributes, layout, copy_operator="bim.copy_attribute_to_selection")
else:
row = layout.row()
op = row.operator("bim.enable_editing_attributes", icon="GREASEPENCIL", text="Edit")
op.obj_type = obj_type
op.obj = obj.name
if "GlobalId" not in [a["name"] for a in Data.products[oprops.ifc_definition_id]]:
row = layout.row(align=True)
row.label(text="STEP ID")
row.label(text=str(oprops.ifc_definition_id))
for attribute in Data.products[oprops.ifc_definition_id]:
if attribute["value"] is None or attribute["type"] == "entity":
continue
for attribute in attributes:
row = layout.row(align=True)
row.label(text=attribute["name"])
row.label(text=str(attribute["value"]))
row.label(text=attribute["value"])
# TODO: reimplement, see #1222
# if "IfcSite/" in context.active_object.name or "IfcBuilding/" in context.active_object.name:
@@ -89,6 +59,7 @@ class BIM_PT_object_attributes(Panel):
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "object"
bl_parent_id = "BIM_PT_object_metadata"
@classmethod
def poll(cls, context):
@@ -99,7 +70,9 @@ class BIM_PT_object_attributes(Panel):
return bool(context.active_object.BIMObjectProperties.ifc_definition_id)
def draw(self, context):
draw_ui(context, self.layout, "Object")
if not AttributesData.is_loaded:
AttributesData.load()
draw_ui(context, self.layout, "Object", AttributesData.data["attributes"])
class BIM_PT_material_attributes(Panel):
@@ -119,4 +92,6 @@ class BIM_PT_material_attributes(Panel):
return False
def draw(self, context):
draw_ui(context, self.layout, "Material")
if not MaterialAttributesData.is_loaded:
MaterialAttributesData.load()
draw_ui(context, self.layout, "Material", MaterialAttributesData.data["attributes"])
@@ -26,6 +26,7 @@ class BIM_PT_augin(bpy.types.Panel):
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
bl_parent_id = "BIM_PT_integrations"
def draw(self, context):
layout = self.layout
@@ -93,6 +93,7 @@ class LoadBcfTopic(bpy.types.Operator):
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
topic = bcfxml.get_topic(self.topic_guid)
bcfxml.get_header(self.topic_guid)
new = context.scene.BCFProperties.topics[self.topic_index]
data_map = {
"name": topic.guid,
@@ -841,6 +842,8 @@ class ActivateBcfViewpoint(bpy.types.Operator):
obj.data.angle = 2 * atan(
(0.5 * cam_height) / (0.5 * cam_width / tan(radians(camera.field_of_view) / 2))
)
else:
return
z_axis = Vector(
(-camera.camera_direction.x, -camera.camera_direction.y, -camera.camera_direction.z)
@@ -29,6 +29,7 @@ class BIM_PT_bcf(Panel):
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
bl_parent_id = "BIM_PT_collaboration"
def draw(self, context):
layout = self.layout
@@ -19,7 +19,7 @@
import os
from pathlib import Path
from blenderbim.bim.prop import StrProperty
from blenderbim.bim.module.root.prop import getIfcClasses
from blenderbim.bim.module.root.prop import get_ifc_classes
from bpy.types import PropertyGroup
from bpy.props import (
PointerProperty,
@@ -33,14 +33,11 @@ from bpy.props import (
)
scenarios_enum = []
classes_enum = []
def purge():
global scenarios_enum
global classes_enum
scenarios_enum = []
classes_enum = []
def getScenarios(self, context):
@@ -68,7 +65,7 @@ class BimTesterProperties(PropertyGroup):
feature: StringProperty(default="", name="Feature / IDS", update=refreshScenarios)
steps: StringProperty(default="", name="Custom Steps")
ifc_file: StringProperty(default="", name="IFC File")
audit_ifc_class: EnumProperty(items=getIfcClasses, name="Audit Class")
audit_ifc_class: EnumProperty(items=get_ifc_classes, name="Audit Class")
qa_reject_element_reason: StringProperty(name="Element Rejection Reason")
scenario: EnumProperty(items=getScenarios, name="Scenario")
should_load_from_memory: BoolProperty(default=False, name="Load from Memory")
@@ -27,6 +27,7 @@ class BIM_PT_qa(Panel):
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
bl_parent_id = "BIM_PT_quality_control"
def draw(self, context):
self.layout.use_property_split = True
@@ -17,14 +17,28 @@
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import bpy
from . import ui
from . import ui, operator, prop
classes = (ui.BIM_PT_boundary,)
classes = (
operator.LoadProjectSpaceBoundaries,
operator.LoadSpaceBoundaries,
operator.LoadBoundary,
operator.SelectProjectBoundaries,
operator.ColourByRelatedBuildingElement,
operator.EnableEditingBoundary,
operator.DisableEditingBoundary,
operator.EditBoundaryAttributes,
operator.UpdateBoundaryGeometry,
ui.BIM_PT_Boundary,
ui.BIM_PT_SpaceBoundaries,
ui.BIM_PT_SceneBoundaries,
prop.BIMBoundaryProperties,
)
def register():
pass
bpy.types.Object.bim_boundary_properties = bpy.props.PointerProperty(type=prop.BIMBoundaryProperties)
def unregister():
pass
del bpy.types.Object.bim_boundary_properties
@@ -0,0 +1,290 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021, 2022 Cyril Waechter <cyril@biminsight.ch>
#
# 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 logging
import ifcopenshell.api
import blenderbim.tool as tool
from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.boundary.data import Data
import blenderbim.bim.import_ifc as import_ifc
def get_boundaries_collection(blender_space):
space_collection = bpy.data.collections.get(blender_space.name, blender_space.users_collection[0])
collection_name = f"Boundaries/{blender_space.BIMObjectProperties.ifc_definition_id}"
boundaries_collection = space_collection.children.get(collection_name)
if not boundaries_collection:
boundaries_collection = bpy.data.collections.new(collection_name)
space_collection.children.link(boundaries_collection)
return boundaries_collection
class Loader:
def __init__(self):
self.ifc_file = None
self.logger = None
self.ifc_importer = None
self.settings = None
self.load_settings()
self.load_importer()
def create_mesh(self, boundary):
# ConnectionGeometry is optional in IFC schema for some reasons.
if not boundary.ConnectionGeometry:
return None
surface = boundary.ConnectionGeometry.SurfaceOnRelatingElement
# workaround for unvalid geometry provided by Revit. See https://github.com/IfcOpenShell/IfcOpenShell/issues/635#issuecomment-770366838
if surface.is_a("IfcCurveBoundedPlane") and not getattr(surface, "InnerBoundaries", None):
surface.InnerBoundaries = ()
shape = ifcopenshell.geom.create_shape(self.settings, surface)
mesh = self.ifc_importer.create_mesh(None, shape)
self.ifc_importer.link_mesh(shape, mesh)
return mesh
def load_settings(self):
self.settings = ifcopenshell.geom.settings()
self.settings.set(self.settings.EXCLUDE_SOLIDS_AND_SURFACES, False)
self.settings.set(self.settings.USE_BREP_DATA, False)
def load_importer(self):
self.ifc_file = tool.Ifc.get()
self.logger = logging.getLogger("ImportIFC")
ifc_import_settings = import_ifc.IfcImportSettings.factory(bpy.context, IfcStore.path, self.logger)
self.ifc_importer = import_ifc.IfcImporter(ifc_import_settings)
self.ifc_importer.file = self.ifc_file
def load_boundary(self, boundary_id, blender_space):
boundary = self.ifc_file.by_id(boundary_id)
obj = tool.Ifc.get_object(boundary)
if obj:
return obj
mesh = self.create_mesh(boundary)
obj = bpy.data.objects.new(f"{boundary.is_a()}/{boundary.Name}", mesh)
obj.matrix_world = blender_space.matrix_world
boundaries_collection = get_boundaries_collection(blender_space)
boundaries_collection.objects.link(obj)
tool.Ifc.link(boundary, obj)
return obj
class LoadProjectSpaceBoundaries(bpy.types.Operator):
bl_idname = "bim.load_project_space_boundaries"
bl_label = "Load all project space boundaries"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
loader = Loader()
if not Data.is_loaded:
Data.load(tool.Ifc.get())
for space_id, boundaries_id in Data.spaces.items():
blender_space = tool.Ifc.get_object(tool.Ifc.get().by_id(space_id))
for boundary_id in boundaries_id:
loader.load_boundary(boundary_id, blender_space)
return {"FINISHED"}
class LoadBoundary(bpy.types.Operator):
bl_idname = "bim.load_boundary"
bl_label = "Load boundary"
bl_options = {"REGISTER", "UNDO"}
boundary_id: bpy.props.IntProperty()
def execute(self, context):
loader = Loader()
blender_space = context.active_object
for obj in context.visible_objects:
obj.select_set(False)
obj = loader.load_boundary(self.boundary_id, blender_space)
obj.select_set(True)
bpy.context.view_layer.objects.active = obj
return {"FINISHED"}
class LoadSpaceBoundaries(bpy.types.Operator):
bl_idname = "bim.load_space_boundaries"
bl_label = "Load selected space boundaries"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
blender_space = context.active_object
oprops = context.active_object.BIMObjectProperties
loader = Loader()
if not Data.is_loaded:
Data.load(tool.Ifc.get())
for boundary_id in Data.spaces.get(oprops.ifc_definition_id, []):
loader.load_boundary(boundary_id, blender_space)
return {"FINISHED"}
class SelectProjectBoundaries(bpy.types.Operator):
bl_idname = "bim.select_project_space_boundaries"
bl_label = "Select all project space boundaries"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
for obj in context.visible_objects:
obj.select_set(False)
ifc_file = tool.Ifc.get()
for boundary_id in Data.boundaries:
boundary = ifc_file.by_id(boundary_id)
obj = tool.Ifc.get_object(boundary)
if obj:
obj.select_set(True)
return {"FINISHED"}
def get_colour(ifc_boundary):
"""Return a color depending on IfcClass given"""
product_colors = {
"IfcWall": (0.7, 0.3, 0, 1),
"IfcWindow": (0, 0.7, 1, 1),
"IfcSlab": (0.7, 0.7, 0.5, 1),
"IfcRoof": (0, 0.3, 0, 1),
"IfcDoor": (1, 1, 1, 1),
}
if ifc_boundary.PhysicalOrVirtualBoundary == "VIRTUAL":
return (1, 0, 1, 1)
element = ifc_boundary.RelatedBuildingElement
if not element:
return (1, 0, 0, 1)
for product, colour in product_colors.items():
if element.is_a(product):
return colour
return (0, 0, 0, 1)
class ColourByRelatedBuildingElement(bpy.types.Operator):
bl_idname = "bim.colour_by_related_building_element"
bl_label = "Apply colour based on related building elements"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
IfcStore.begin_transaction(self)
self.store_state(context)
result = self._execute(context)
IfcStore.add_transaction_operation(self)
IfcStore.end_transaction(self)
return result
def _execute(self, context):
self.file = tool.Ifc.get()
for obj in context.visible_objects:
if not obj.BIMObjectProperties.ifc_definition_id:
continue
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
if not element.is_a("IfcRelSpaceBoundary"):
continue
obj.color = get_colour(element)
areas = [a for a in context.screen.areas if a.type == "VIEW_3D"]
if areas:
areas[0].spaces[0].shading.color_type = "OBJECT"
return {"FINISHED"}
def store_state(self, context):
areas = [a for a in context.screen.areas if a.type == "VIEW_3D"]
if areas:
self.transaction_data = {"area": areas[0], "color_type": areas[0].spaces[0].shading.color_type}
def rollback(self, data):
if data:
data["area"].spaces[0].shading.color_type = data["color_type"]
def commit(self, data):
if data:
data["area"].spaces[0].shading.color_type = "OBJECT"
EDITABLE_ATTRIBUTES = {
"RelatingSpace": "relating_space",
"RelatedBuildingElement": "related_building_element",
"ParentBoundary": "parent_boundary",
"CorrespondingBoundary": "corresponding_boundary",
}
class EnableEditingBoundary(bpy.types.Operator):
bl_idname = "bim.enable_editing_boundary"
bl_label = "Edit boundary relations"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
bprops = context.active_object.bim_boundary_properties
bprops.is_editing = True
boundary = tool.Ifc.get_entity(context.active_object)
for ifc_attribute, blender_property in EDITABLE_ATTRIBUTES.items():
entity = getattr(boundary, ifc_attribute, None)
if not entity:
continue
obj = tool.Ifc.get_object(entity)
if entity and obj:
setattr(bprops, blender_property, obj)
return {"FINISHED"}
class DisableEditingBoundary(bpy.types.Operator):
bl_idname = "bim.disable_editing_boundary"
bl_label = "Disable editing boundary relations"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
bprops = context.active_object.bim_boundary_properties
bprops.is_editing = False
for ifc_attribute, blender_property in EDITABLE_ATTRIBUTES.items():
setattr(bprops, blender_property, None)
return {"FINISHED"}
class EditBoundaryAttributes(bpy.types.Operator):
bl_idname = "bim.edit_boundary_attributes"
bl_label = "Disable editing boundary relations"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context):
bprops = context.active_object.bim_boundary_properties
boundary = tool.Ifc.get_entity(context.active_object)
attributes = dict()
for ifc_attribute, blender_property in EDITABLE_ATTRIBUTES.items():
obj = getattr(bprops, blender_property, None)
entity = tool.Ifc.get_entity(obj)
attributes[blender_property] = entity
ifcopenshell.api.run("boundary.edit_attributes", tool.Ifc.get(), entity=boundary, **attributes)
bpy.ops.bim.disable_editing_boundary()
return {"FINISHED"}
class UpdateBoundaryGeometry(bpy.types.Operator):
bl_idname = "bim.update_boundary_geometry"
bl_label = "Update boundary geometry"
bl_description = """
Update boundary connection geometry from mesh.
Mesh must lie on a single plane. It should look like a face or a face with holes.
"""
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context):
settings = tool.Boundary.get_assign_connection_geometry_settings(context.active_object)
ifcopenshell.api.run("boundary.assign_connection_geometry", tool.Ifc.get(), **settings)
return {"FINISHED"}
@@ -0,0 +1,60 @@
# 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
from bpy.types import PropertyGroup
from bpy.props import (
PointerProperty,
StringProperty,
EnumProperty,
BoolProperty,
IntProperty,
FloatProperty,
FloatVectorProperty,
CollectionProperty,
)
import blenderbim.tool as tool
def space_filter(self, object):
entity = tool.Ifc.get_entity(object)
if entity:
return entity.is_a("IfcSpace") or entity.is_a("IfcExternalSpatialElement")
return False
def boundary_filter(self, object):
entity = tool.Ifc.get_entity(object)
if entity:
return entity.is_a("IfcRelSpaceBoundary")
return False
def element_filter(self, object):
entity = tool.Ifc.get_entity(object)
if entity:
return entity.is_a("IfcElement")
return False
class BIMBoundaryProperties(PropertyGroup):
is_editing: BoolProperty(name="Is Editing")
relating_space: PointerProperty(name="RelatingSpace", type=bpy.types.Object, poll=space_filter)
related_building_element: PointerProperty(name="RelatedBuildingElement", type=bpy.types.Object, poll=element_filter)
parent_boundary: PointerProperty(name="ParentBoundary", type=bpy.types.Object, poll=boundary_filter)
corresponding_boundary: PointerProperty(name="CorrespondingBoundary", type=bpy.types.Object, poll=boundary_filter)
@@ -20,16 +20,37 @@ import bpy
import blenderbim.bim.helper
from bpy.types import Panel, UIList
from blenderbim.bim.ifc import IfcStore
import blenderbim.tool as tool
from ifcopenshell.api.boundary.data import Data
class BIM_PT_boundary(Panel):
class BIM_PT_SceneBoundaries(Panel):
bl_label = "IFC Space Boundaries"
bl_idname = "BIM_PT_boundary"
bl_id_name = "BIM_PT_scene_boundaries"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
bl_parent_id = "BIM_PT_geometry"
@classmethod
def poll(cls, context):
return IfcStore.get_file()
def draw(self, context):
row = self.layout.row(align=True)
row.operator("bim.load_project_space_boundaries")
row.operator("bim.select_project_space_boundaries", text="", icon="RESTRICT_SELECT_OFF")
row.operator("bim.colour_by_related_building_element", text="", icon="BRUSH_DATA")
class BIM_PT_Boundary(Panel):
bl_label = "IFC Space Boundary"
bl_idname = "BIM_PT_Boundary"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "object"
bl_parent_id = "BIM_PT_geometry_object"
@classmethod
def poll(cls, context):
@@ -40,15 +61,97 @@ class BIM_PT_boundary(Panel):
return False
if not IfcStore.get_element(props.ifc_definition_id):
return False
if IfcStore.get_file().by_id(props.ifc_definition_id).is_a() not in ["IfcSpace", "IfcExternalSpatialElement"]:
return False
return True
entity = IfcStore.get_file().by_id(props.ifc_definition_id)
return entity.is_a("IfcRelSpaceBoundary")
def draw(self, context):
self.oprops = context.active_object.BIMObjectProperties
if not Data.is_loaded:
Data.load(IfcStore.get_file())
for boundary_id in Data.spaces.get(self.oprops.ifc_definition_id, []):
boundary = Data.boundaries[boundary_id]
props = context.active_object.BIMObjectProperties
ifc_file = tool.Ifc.get()
element = ifc_file.by_id(props.ifc_definition_id)
boundary = ifc_file.by_id(props.ifc_definition_id)
self.bprops = context.active_object.bim_boundary_properties
if self.bprops.is_editing:
row = self.layout.row(align=True)
row.operator("bim.edit_boundary_attributes", icon="CHECKMARK", text="Save Attributes")
row.operator("bim.disable_editing_boundary", icon="CANCEL", text="")
self.draw_relation_editor(boundary, "RelatingSpace", "relating_space")
self.draw_relation_editor(boundary, "RelatedBuildingElement", "related_building_element")
self.draw_relation_editor(boundary, "ParentBoundary", "parent_boundary")
self.draw_relation_editor(boundary, "CorrespondingBoundary", "corresponding_boundary")
else:
row = self.layout.row()
row.label(text=f"{boundary_id}", icon="GHOST_ENABLED")
row.operator("bim.enable_editing_boundary", icon="GREASEPENCIL", text="Edit")
self.draw_relation_data(boundary, "RelatingSpace")
self.draw_relation_data(boundary, "RelatedBuildingElement")
self.draw_relation_data(boundary, "ParentBoundary")
self.draw_relation_data(boundary, "CorrespondingBoundary")
if hasattr(boundary, "InnerBoundaries"):
for i, inner_boundary in enumerate(getattr(boundary, "InnerBoundaries", ())):
row = self.layout.row(align=True)
row.label(text="InnerBoundaries")
row.label(text=f"[{i}]")
row.label(text=f"{inner_boundary.is_a()}/{inner_boundary.Name}")
row = self.layout.row()
row.operator("bim.update_boundary_geometry")
def draw_relation_data(self, boundary, ifc_attribute: str):
row = self.layout.row(align=True)
if hasattr(boundary, ifc_attribute):
row.label(text=ifc_attribute)
entity = getattr(boundary, ifc_attribute)
if entity:
row.label(text=f"{entity.is_a()}/{entity.Name}")
op = row.operator("bim.select_global_id", text="", icon="TRACKER")
op.global_id = entity.GlobalId
else:
row.label(text="")
def draw_relation_editor(self, boundary, ifc_attribute: str, blender_property: str):
if hasattr(boundary, ifc_attribute):
row = self.layout.row(align=True)
row.prop(self.bprops, blender_property)
class BIM_PT_SpaceBoundaries(Panel):
bl_label = "IFC Space Boundaries"
bl_idname = "BIM_PT_SpaceBoundaries"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "object"
bl_parent_id = "BIM_PT_geometry_object"
@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
valid_classes = ("IfcSpace", "IfcExternalSpatialElement")
entity = IfcStore.get_file().by_id(props.ifc_definition_id)
for ifc_class in valid_classes:
if entity.is_a(ifc_class):
return True
return False
def draw(self, context):
self.props = context.active_object.BIMObjectProperties
self.ifc_file = tool.Ifc.get()
row = self.layout.row()
row.operator("bim.load_space_boundaries")
if not Data.is_loaded:
Data.load(self.ifc_file)
for boundary_id in Data.spaces.get(self.props.ifc_definition_id, []):
boundary_data = Data.boundaries[boundary_id]
building_element = self.ifc_file.by_id(boundary_data["RelatedBuildingElement"])
row = self.layout.row()
if building_element:
bld_el_description = f"{building_element.is_a()}/{building_element.Name}"
else:
bld_el_description = None
row.label(text=f"{boundary_id} > {bld_el_description}", icon="GHOST_ENABLED")
op = row.operator("bim.load_boundary", text="", icon="RESTRICT_SELECT_OFF")
op.boundary_id = boundary_id
@@ -16,17 +16,34 @@
# 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 test.bim.bootstrap
import bpy
from . import ui, prop, operator
classes = (
operator.AddBrick,
operator.AddBrickFeed,
operator.AssignBrickReference,
operator.CloseBrickProject,
operator.ConvertBrickProject,
operator.ConvertIfcToBrick,
operator.LoadBrickProject,
operator.NewBrickFile,
operator.RefreshBrickViewer,
operator.RemoveBrick,
operator.RewindBrickClass,
operator.ViewBrickClass,
operator.ViewBrickItem,
prop.Brick,
prop.BIMBrickProperties,
ui.BIM_PT_brickschema,
ui.BIM_PT_ifc_brickschema_references,
ui.BIM_UL_bricks,
)
class TestExecuteIfcPatch(test.bim.bootstrap.NewFile):
@test.bim.bootstrap.scenario
def test_executing_ifcpatch(self):
return """
Given I set "scene.BIMPatchProperties.ifc_patch_recipes" to "OffsetObjectPlacements"
And I set "scene.BIMPatchProperties.ifc_patch_input" to "{cwd}/test/files/basic.ifc"
And I set "scene.BIMPatchProperties.ifc_patch_output" to "{cwd}/test/files/basic-patched.ifc"
And I set "scene.BIMPatchProperties.ifc_patch_args" to "[123454321,0,0,0]"
When I press "bim.execute_ifc_patch"
Then the file "{cwd}/test/files/basic-patched.ifc" should contain "123454321"
"""
def register():
bpy.types.Scene.BIMBrickProperties = bpy.props.PointerProperty(type=prop.BIMBrickProperties)
def unregister():
del bpy.types.Scene.BIMBrickProperties
@@ -0,0 +1,180 @@
# 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 blenderbim.tool as tool
from blenderbim.tool.brick import BrickStore
try:
from rdflib import URIRef, BNode
except:
# See #1860
print("Warning: brickschema not available.")
def refresh():
BrickschemaData.is_loaded = False
BrickschemaReferencesData.is_loaded = False
class BrickschemaData:
data = {}
is_loaded = False
@classmethod
def load(cls):
cls.is_loaded = True
cls.data = {
"is_loaded": cls.get_is_loaded(),
"attributes": cls.attributes(),
"namespaces": cls.namespaces(),
"brick_equipment_classes": cls.brick_equipment_classes(),
}
@classmethod
def get_is_loaded(cls):
return BrickStore.graph is not None
@classmethod
def attributes(cls):
if BrickStore.graph is None:
return []
props = bpy.context.scene.BIMBrickProperties
try:
brick = props.bricks[props.active_brick_index]
except:
return []
results = []
uri = brick.uri
query = BrickStore.graph.query(
"""
PREFIX brick: <https://brickschema.org/schema/Brick#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
SELECT DISTINCT ?name ?value ?sp ?sv WHERE {
<{uri}> ?name ?value .
OPTIONAL {
{ ?name rdfs:range brick:TimeseriesReference . }
UNION
{ ?name a brick:EntityProperty . }
?value ?sp ?sv }
}
""".replace(
"{uri}", uri
)
)
for row in query:
name = row.get("name").toPython().split("#")[-1]
value = row.get("value")
results.append(
{
"name": name,
"value": value.toPython().split("#")[-1],
"is_uri": isinstance(value, URIRef),
"value_uri": value.toPython(),
"is_globalid": name == "globalID",
}
)
if isinstance(row.get("value"), BNode):
for s, p, o in BrickStore.graph.triples((value, None, None)):
results.append(
{
"name": name + ":" + p.toPython().split("#")[-1],
"value": o.toPython().split("#")[-1],
"is_uri": isinstance(o, URIRef),
"value_uri": o.toPython(),
"is_globalid": p.toPython().split("#")[-1] == "globalID",
}
)
return results
@classmethod
def namespaces(cls):
if BrickStore.graph is None:
return []
results = []
for alias, uri in BrickStore.graph.namespaces():
results.append((uri, f"{alias}: {uri}", ""))
return results
@classmethod
def brick_equipment_classes(cls):
if BrickStore.graph is None:
return []
results = []
query = BrickStore.graph.query(
"""
PREFIX brick: <https://brickschema.org/schema/Brick#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
SELECT ?class WHERE {
?class rdfs:subClassOf* brick:Equipment .
}
"""
)
for uri in sorted([x[0].toPython() for x in query]):
results.append((uri, uri.split("#")[-1], ""))
return results
class BrickschemaReferencesData:
data = {}
is_loaded = False
@classmethod
def load(cls):
cls.is_loaded = True
cls.data = {"is_loaded": cls.get_is_loaded(), "libraries": cls.libraries(), "references": cls.references()}
@classmethod
def get_is_loaded(cls):
return BrickStore.graph is not None
@classmethod
def libraries(cls):
results = []
ifc = tool.Ifc.get()
if not ifc:
return results
for library in ifc.by_type("IfcLibraryInformation"):
if tool.Ifc.get_schema() == "IFC2X3":
results.append((str(library.id()), library.Name or "Unnamed", ""))
elif ".ttl" in library.Location:
results.append((str(library.id()), library.Name or "Unnamed", ""))
return results
@classmethod
def references(cls):
results = []
for rel in getattr(tool.Ifc.get_entity(bpy.context.active_object), "HasAssociations", []):
if rel.is_a("IfcRelAssociatesLibrary"):
reference = rel.RelatingLibrary
if tool.Ifc.get_schema() == "IFC2X3" and "#" not in reference.ItemReference:
continue
if tool.Ifc.get_schema() != "IFC2X3" and "#" not in reference.Identification:
continue
results.append(
{
"id": reference.id(),
"identification": reference.ItemReference
if tool.Ifc.get_schema() == "IFC2X3"
else reference.Identification,
"name": reference.Name or "Unnamed",
}
)
return results
@@ -0,0 +1,191 @@
# 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 ifcopenshell.api
import blenderbim.tool as tool
import blenderbim.core.brick as core
import blenderbim.bim.handler
from blenderbim.bim.ifc import IfcStore
class Operator:
def execute(self, context):
self._execute(context)
blenderbim.bim.handler.refresh_ui_data()
return {"FINISHED"}
class LoadBrickProject(bpy.types.Operator, Operator):
bl_idname = "bim.load_brick_project"
bl_label = "Load Brickschema Project"
bl_options = {"REGISTER", "UNDO"}
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
filter_glob: bpy.props.StringProperty(default="*.ttl", options={"HIDDEN"})
def _execute(self, context):
core.load_brick_project(tool.Brick, filepath=self.filepath)
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
return {"RUNNING_MODAL"}
class ViewBrickClass(bpy.types.Operator, Operator):
bl_idname = "bim.view_brick_class"
bl_label = "View Brick Class"
bl_options = {"REGISTER", "UNDO"}
brick_class: bpy.props.StringProperty(name="Brick Class")
def _execute(self, context):
core.view_brick_class(tool.Brick, brick_class=self.brick_class)
class ViewBrickItem(bpy.types.Operator, Operator):
bl_idname = "bim.view_brick_item"
bl_label = "View Brick Item"
bl_options = {"REGISTER", "UNDO"}
item: bpy.props.StringProperty(name="Brick Item")
def _execute(self, context):
core.view_brick_item(tool.Brick, item=self.item)
class RewindBrickClass(bpy.types.Operator, Operator):
bl_idname = "bim.rewind_brick_class"
bl_label = "Rewind Brick Class"
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
core.rewind_brick_class(tool.Brick)
class CloseBrickProject(bpy.types.Operator, Operator):
bl_idname = "bim.close_brick_project"
bl_label = "Close Brick Project"
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
core.close_brick_project(tool.Brick)
class ConvertBrickProject(bpy.types.Operator, Operator):
bl_idname = "bim.convert_brick_project"
bl_label = "Convert Brick Project"
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
core.convert_brick_project(tool.Ifc, tool.Brick)
class AssignBrickReference(bpy.types.Operator, Operator):
bl_idname = "bim.assign_brick_reference"
bl_label = "Assign Brick Reference"
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
props = context.scene.BIMBrickProperties
core.assign_brick_reference(
tool.Ifc,
tool.Brick,
element=tool.Ifc.get_entity(context.active_object),
library=tool.Ifc.get().by_id(int(props.libraries)),
brick_uri=props.bricks[props.active_brick_index].uri,
)
class AddBrick(bpy.types.Operator, Operator):
bl_idname = "bim.add_brick"
bl_label = "Add Brick"
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
props = context.scene.BIMBrickProperties
library = None
if props.libraries:
library = tool.Ifc.get().by_id(int(props.libraries))
core.add_brick(
tool.Ifc,
tool.Brick,
element=tool.Ifc.get_entity(context.active_object) if context.selected_objects else None,
namespace=props.namespace,
brick_class=props.brick_equipment_class,
library=library,
)
class AddBrickFeed(bpy.types.Operator, Operator):
bl_idname = "bim.add_brick_feed"
bl_label = "Add Brick Feed"
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
source = tool.Ifc.get_entity([o for o in context.selected_objects if o != context.active_object][0])
destination = tool.Ifc.get_entity(context.active_object)
core.add_brick_feed(
tool.Ifc,
tool.Brick,
source=source,
destination=destination,
)
class ConvertIfcToBrick(bpy.types.Operator, Operator):
bl_idname = "bim.convert_ifc_to_brick"
bl_label = "Convert IFC To Brick"
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
props = context.scene.BIMBrickProperties
library = None
if props.libraries:
library = tool.Ifc.get().by_id(int(props.libraries))
core.convert_ifc_to_brick(tool.Brick, namespace=props.namespace, library=library)
class NewBrickFile(bpy.types.Operator, Operator):
bl_idname = "bim.new_brick_file"
bl_label = "New Brick File"
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
core.new_brick_file(tool.Brick)
class RefreshBrickViewer(bpy.types.Operator, Operator):
bl_idname = "bim.refresh_brick_viewer"
bl_label = "Refresh Brick Viewer"
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
core.refresh_brick_viewer(tool.Brick)
class RemoveBrick(bpy.types.Operator, Operator):
bl_idname = "bim.remove_brick"
bl_label = "Remove Brick"
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
props = context.scene.BIMBrickProperties
core.remove_brick(
tool.Ifc,
tool.Brick,
library=tool.Ifc.get().by_id(int(props.libraries)) if props.libraries else None,
brick_uri=props.bricks[props.active_brick_index].uri,
)
@@ -0,0 +1,71 @@
# 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
from blenderbim.bim.module.brick.data import BrickschemaData, BrickschemaReferencesData
from blenderbim.bim.prop import StrProperty, Attribute
from bpy.types import PropertyGroup
from bpy.props import (
PointerProperty,
StringProperty,
EnumProperty,
BoolProperty,
IntProperty,
FloatProperty,
FloatVectorProperty,
CollectionProperty,
)
def update_active_brick_index(self, context):
BrickschemaData.is_loaded = False
def get_libraries(self, context):
if not BrickschemaReferencesData.is_loaded:
BrickschemaReferencesData.load()
return BrickschemaReferencesData.data["libraries"]
def get_namespaces(self, context):
if not BrickschemaData.is_loaded:
BrickschemaData.load()
return BrickschemaData.data["namespaces"]
def get_brick_equipment_classes(self, context):
if not BrickschemaData.is_loaded:
BrickschemaData.load()
return BrickschemaData.data["brick_equipment_classes"]
class Brick(PropertyGroup):
name: StringProperty(name="Name")
label: StringProperty(name="Label")
uri: StringProperty(name="URI")
total_items: IntProperty(name="Total Items")
class BIMBrickProperties(PropertyGroup):
active_brick_class: StringProperty(name="Active Brick Class")
brick_breadcrumbs: CollectionProperty(name="Brick Breadcrumbs", type=StrProperty)
bricks: CollectionProperty(name="Bricks", type=Brick)
active_brick_index: IntProperty(name="Active Brick Index", update=update_active_brick_index)
libraries: EnumProperty(name="Libraries", items=get_libraries)
namespace: EnumProperty(name="Namespace", items=get_namespaces)
brick_equipment_class: EnumProperty(name="Brick Equipment Class", items=get_brick_equipment_classes)
@@ -0,0 +1,132 @@
# 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 blenderbim.tool as tool
from bpy.types import Panel, UIList
from blenderbim.bim.module.brick.data import BrickschemaData, BrickschemaReferencesData
class BIM_PT_brickschema(Panel):
bl_label = "Brickschema Project"
bl_idname = "BIM_PT_brickschema"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
def draw(self, context):
if not BrickschemaData.is_loaded:
BrickschemaData.load()
self.props = context.scene.BIMBrickProperties
if not BrickschemaData.data["is_loaded"]:
row = self.layout.row(align=True)
row.operator("bim.new_brick_file", text="Create Project")
row.operator("bim.load_brick_project", text="Load Project")
return
row = self.layout.row(align=True)
if len(self.props.brick_breadcrumbs):
row.operator("bim.rewind_brick_class", text="", icon="FRAME_PREV")
row.label(text=self.props.active_brick_class)
row.operator("bim.refresh_brick_viewer", text="", icon="FILE_REFRESH")
row.operator("bim.close_brick_project", text="", icon="CANCEL")
row = self.layout.row(align=True)
row.prop(self.props, "namespace", text="")
row.prop(self.props, "brick_equipment_class", text="")
row.operator("bim.add_brick", text="", icon="ADD")
row = self.layout.row(align=True)
row.alignment = "RIGHT"
row.operator("bim.add_brick_feed", text="", icon="PLUGIN")
row.operator("bim.remove_brick", text="", icon="X")
self.layout.template_list("BIM_UL_bricks", "", self.props, "bricks", self.props, "active_brick_index")
for attribute in BrickschemaData.data["attributes"]:
row = self.layout.row(align=True)
row.label(text=attribute["name"])
row.label(text=attribute["value"])
if attribute["is_uri"]:
op = row.operator("bim.view_brick_item", text="", icon="DISCLOSURE_TRI_RIGHT")
op.item = attribute["value_uri"]
if attribute["is_globalid"]:
op = row.operator("bim.select_global_id", icon="RESTRICT_SELECT_OFF", text="")
op.global_id = attribute["value"]
class BIM_PT_ifc_brickschema_references(Panel):
bl_label = "IFC Brickschema References"
bl_idname = "BIM_PT_ifc_brickschema_references"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
bl_parent_id = "BIM_PT_brickschema"
@classmethod
def poll(cls, context):
return tool.Ifc.get()
def draw(self, context):
if not BrickschemaReferencesData.is_loaded:
BrickschemaReferencesData.load()
self.props = context.scene.BIMBrickProperties
if not BrickschemaReferencesData.data["is_loaded"]:
row = self.layout.row()
row.label(text="No Brickschema Project Loaded")
return
if not BrickschemaReferencesData.data["libraries"]:
row = self.layout.row(align=True)
row.label(text="No IFC Libraries")
row.operator("bim.convert_brick_project", text="", icon="ADD")
return
row = self.layout.row(align=True)
row.prop(self.props, "libraries")
row.operator("bim.convert_brick_project", text="", icon="ADD")
row = self.layout.row(align=True)
row.operator("bim.assign_brick_reference", icon="ADD")
row.operator("bim.convert_ifc_to_brick", icon="IMPORT")
if not BrickschemaReferencesData.data["references"]:
row = self.layout.row()
row.label(text="No References")
for reference in BrickschemaReferencesData.data["references"]:
row = self.layout.row(align=True)
row.label(text=reference["identification"], icon="ASSET_MANAGER")
row.label(text=reference["name"])
row.operator("bim.unassign_library_reference", text="", icon="X").reference = reference["id"]
row.operator("bim.view_brick_item", text="", icon="DISCLOSURE_TRI_RIGHT").item = reference["identification"]
class BIM_UL_bricks(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
if item:
row = layout.row(align=True)
if item.total_items:
op = row.operator("bim.view_brick_class", text="", icon="DISCLOSURE_TRI_RIGHT", emboss=False)
op.brick_class = item.name
row.label(text=item.label if item.label else item.name)
if item.total_items:
row.label(text=str(item.total_items))
@@ -0,0 +1,39 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2022 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 . import operator, workspace
classes = (
operator.CadArcFrom2Points,
operator.CadArcFrom3Points,
operator.CadFillet,
operator.CadMitre,
operator.CadTrimExtend,
workspace.CadHotkey,
)
def register():
if not bpy.app.background:
bpy.utils.register_tool(workspace.CadTool, after={"builtin.transform"}, separator=True, group=True)
def unregister():
if not bpy.app.background:
bpy.utils.unregister_tool(workspace.CadTool)
@@ -0,0 +1,345 @@
# 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
import math
import bmesh
import mathutils
import bpy_extras
import blenderbim.tool as tool
messages = {
"SHARED_VERTEX": "Shared Vertex, no intersection possible",
"PARALLEL_EDGES": "Edges Parallel, no intersection possible",
"NON_PLANAR_EDGES": "Non Planar Edges, no clean intersection point",
}
class CadTrimExtend(bpy.types.Operator):
bl_idname = "bim.cad_trim_extend"
bl_label = "CAD Trim / Extend"
@classmethod
def poll(cls, context):
obj = context.active_object
return bool(obj) and obj.type == "MESH"
def cancel_message(self, msg):
print(msg)
self.report({"WARNING"}, msg)
return {"CANCELLED"}
def execute(self, context):
# final attempt to enter unfragmented bm/mesh
# ghastly, but what can I do? it works with these
# fails without.
bpy.ops.object.mode_set(mode="OBJECT")
bpy.ops.object.mode_set(mode="EDIT")
obj = context.active_object
me = obj.data
bm = bmesh.from_edit_mesh(me)
bm.verts.ensure_lookup_table()
bm.edges.ensure_lookup_table()
edges = [e for e in bm.edges if e.select and not e.hide]
if len(edges) == 2:
message = tool.Cad.do_vtx_if_appropriate(bm, edges, mode="T")
if isinstance(message, set):
msg = messages.get(message.pop())
return self.cancel_message(msg)
bm = message
else:
return self.cancel_message("select two edges!")
bm.verts.index_update()
bm.edges.index_update()
bmesh.update_edit_mesh(me, loop_triangles=True)
return {"FINISHED"}
class CadMitre(bpy.types.Operator):
bl_idname = "bim.cad_mitre"
bl_label = "CAD Mitre"
@classmethod
def poll(cls, context):
obj = context.active_object
return bool(obj) and obj.type == "MESH"
def cancel_message(self, msg):
print(msg)
self.report({"WARNING"}, msg)
return {"CANCELLED"}
def execute(self, context):
# final attempt to enter unfragmented bm/mesh
# ghastly, but what can I do? it works with these
# fails without.
bpy.ops.object.mode_set(mode="OBJECT")
bpy.ops.object.mode_set(mode="EDIT")
obj = context.active_object
me = obj.data
bm = bmesh.from_edit_mesh(me)
bm.verts.ensure_lookup_table()
bm.edges.ensure_lookup_table()
edges = [e for e in bm.edges if e.select and not e.hide]
if len(edges) == 2:
message = tool.Cad.do_vtx_if_appropriate(bm, edges, mode="V")
if isinstance(message, set):
msg = messages.get(message.pop())
return self.cancel_message(msg)
bm = message
else:
return self.cancel_message("select two edges!")
bm.verts.index_update()
bm.edges.index_update()
bmesh.update_edit_mesh(me, loop_triangles=True)
return {"FINISHED"}
class CadFillet(bpy.types.Operator):
bl_idname = "bim.cad_fillet"
bl_label = "CAD Fillet"
bl_options = {"REGISTER", "UNDO"}
resolution: bpy.props.IntProperty(name="Arc Resolution", min=0, default=1)
radius: bpy.props.FloatProperty(name="Radius", default=0.1)
@classmethod
def poll(cls, context):
obj = context.active_object
return obj and obj.type == "MESH" and context.mode == "EDIT_MESH"
def draw(self, context):
layout = self.layout
for prop in self.__class__.__annotations__.keys():
layout.prop(self, prop)
def execute(self, context):
bpy.ops.object.mode_set(mode="OBJECT")
bpy.ops.object.mode_set(mode="EDIT")
obj = bpy.context.active_object
cursor = obj.matrix_world.inverted() @ bpy.context.scene.cursor.location
mesh = obj.data
bm = bmesh.from_edit_mesh(mesh)
selected_edges = [e for e in bm.edges if e.select]
if len(selected_edges) != 2:
return {"CANCELLED"}
# Assume the user has selected two edges sharing a vert, but merge verts
# just in case the vertex is coincident but not shared.
all_verts = list(set(selected_edges[0].verts) | set(selected_edges[1].verts))
bmesh.ops.remove_doubles(bm, verts=all_verts, dist=1e-4)
# Subdivide one of the edges to give us an extra vert to play with
# without damaging any faces that already exist.
cut = bmesh.ops.bisect_edges(bm, edges=[selected_edges[1]], cuts=1)
bm.edges.index_update()
bmesh.update_edit_mesh(mesh)
is_wire = selected_edges[0].is_wire
# Recover the original intention of the selected edges after our bisect operation
cut_edges = cut["geom_split"][1:]
selected_edges = [e for e in bm.edges if e.select and e not in cut_edges]
if set(selected_edges[0].verts) & set(cut_edges[0].verts):
selected_edges.append(cut_edges[0])
elif set(selected_edges[0].verts) & set(cut_edges[1].verts):
selected_edges.append(cut_edges[1])
# Calculate the distance to slide each edge to make space for the fillet arc
shared_vert = list(set(selected_edges[0].verts) & set(selected_edges[1].verts))[0]
v1 = selected_edges[0].other_vert(shared_vert)
v2 = selected_edges[1].other_vert(shared_vert)
dir1 = (v1.co - shared_vert.co).normalized()
dir2 = (v2.co - shared_vert.co).normalized()
edge_angle = dir1.angle(dir2)
slide_distance = self.radius / math.tan(edge_angle / 2)
angle = math.pi - edge_angle
fillet_v1 = shared_vert.co + (dir1 * slide_distance)
fillet_v2 = shared_vert.co + (dir2 * slide_distance)
normal = mathutils.geometry.normal([v1.co, shared_vert.co, v2.co])
center = mathutils.geometry.intersect_line_line(
fillet_v1, fillet_v1 + normal.cross(dir1), fillet_v2, fillet_v2 + normal.cross(dir2)
)[0]
v = bm.verts.new(fillet_v1)
steps = self.resolution * 4 if self.resolution else 1
spin = bmesh.ops.spin(bm, geom=[v], axis=normal, cent=center, steps=steps, angle=angle)
bmesh.ops.pointmerge(bm, verts=[shared_vert, v], merge_co=v.co)
bmesh.ops.pointmerge(bm, verts=[v2, spin["geom_last"][0]], merge_co=spin["geom_last"][0].co)
# If faces are involved, then the chamfer edge protects existing faces from the newly created fillet.
# If no faces are involved, we delete the chamfer edge.
if is_wire:
chamfer_edge = [e for e in bm.edges if shared_vert in e.verts and v2 in e.verts][0]
bm.edges.remove(chamfer_edge)
bmesh.update_edit_mesh(mesh)
mesh.update()
bm.free()
return {"FINISHED"}
class CadArcFrom2Points(bpy.types.Operator):
bl_idname = "bim.cad_arc_from_2_points"
bl_label = "CAD Arc from 2 Points"
bl_options = {"REGISTER", "UNDO"}
resolution: bpy.props.IntProperty(name="Arc Resolution", min=1, default=1)
should_flip: bpy.props.BoolProperty(name="Flip", description="Flip arc", default=False)
def draw(self, context):
layout = self.layout
for prop in self.__class__.__annotations__.keys():
layout.prop(self, prop)
def execute(self, context):
bpy.ops.object.mode_set(mode="OBJECT")
bpy.ops.object.mode_set(mode="EDIT")
if bpy.context.mode != "EDIT_MESH":
return {"CANCELLED"}
obj = bpy.context.active_object
region = bpy.context.region
region_3d = bpy.context.area.spaces.active.region_3d
cursor = bpy.context.scene.cursor.location
center = bpy_extras.view3d_utils.location_3d_to_region_2d(region, region_3d, cursor)
if not center:
return {"CANCELLED"}
mesh = obj.data
mw = obj.matrix_world
bm = bmesh.from_edit_mesh(mesh)
selected_verts = [v for v in bm.verts if v.select]
if len(selected_verts) != 2:
return {"CANCELLED"}
v1 = bpy_extras.view3d_utils.location_3d_to_region_2d(region, region_3d, mw @ selected_verts[0].co)
v2 = bpy_extras.view3d_utils.location_3d_to_region_2d(region, region_3d, mw @ selected_verts[1].co)
l1 = v1 - center
l2 = v2 - center
angle = l1.angle_signed(l2)
if self.should_flip:
if angle > 0:
angle = -((math.pi * 2) - angle)
else:
angle = (math.pi * 2) + angle
v = selected_verts[0]
bm.verts.remove(selected_verts[1])
axis = region_3d.view_rotation @ mathutils.Vector((0, 0, 1))
bmesh.ops.spin(
bm,
geom=[v],
axis=mw.inverted().to_quaternion() @ axis,
cent=mw.inverted() @ cursor,
steps=self.resolution * 4,
angle=-angle,
)
bmesh.update_edit_mesh(mesh)
mesh.update()
return {"FINISHED"}
class CadArcFrom3Points(bpy.types.Operator):
bl_idname = "bim.cad_arc_from_3_points"
bl_label = "CAD Arc from 3 Points"
bl_options = {"REGISTER", "UNDO"}
resolution: bpy.props.IntProperty(name="Arc Resolution", min=1, default=1)
only_recalculate_center: bpy.props.BoolProperty(name="Only Recalculate Center", default=False)
@classmethod
def poll(cls, context):
obj = context.active_object
return obj and obj.type == "MESH" and context.mode == "EDIT_MESH"
def draw(self, context):
layout = self.layout
for prop in self.__class__.__annotations__.keys():
layout.prop(self, prop)
def execute(self, context):
bpy.ops.object.mode_set(mode="OBJECT")
bpy.ops.object.mode_set(mode="EDIT")
if bpy.context.mode != "EDIT_MESH":
return {"CANCELLED"}
obj = bpy.context.edit_object
mesh = obj.data
bm = bmesh.from_edit_mesh(mesh)
selected_verts = [v for v in bm.verts if v.select]
if len(selected_verts) != 3:
return {"CANCELLED"}
pts = [v.co for v in selected_verts]
center = tool.Cad.get_center_of_arc(pts, obj)
if not center:
return {"CANCELLED"}
bpy.context.scene.cursor.location = center
if self.only_recalculate_center:
return {"FINISHED"}
center = obj.matrix_world.inverted() @ center
def get_distance_to_other_points(vert):
other_verts = [v for v in selected_verts if v != vert]
total_vector = mathutils.Vector((0, 0, 0))
for other_vert in other_verts:
total_vector += other_vert.co - vert.co
return total_vector.length
# For three selected points that represent a start, end, and point on an
# arc, the start and end verts can be identified by being furthest away
# from the other 2 points.
arc_end_verts = sorted(selected_verts, key=get_distance_to_other_points)[-2:]
normal = mathutils.geometry.normal(pts)
dir1 = (arc_end_verts[0].co - center).normalized()
dir2 = (arc_end_verts[1].co - center).normalized()
# Let's get the matrix that represents the coordinate system of the arc.
# This matrix allows us to get 2D vectors for calculating the signed arc angle.
z = normal
x = (arc_end_verts[0].co - center).normalized()
y = z.cross(x)
arc_matrix = mathutils.Matrix([x, y, z]).transposed().to_4x4()
dir1 = ((arc_matrix.inverted() @ arc_end_verts[0].co) - (arc_matrix.inverted() @ center)).normalized()
dir2 = ((arc_matrix.inverted() @ arc_end_verts[1].co) - (arc_matrix.inverted() @ center)).normalized()
angle = dir1.xy.angle_signed(dir2.xy)
v = arc_end_verts[0]
bmesh.ops.spin(bm, geom=[v], axis=normal, cent=center, steps=self.resolution * 4, angle=-angle)
bmesh.update_edit_mesh(mesh)
mesh.update()
return {"FINISHED"}
@@ -0,0 +1,93 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2022 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 os
import bpy
import blenderbim.bim.module.type.prop as type_prop
from bpy.types import WorkSpaceTool
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.module.model.data import AuthoringData
class CadTool(WorkSpaceTool):
bl_space_type = "VIEW_3D"
bl_context_mode = "EDIT_MESH"
bl_idname = "bim.cad_tool"
bl_label = "CAD Tool"
bl_description = "Gives you CAD authoring related superpowers"
bl_icon = os.path.join(os.path.dirname(__file__), "ops.authoring.cad")
bl_widget = None
# https://docs.blender.org/api/current/bpy.types.KeyMapItems.html
bl_keymap = (
("bim.cad_hotkey", {"type": "E", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_E")]}),
("bim.cad_hotkey", {"type": "T", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_T")]}),
("bim.cad_fillet", {"type": "F", "value": "PRESS", "shift": True}, {"properties": []}),
# Enable Mesh Tools add-on to get this amazing tool
("mesh.offset_edges", {"type": "O", "value": "PRESS", "shift": True}, {"properties": []}),
("bim.cad_arc_from_2_points", {"type": "C", "value": "PRESS", "shift": True}, {"properties": []}),
("bim.cad_arc_from_3_points", {"type": "V", "value": "PRESS", "shift": True}, {"properties": []}),
)
def draw_settings(context, layout, tool):
row = layout.row(align=True)
row.label(text="", icon="EVENT_SHIFT")
row.label(text="Extend", icon="EVENT_E")
row = layout.row(align=True)
row.label(text="", icon="EVENT_SHIFT")
row.label(text="Mitre", icon="EVENT_T")
row = layout.row(align=True)
row.label(text="", icon="EVENT_SHIFT")
row.label(text="Fillet", icon="EVENT_F")
row = layout.row(align=True)
row.label(text="", icon="EVENT_SHIFT")
row.label(text="Offset", icon="EVENT_O")
row = layout.row(align=True)
row.label(text="", icon="EVENT_SHIFT")
row.label(text="2-Point Arc", icon="EVENT_C")
row = layout.row(align=True)
row.label(text="", icon="EVENT_SHIFT")
row.label(text="3-Point Arc", icon="EVENT_V")
class CadHotkey(bpy.types.Operator):
bl_idname = "bim.cad_hotkey"
bl_label = "CAD Hotkey"
bl_options = {"REGISTER", "UNDO"}
hotkey: bpy.props.StringProperty()
def execute(self, context):
self.props = context.scene.BIMModelProperties
self.has_ifc_class = True
try:
self.has_ifc_class = bool(self.props.ifc_class)
except:
pass
getattr(self, f"hotkey_{self.hotkey}")()
return {"FINISHED"}
def hotkey_S_E(self):
bpy.ops.bim.cad_trim_extend()
def hotkey_S_T(self):
bpy.ops.bim.cad_mitre()
@@ -34,9 +34,6 @@ classes = (
operator.SmartClashGroup,
operator.SelectSmartGroup,
operator.LoadSmartGroupsForActiveClashSet,
operator.SetBlenderClashSetA,
operator.SetBlenderClashSetB,
operator.ExecuteBlenderClash,
prop.ClashSource,
prop.ClashSet,
prop.SmartClashGroup,
@@ -411,82 +411,3 @@ class SelectSmartGroup(bpy.types.Operator):
obj.select_set(True)
return {"FINISHED"}
class BlenderClasher:
def process_clash_set(self, context):
import collision
a_cm = collision.CollisionManager()
b_cm = collision.CollisionManager()
self.add_to_cm(a_cm, context.scene.BIMClashProperties.blender_clash_set_a, context)
self.add_to_cm(b_cm, context.scene.BIMClashProperties.blender_clash_set_b, context)
results = a_cm.in_collision_other(b_cm, return_data=True)
if not results[0]:
print("No clashes")
return
for contact in results[1]:
if contact.raw.penetration_depth < 0.01:
continue
print("-----")
print(contact.names)
print(contact.raw.normal)
print(contact.raw.pos)
def add_to_cm(self, cm, object_names, context):
import ifcclash
for object_name in object_names:
name = object_name.name
obj = bpy.data.objects[name]
triangulated_mesh = self.triangulate_mesh(obj, context)
mesh = ifcclash.Mesh()
mesh.vertices = np.array([tuple(obj.matrix_world @ v.co) for v in triangulated_mesh.vertices])
mesh.faces = np.array([tuple(p.vertices) for p in triangulated_mesh.polygons])
cm.add_object(name, mesh)
def triangulate_mesh(self, obj, context):
mesh = obj.evaluated_get(context.evaluated_depsgraph_get()).to_mesh()
bm = bmesh.new()
bm.from_mesh(mesh)
bmesh.ops.triangulate(bm, faces=bm.faces)
bm.to_mesh(mesh)
bm.free()
del bm
return mesh
class SetBlenderClashSetA(bpy.types.Operator):
bl_idname = "bim.set_blender_clash_set_a"
bl_label = "Set Blender Clash Set A"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
context.scene.BIMClashProperties.blender_clash_set_a.clear()
for obj in context.selected_objects:
new = context.scene.BIMClashProperties.blender_clash_set_a.add()
new.name = obj.name
return {"FINISHED"}
class SetBlenderClashSetB(bpy.types.Operator):
bl_idname = "bim.set_blender_clash_set_b"
bl_label = "Set Blender Clash Set B"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
context.scene.BIMClashProperties.blender_clash_set_b.clear()
for obj in context.selected_objects:
new = context.scene.BIMClashProperties.blender_clash_set_b.add()
new.name = obj.name
return {"FINISHED"}
class ExecuteBlenderClash(bpy.types.Operator):
bl_idname = "bim.execute_blender_clash"
bl_label = "Execute Blender Clash"
def execute(self, context):
blender_clasher = BlenderClasher()
blender_clasher.process_clash_set(context)
return {"FINISHED"}
@@ -27,6 +27,7 @@ class BIM_PT_ifcclash(Panel):
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
bl_parent_id = "BIM_PT_quality_control"
def draw(self, context):
layout = self.layout
@@ -34,17 +35,6 @@ class BIM_PT_ifcclash(Panel):
scene = context.scene
props = scene.BIMClashProperties
layout.label(text="Blender Clash:")
row = layout.row(align=True)
row.operator("bim.set_blender_clash_set_a")
row.operator("bim.set_blender_clash_set_b")
row = layout.row(align=True)
row.operator("bim.execute_blender_clash")
layout.label(text="IFC Clash:")
row = layout.row(align=True)
row.operator("bim.add_clash_set")
row.operator("bim.import_clash_sets", text="", icon="IMPORT")
@@ -29,6 +29,7 @@ class BIM_PT_classifications(Panel):
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
bl_parent_id = "BIM_PT_project_setup"
@classmethod
def poll(cls, context):
@@ -86,6 +87,7 @@ class BIM_PT_classification_references(Panel):
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "object"
bl_parent_id = "BIM_PT_object_metadata"
@classmethod
def poll(cls, context):
@@ -27,6 +27,7 @@ class BIM_PT_cobie(Panel):
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
bl_parent_id = "BIM_PT_collaboration"
def draw(self, context):
layout = self.layout
@@ -29,6 +29,7 @@ class BIM_PT_constraints(Panel):
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
bl_parent_id = "BIM_PT_project_setup"
@classmethod
def poll(cls, context):
@@ -73,6 +74,7 @@ class BIM_PT_object_constraints(Panel):
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "object"
bl_parent_id = "BIM_PT_misc_object"
@classmethod
def poll(cls, context):
@@ -68,7 +68,7 @@ class EnableEditingContext(bpy.types.Operator, Operator):
context: bpy.props.IntProperty()
def _execute(self, context):
core.enable_editing_context(tool.ContextEditor, context=tool.Ifc.get().by_id(self.context))
core.enable_editing_context(tool.Context, context=tool.Ifc.get().by_id(self.context))
class DisableEditingContext(bpy.types.Operator, Operator):
@@ -77,7 +77,7 @@ class DisableEditingContext(bpy.types.Operator, Operator):
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
core.disable_editing_context(tool.ContextEditor)
core.disable_editing_context(tool.Context)
class EditContext(bpy.types.Operator, Operator):
@@ -86,4 +86,4 @@ class EditContext(bpy.types.Operator, Operator):
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
core.edit_context(tool.Ifc, tool.ContextEditor)
core.edit_context(tool.Ifc, tool.Context)
@@ -29,6 +29,7 @@ class BIM_PT_context(bpy.types.Panel):
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
bl_parent_id = "BIM_PT_geometry"
@classmethod
def poll(cls, context):
@@ -31,6 +31,7 @@ class BIM_PT_cost_schedules(Panel):
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
bl_parent_id = "BIM_PT_4D5D"
@classmethod
def poll(cls, context):
@@ -26,6 +26,7 @@ class BIM_PT_covetool(bpy.types.Panel):
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
bl_parent_id = "BIM_PT_integrations"
def draw(self, context):
layout = self.layout
@@ -22,10 +22,13 @@ from . import ui, prop, operator
classes = (
operator.AddCsvAttribute,
operator.RemoveCsvAttribute,
operator.RemoveAllCsvAttributes,
operator.ExportIfcCsv,
operator.ImportIfcCsv,
operator.EyedropIfcCsv,
operator.SelectCsvIfcFile,
operator.ImportCsvAttributes,
operator.ExportCsvAttributes,
prop.CsvProperties,
ui.BIM_PT_ifccsv,
)
@@ -49,6 +49,65 @@ class RemoveCsvAttribute(bpy.types.Operator):
return {"FINISHED"}
class RemoveAllCsvAttributes(bpy.types.Operator):
bl_idname = "bim.remove_all_csv_attributes"
bl_label = "Remove all CSV Attributes"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
context.scene.CsvProperties.csv_attributes.clear()
return {"FINISHED"}
class ImportCsvAttributes(bpy.types.Operator):
bl_idname = "bim.import_csv_attributes"
bl_label = "Import CSV Attributes"
bl_options = {"REGISTER", "UNDO"}
filter_glob: bpy.props.StringProperty(default="*.json", options={"HIDDEN"})
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def execute(self, context):
csv_attributes = context.scene.CsvProperties.csv_attributes
csv_attributes.clear()
csv_json = json.load(open(self.filepath))
i = 0
for attribute in csv_json:
csv_attributes.add()
csv_attributes[i].name = attribute
i += 1
return {"FINISHED"}
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
return {"RUNNING_MODAL"}
class ExportCsvAttributes(bpy.types.Operator):
bl_idname = "bim.export_csv_attributes"
bl_label = "Export CSV Attributes"
bl_options = {"REGISTER", "UNDO"}
filename_ext = ".json"
filter_glob: bpy.props.StringProperty(default="*.json", options={"HIDDEN"})
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def execute(self, context):
csv_attributes = []
with open(self.filepath, "w") as outfile:
for attribute in context.scene.CsvProperties.csv_attributes:
csv_attributes.append(attribute.name)
json.dump(csv_attributes, outfile)
return {"FINISHED"}
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"}
class ExportIfcCsv(bpy.types.Operator):
bl_idname = "bim.export_ifccsv"
bl_label = "Export IFC to CSV"
@@ -111,6 +170,8 @@ class ImportIfcCsv(bpy.types.Operator):
else:
ifc_csv.delimiter = props.csv_delimiter
ifc_csv.Import(ifc_file)
if not props.should_load_from_memory:
ifc_file.write(props.csv_ifc_file)
purge_module_data()
return {"FINISHED"}
@@ -32,7 +32,7 @@ from bpy.props import (
class CsvProperties(PropertyGroup):
csv_ifc_file: StringProperty(default="", name="CSV IFC File")
csv_ifc_file: StringProperty(default="", name="IFC File")
ifc_selector: StringProperty(default="", name="IFC Selector")
csv_attributes: CollectionProperty(name="CSV Attributes", type=StrProperty)
csv_delimiter: EnumProperty(
+22 -4
View File
@@ -27,6 +27,7 @@ class BIM_PT_ifccsv(Panel):
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
bl_parent_id = "BIM_PT_collaboration"
def draw(self, context):
layout = self.layout
@@ -46,15 +47,29 @@ class BIM_PT_ifccsv(Panel):
row = layout.row(align=True)
row.prop(props, "ifc_selector")
row.operator("bim.eyedrop_ifccsv", icon="EYEDROPPER", text="")
layout.separator()
row = layout.row()
row.operator("bim.add_csv_attribute")
split = row.split(factor=0.7)
c = split.column()
c.operator("bim.add_csv_attribute")
c = split.column()
c.operator("bim.import_csv_attributes", icon="IMPORT", text="Load Template")
for index, attribute in enumerate(props.csv_attributes):
row = layout.row(align=True)
row.prop(attribute, "name", text="")
row.operator("bim.remove_csv_attribute", icon="X", text="").index = index
if props.csv_attributes:
row = layout.row()
row.label()
row.operator("bim.remove_all_csv_attributes", icon="CANCEL", text="")
row = layout.row()
row.operator("bim.export_csv_attributes", icon="EXPORT", text="Create Template")
layout.separator()
row = layout.row(align=True)
row.prop(props, "csv_delimiter")
@@ -62,6 +77,9 @@ class BIM_PT_ifccsv(Panel):
row = layout.row(align=True)
row.prop(props, "csv_custom_delimiter")
row = layout.row(align=True)
row.operator("bim.export_ifccsv", icon="EXPORT")
row.operator("bim.import_ifccsv", icon="IMPORT")
row = layout.row()
split = row.split(factor=0.7)
c = split.column()
c.operator("bim.export_ifccsv", icon="EXPORT")
c = split.column()
c.operator("bim.import_ifccsv", icon="IMPORT")
@@ -20,17 +20,21 @@ import bpy
from . import ui, prop, operator
classes = (
operator.PrintIfcFile,
operator.PurgeIfcLinks,
operator.PrintObjectPlacement,
operator.ValidateIfcFile,
operator.ProfileImportIFC,
operator.CreateAllShapes,
operator.CreateShapeFromStepId,
operator.SelectHighPolygonMeshes,
operator.InspectFromStepId,
operator.InspectFromObject,
operator.InspectFromStepId,
operator.ParseExpress,
operator.PrintIfcFile,
operator.PrintObjectPlacement,
operator.ProfileImportIFC,
operator.PurgeHdf5Cache,
operator.PurgeIfcLinks,
operator.RewindInspector,
operator.SelectExpressFile,
operator.SelectHighPolygonMeshes,
operator.SelectHighestPolygonMeshes,
operator.ValidateIfcFile,
prop.BIMDebugProperties,
ui.BIM_PT_debug,
)
@@ -16,20 +16,25 @@
# 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 time
import logging
import ifcopenshell
import ifcopenshell.util.placement
import ifcopenshell.util.representation
import blenderbim.tool as tool
import blenderbim.core.debug as core
import blenderbim.bim.handler
import blenderbim.bim.import_ifc as import_ifc
import blenderbim.tool as tool
from blenderbim.bim.ifc import IfcStore
class PrintIfcFile(bpy.types.Operator):
bl_idname = "bim.print_ifc_file"
bl_label = "Print IFC File"
bl_description = "Prints the file contents in the system console.\nAccess it with Window > Toggle System Console"
@classmethod
def poll(cls, context):
@@ -43,6 +48,7 @@ class PrintIfcFile(bpy.types.Operator):
class PurgeIfcLinks(bpy.types.Operator):
bl_idname = "bim.purge_ifc_links"
bl_label = "Purge IFC Links"
bl_description = "Purge all definitions and references from the file.\nWarning : Cannot be undone."
def execute(self, context):
for obj in bpy.data.objects:
@@ -96,7 +102,8 @@ class ProfileImportIFC(bpy.types.Operator):
class CreateAllShapes(bpy.types.Operator):
bl_idname = "bim.create_all_shapes"
bl_label = "Create All Shapes"
bl_label = "Test All Shapes"
bl_description = "Look for errors in all the shapes contained in the file"
@classmethod
def poll(cls, context):
@@ -126,7 +133,7 @@ class CreateAllShapes(bpy.types.Operator):
except:
failures.append(element)
print("***** FAILURE *****")
print("Failures:")
print(f"Failures: {len(failures)}")
for failure in failures:
print(failure)
return {"FINISHED"}
@@ -135,6 +142,7 @@ class CreateAllShapes(bpy.types.Operator):
class CreateShapeFromStepId(bpy.types.Operator):
bl_idname = "bim.create_shape_from_step_id"
bl_label = "Create Shape From STEP ID"
bl_description = "Recreate a mesh object from a STEP ID"
bl_options = {"REGISTER", "UNDO"}
should_include_curves: bpy.props.BoolProperty()
@@ -154,9 +162,12 @@ class CreateShapeFromStepId(bpy.types.Operator):
if self.should_include_curves:
settings.set(settings.INCLUDE_CURVES, True)
shape = ifcopenshell.geom.create_shape(settings, element)
ifc_importer = import_ifc.IfcImporter(self.ifc_import_settings)
ifc_importer.file = self.file
mesh = ifc_importer.create_mesh(element, shape)
if shape:
ifc_importer = import_ifc.IfcImporter(self.ifc_import_settings)
ifc_importer.file = self.file
mesh = ifc_importer.create_mesh(element, shape)
else:
mesh = None
obj = bpy.data.objects.new("Debug", mesh)
context.scene.collection.objects.link(obj)
return {"FINISHED"}
@@ -165,20 +176,37 @@ class CreateShapeFromStepId(bpy.types.Operator):
class SelectHighPolygonMeshes(bpy.types.Operator):
bl_idname = "bim.select_high_polygon_meshes"
bl_label = "Select High Polygon Meshes"
bl_description = "Select objects containing more polygons than the specified number"
bl_options = {"REGISTER", "UNDO"}
threshold: bpy.props.IntProperty()
def execute(self, context):
[
o.select_set(True)
for o in context.view_layer.objects
if o.type == "MESH" and len(o.data.polygons) > context.scene.BIMDebugProperties.number_of_polygons
]
for obj in context.view_layer.objects:
if obj.type == "MESH" and len(obj.data.polygons) > self.threshold:
obj.select_set(True)
return {"FINISHED"}
class SelectHighestPolygonMeshes(bpy.types.Operator):
bl_idname = "bim.select_highest_polygon_meshes"
bl_label = "Select Highest Polygon Meshes"
bl_description = "Select objects with a number of polygons superior to the specified percentile"
bl_options = {"REGISTER", "UNDO"}
percentile: bpy.props.IntProperty()
def execute(self, context):
objects = [obj for obj in context.view_layer.objects if obj.type == "MESH"]
if objects:
percentile = len(max(objects, key=lambda o: len(o.data.polygons)).data.polygons) * self.percentile / 100
print(f"Selected all Meshes with more than {int(percentile)} polygons")
[obj.select_set(True) for obj in objects if len(obj.data.polygons) > percentile]
return {"FINISHED"}
class RewindInspector(bpy.types.Operator):
bl_idname = "bim.rewind_inspector"
bl_label = "Rewind Inspector"
bl_description = "Rewind the Inspector to the previously inspected element"
def execute(self, context):
props = context.scene.BIMDebugProperties
@@ -195,6 +223,7 @@ class RewindInspector(bpy.types.Operator):
class InspectFromStepId(bpy.types.Operator):
bl_idname = "bim.inspect_from_step_id"
bl_label = "Inspect From STEP ID"
bl_description = "Inspect the attributes and references by looking up the specified STEP ID"
step_id: bpy.props.IntProperty()
@classmethod
@@ -245,12 +274,21 @@ class InspectFromStepId(bpy.types.Operator):
class InspectFromObject(bpy.types.Operator):
bl_idname = "bim.inspect_from_object"
bl_label = "Inspect From Object"
bl_description = "Inspect the Active Object's attributes and references"
@classmethod
def poll(cls, context):
if not context.active_object:
if bpy.app.version >= (3, 0, 0):
cls.poll_message_set("No Active Object")
elif not context.active_object.BIMObjectProperties.ifc_definition_id:
if bpy.app.version >= (3, 0, 0):
cls.poll_message_set("Active Object doesn't have an IFC definition")
else:
return True
def execute(self, context):
ifc_definition_id = context.active_object.BIMObjectProperties.ifc_definition_id
if not ifc_definition_id:
return {"FINISHED"}
bpy.ops.bim.inspect_from_step_id(step_id=ifc_definition_id)
bpy.ops.bim.inspect_from_step_id(step_id=context.active_object.BIMObjectProperties.ifc_definition_id)
return {"FINISHED"}
@@ -262,3 +300,40 @@ class PrintObjectPlacement(bpy.types.Operator):
def execute(self, context):
print(ifcopenshell.util.placement.get_local_placement(IfcStore.get_file().by_id(self.step_id)))
return {"FINISHED"}
class ParseExpress(bpy.types.Operator):
bl_idname = "bim.parse_express"
bl_label = "Parse Express"
def execute(self, context):
core.parse_express(tool.Debug, context.scene.BIMDebugProperties.express_file)
blenderbim.bim.handler.refresh_ui_data()
return {"FINISHED"}
class SelectExpressFile(bpy.types.Operator):
bl_idname = "bim.select_express_file"
bl_label = "Select Express File"
bl_options = {"REGISTER", "UNDO"}
bl_description = "Select an IFC EXPRESS definition"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
filter_glob: bpy.props.StringProperty(default="*.exp", options={"HIDDEN"})
def execute(self, context):
if os.path.exists(self.filepath) and "exp" in os.path.splitext(self.filepath)[1]:
context.scene.BIMDebugProperties.express_file = self.filepath
return {"FINISHED"}
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
return {"RUNNING_MODAL"}
class PurgeHdf5Cache(bpy.types.Operator):
bl_idname = "bim.purge_hdf5_cache"
bl_label = "Purge HDF5 Cache"
def execute(self, context):
core.purge_hdf5_cache(tool.Debug)
return {"FINISHED"}
@@ -33,8 +33,10 @@ from bpy.props import (
class BIMDebugProperties(PropertyGroup):
step_id: IntProperty(name="STEP ID")
number_of_polygons: IntProperty(name="Number of Polygons", min=0)
active_step_id: IntProperty(name="STEP ID")
percentile_of_polygons: IntProperty(name="Percentile of Polygons", min=0, max=100, default=90, subtype="PERCENTAGE")
active_step_id: IntProperty(name="STEP ID", default=1, soft_min=1)
step_id_breadcrumb: CollectionProperty(name="STEP ID Breadcrumb", type=StrProperty)
attributes: CollectionProperty(name="Attributes", type=Attribute)
inverse_attributes: CollectionProperty(name="Inverse Attributes", type=Attribute)
inverse_references: CollectionProperty(name="Inverse References", type=Attribute)
express_file: StringProperty(name="Express File")
@@ -27,6 +27,7 @@ class BIM_PT_debug(Panel):
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
bl_parent_id = "BIM_PT_quality_control"
def draw(self, context):
layout = self.layout
@@ -38,9 +39,17 @@ class BIM_PT_debug(Panel):
row.operator("bim.validate_ifc_file", icon="CHECKMARK", text="")
row.operator("bim.select_ifc_file", icon="FILE_FOLDER", text="")
row = self.layout.row(align=True)
row.prop(props, "express_file", text="")
row.operator("bim.parse_express", icon="IMPORT", text="")
row.operator("bim.select_express_file", icon="FILE_FOLDER", text="")
row = layout.row()
row.operator("bim.print_ifc_file")
row = layout.row()
row.operator("bim.purge_hdf5_cache")
row = layout.row()
row.operator("bim.purge_ifc_links")
@@ -56,8 +65,13 @@ class BIM_PT_debug(Panel):
row.prop(props, "step_id", text="")
row = layout.split(factor=0.7, align=True)
row.operator("bim.select_high_polygon_meshes")
row.operator("bim.select_high_polygon_meshes").threshold = context.scene.BIMDebugProperties.number_of_polygons
row.prop(props, "number_of_polygons", text="")
row = layout.split(factor=0.7, align=True)
row.operator(
"bim.select_highest_polygon_meshes"
).percentile = context.scene.BIMDebugProperties.percentile_of_polygons
row.prop(props, "percentile_of_polygons", text="")
layout.label(text="Inspector:")
@@ -0,0 +1,58 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2022 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/>.
# ############################################################################ #
# Hey there! Welcome to the BlenderBIM Add-on code. Please feel free to reach
# out if you have any questions or need further guidance. Happy hacking!
# ############################################################################ #
# Every module has a __init__.py file to load all of its classes. Every
# operation, property, and interface needs to be registered with the Blender
# system when the add-on loads. This is where it happens.
import bpy
from . import ui, prop, operator
# You'll need to provide a list of every one of your classes here. If you forget
# to specify your class, it won't load and you won't be able to use that
# operator, property, or interface.
classes = (
operator.DemonstrateHelloWorld,
operator.DemonstrateRenameProject,
prop.BIMDemoProperties,
ui.BIM_PT_demo,
)
# When the add-on loads, this register function is called. This allows you to
# perform additional tasks during startup. If you need to store custom
# properties, this is where you tell Blender where they are going to be stored.
# You might see more advanced registrations happening in other modules.
def register():
# Properties are usually stored on bpy.types.Scene when they are something
# that affects everything in the project, or bpy.types.Object when they
# affect a single BIM element.
bpy.types.Scene.BIMDemoProperties = bpy.props.PointerProperty(type=prop.BIMDemoProperties)
# When someone disables the add-on, we need to unload everything we loaded. This
# does the reverse of the register function.
def unregister():
del bpy.types.Scene.BIMDemoProperties
@@ -0,0 +1,78 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2022 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/>.
# ############################################################################ #
# Hey there! Welcome to the BlenderBIM Add-on code. Please feel free to reach
# out if you have any questions or need further guidance. Happy hacking!
# ############################################################################ #
# Every module has a data.py file to load data for its interface panels to
# display. When a panel needs to show information that doesn't come from a
# Blender property, it gets that data from one of these classes. This separation
# of "fetching data" and "displaying data" is a common concept that makes UI
# code much simpler, more efficient, and less error-prone to refresh data.
import blenderbim.tool as tool
# All data must have a refresh function. The refresh function simply sets all
# data class to be not yet loaded. So the next time the interface is drawn to
# the user, it will force the data to be reloaded.
def refresh():
# When you define your own data classes, just add to this list!
DemoData.is_loaded = False
# This is a sample data class. It correlates to a single interface panel. Panels
# should not share data classes. This makes it easy to write your interface
# without having your code mixed in with other parts of the interface. As a
# convention, the class is named the same name as the panel.
class DemoData:
# All data classes must have two variables. One to store all the data it has
# loaded and another to store the load state.
data = {}
is_loaded = False
# Every data class must have a load function. This lets us load data in a
# predictable manner.
@classmethod
def load(cls):
# The load function always has two responsibilities: populate the data,
# and set is_loaded to true.
cls.data = {
"has_project": cls.has_project(),
"project_name": cls.project_name(),
}
cls.is_loaded = True
@classmethod
def has_project(cls):
# Here, we check whether or not there is an active IFC project in our
# Blender session.
return bool(tool.Ifc.get())
@classmethod
def project_name(cls):
# Imagine how messy our UI code would be if all of this was mixed in
# with our layout code. Here, it's isolated and testable, and the UX
# becomes easier to maintain and change.
ifc = tool.Ifc.get()
if ifc:
return ifc.by_type("IfcProject")[0].Name or "Unnamed"
@@ -0,0 +1,79 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2022 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/>.
# ############################################################################ #
# Hey there! Welcome to the BlenderBIM Add-on code. Please feel free to reach
# out if you have any questions or need further guidance. Happy hacking!
# ############################################################################ #
# Every module has an operator.py file to define all of the buttons that a user
# can click on from the Blender interface. Blender calls these buttons
# "Operators", since they correlate to a single user operation.
import bpy
import blenderbim.tool as tool
import blenderbim.core.demo as core
import blenderbim.bim.handler
# Each button correlates to a class like the one below. In this case, we're
# creating a new button that will execute a hello world feature.
class DemonstrateHelloWorld(bpy.types.Operator, tool.Ifc.Operator):
# Every operator has a unique ID. If you enable Python tooltips and hover
# over any button in the interface, you will see each button will run a
# function that uses this ID. For example, hovering over this button will
# show that the code it executes is "bpy.ops.bim.demonstrate_hello_world()"
bl_idname = "bim.demonstrate_hello_world"
# In the interface, this button will have the text "Print Hello World".
bl_label = "Print Hello World"
# This code means that the user can undo or redo after pressing the button.
bl_options = {"REGISTER", "UNDO"}
# When hovering over the button, this helpful description will be shown.
bl_description = "Prints the text 'Hello World'"
# When the button is pressed, this _execute() function will run.
def _execute(self, context):
# Every operator should do one thing only: execute a core function. In
# order to execute a core function, the operator's responsibility is to
# pass in all of the inputs the core needs to do its job.
# A core function simply tells tools what to do, so a core function will
# always need at least one tool as an input.
core.demonstrate_hello_world(tool.Demo)
# This class, just like the one above, will correlate to another button in the
# interface. It has a different ID and a different label.
class DemonstrateRenameProject(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.demonstrate_rename_project"
bl_label = "Rename IFC Project"
bl_options = {"REGISTER", "UNDO"}
bl_description = "Renames your IFC project name and prints the name"
def _execute(self, context):
# This core function requires three inputs: two tools, and a name
# string. In this case, the name is taken from some custom Blender
# properties. Generally, the inputs to the core function will come from
# properties (such as an input field) or data from the scene (like the
# actively selected object).
core.demonstrate_rename_project(tool.Ifc, tool.Demo, name=bpy.context.scene.BIMDemoProperties.name)
@@ -0,0 +1,61 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2022 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/>.
# ############################################################################ #
# Hey there! Welcome to the BlenderBIM Add-on code. Please feel free to reach
# out if you have any questions or need further guidance. Happy hacking!
# ############################################################################ #
# Every module has a prop.py file to define Blender properties. Any time you
# want an interface widget like an input field, dropdown, checkbox, or number
# slider, you need a Blender property to store that widget's data. If you want
# to store data that will affect the Blender interface, you also need a
# property. Properties are stored in the .blend file, so when your user closes
# their Blender session, and reopens it, things are how they left it.
import bpy
from bpy.types import PropertyGroup
# Properties have many different data types. We won't use all of them in this
# demo module, but this is a list for your reference.
from bpy.props import (
PointerProperty,
StringProperty,
EnumProperty,
BoolProperty,
IntProperty,
FloatProperty,
FloatVectorProperty,
CollectionProperty,
)
# All properties must belong in a property group. Usually, you'd have a group
# named after your module.
class BIMDemoProperties(PropertyGroup):
# This first property is a string. This means that in the interface, it will
# represent a text input field. We can give it a name and a default value.
# The name will be the label shown next to the input field in the interface.
name: StringProperty(name="Name", default="New Project Name")
# Not all properties need to be shown using their equivalent input widget.
# In this case, we can store a message string, but we will never show it as
# an input text field in the ui.py.
message: StringProperty(name="Message")
show_hints: BoolProperty(name="Show Hints", default=False)
@@ -0,0 +1,131 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2022 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/>.
# ############################################################################ #
# Hey there! Welcome to the BlenderBIM Add-on code. Please feel free to reach
# out if you have any questions or need further guidance. Happy hacking!
# ############################################################################ #
# Every module has a ui.py file to define its interface. Interfaces describe how
# panels, buttons, labels, and input fields are laid out.
import bpy
from blenderbim.bim.module.demo.data import DemoData
# Every panel in the interface correlates to one of these classes. If you enable
# the "Developer Extras" option in Blender, then you can actually right click on
# any panel in Blender, and click "Edit Source". This will bring you to a ui.py
# file like this one where you can see this code. Pretty neat!
class BIM_PT_demo(bpy.types.Panel):
# Every panel has a title.
bl_label = "BlenderBIM Demo"
# Every panel must have an ID. It must be unique. For example, you may want
# to later reference the panel (such as if you want to create a nested
# subpanel).
bl_idname = "BIM_PT_demo"
# This tells the panel to appear in the properties section (in the bottom
# right by default) of the Blender interface.
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
# This tells the panel to appear in the "scene" tab of the properties panel.
bl_context = "scene"
# Every panel has a draw function. This draws all the layout of the panel,
# including all of its labels, buttons, and so on. Note that if the panel is
# hidden, this draw function will not be called.
def draw(self, context):
# Before drawing any content, the panel must load any dynamic variables
# that it will show. Most panels have dynamic content, which display
# changing data from your BIM model. You must load that data first. The
# two lines are always the same, "if not loaded, then load".
if not DemoData.is_loaded:
# Each panel should have its own data class that it loads data from.
# Note that we only load data if it hasn't already been loaded.
# This is because interface panels are drawn continuously. This
# draw() function will be called every time you scroll or move your
# mouse over it. Loading data is slow, so we only refresh data when
# we have to.
DemoData.load()
# Interface panels often show properties. For convenience, define where
# the properties are stored for the module.
self.props = context.scene.BIMDemoProperties
# This defines a new "row" in our layout. When a new row is defined, the
# things on that row, like buttons, labels, and input fields, show on a
# new line.
row = self.layout.row()
# This is the simplest interface element - a label placed in our row.
# If you enable the "Icon Viewer" add-on, you can view a list of
# Blender's built-in icons to choose from in the Blender text editor's
# side panel.
row.label(text="This is a demo panel", icon="INFO")
# Our interface can contain simple logic. For example, if we don't have
# an IFC project, show an error message and don't draw anything else.
# The ui.py should always have very simple logic. Details like how to
# determine whether we have a project is delegated to the data loader.
if not DemoData.data["has_project"]:
row = self.layout.row()
row.label(text="Load or create an IFC project first", icon="ERROR")
return
row = self.layout.row()
# When you want to show a button in the interface, you have to specify
# an operator. Operators are defined in operator.py. You reference an
# operator using its unique ID.
row.operator("bim.demonstrate_hello_world")
# Blender properties can be used to affect what your panel shows. Notice
# how the logic is very simple. Only use simple boolean predicates and
# loops in your layout. No complex logic should ever been seen in the
# interface code.
if self.props.message:
row = self.layout.row()
row.label(text=self.props.message)
row = self.layout.row(align=True)
row.label(text="Project Name")
# Sometimes you need data that doesn't come from Blender properties,
# like from your IFC model. In this case, just get some data from your
# data loader. For those familiar with how templating languages work,
# this is exactly the same.
row.label(text=DemoData.data["project_name"])
row = self.layout.row()
# We've seen how to show text labels and buttons using operators. What
# about text input fields, number sliders, dropdowns, checkboxes, and
# scrollable lists? The way Blender works is that you simply show a
# Blender property in your interface. The data type of that property
# determines which UI widget is shown. A text property will show a text
# field. A number property will show a number slider. An enum property
# will show a drop down. And so on. In this case, our name property is a
# text data type, so expect to see a text input field show up here.
row.prop(self.props, "name")
row = self.layout.row()
# Here's another button, referencing another operator.
row.operator("bim.demonstrate_rename_project")
if self.props.show_hints:
row = self.layout.row()
row.label(text="Name cannot be blank!")
@@ -27,6 +27,7 @@ class BIM_PT_diff(Panel):
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
bl_parent_id = "BIM_PT_quality_control"
def draw(self, context):
layout = self.layout
@@ -20,35 +20,29 @@ import bpy
from . import ui, prop, operator
classes = (
operator.LoadInformation,
operator.LoadDocumentReferences,
operator.DisableDocumentEditingUI,
operator.EnableEditingDocument,
operator.DisableEditingDocument,
operator.AddInformation,
operator.AddDocumentReference,
operator.EditInformation,
operator.EditDocumentReference,
operator.RemoveDocument,
operator.EnableAssigningDocument,
operator.DisableAssigningDocument,
operator.AddInformation,
operator.AssignDocument,
operator.DisableDocumentEditingUI,
operator.DisableEditingDocument,
operator.EditDocument,
operator.EnableEditingDocument,
operator.LoadDocument,
operator.LoadParentDocument,
operator.LoadProjectDocuments,
operator.RemoveDocument,
operator.UnassignDocument,
prop.Document,
prop.BIMDocumentProperties,
prop.BIMObjectDocumentProperties,
ui.BIM_PT_documents,
ui.BIM_PT_object_documents,
ui.BIM_UL_documents,
ui.BIM_UL_object_documents,
)
def register():
bpy.types.Scene.BIMDocumentProperties = bpy.props.PointerProperty(type=prop.BIMDocumentProperties)
bpy.types.Object.BIMObjectDocumentProperties = bpy.props.PointerProperty(type=prop.BIMObjectDocumentProperties)
def unregister():
del bpy.types.Scene.BIMDocumentProperties
del bpy.types.Object.BIMObjectDocumentProperties
@@ -0,0 +1,116 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2022 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 os
import bpy
import ifcopenshell
import ifcopenshell.util.schema
import blenderbim.tool as tool
def refresh():
DocumentData.is_loaded = False
ObjectDocumentData.is_loaded = False
class DocumentData:
data = {}
is_loaded = False
@classmethod
def load(cls):
cls.data = {
"total_information": cls.total_information(),
"parent_document": cls.parent_document(),
}
cls.is_loaded = True
@classmethod
def total_information(cls):
return len(
[
rel
for rel in tool.Ifc.get().by_type("IfcProject")[0].HasAssociations or []
if rel.is_a("IfcRelAssociatesDocument") and rel.RelatingDocument.is_a("IfcDocumentInformation")
]
)
@classmethod
def parent_document(cls):
props = bpy.context.scene.BIMDocumentProperties
if len(props.breadcrumbs):
parent = tool.Ifc.get().by_id(int(props.breadcrumbs[-1].name))
if tool.Ifc.get_schema() == "IFC2X3":
return str(parent.DocumentId)
return str(parent.Identification)
return ""
class ObjectDocumentData:
data = {}
is_loaded = False
@classmethod
def load(cls):
cls.data = {
"documents": cls.documents(),
}
cls.is_loaded = True
@classmethod
def documents(cls):
results = []
element = tool.Ifc.get_entity(bpy.context.active_object)
if not element:
return results
for rel in getattr(element, "HasAssociations", []):
if rel.is_a("IfcRelAssociatesDocument"):
if not rel.RelatingDocument.is_a("IfcDocumentReference"):
continue
name = rel.RelatingDocument.Name
if not name and rel.RelatingDocument.ReferencedDocument:
name = rel.RelatingDocument.ReferencedDocument.Name
if tool.Ifc.get_schema() == "IFC2X3":
identification = rel.RelatingDocument.ItemReference
if not identification and rel.RelatingDocument.ReferencedDocument:
identification = rel.RelatingDocument.ReferencedDocument.DocumentId
else:
identification = rel.RelatingDocument.Identification
if not identification and rel.RelatingDocument.ReferencedDocument:
identification = rel.RelatingDocument.ReferencedDocument.Identification
location = rel.RelatingDocument.Location
if location is None and rel.RelatingDocument.ReferencedDocument:
location = rel.RelatingDocument.ReferencedDocument.Location
if location:
if not "://" in location:
if not os.path.isabs(location):
location = os.path.abspath(os.path.join(os.path.dirname(tool.Ifc.get_path()), location))
location = "file://" + location
results.append(
{
"id": rel.RelatingDocument.id(),
"identification": identification,
"name": name,
"location": location,
}
)
return results
@@ -1,5 +1,5 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
# Copyright (C) 2020, 2021, 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of BlenderBIM Add-on.
#
@@ -20,303 +20,133 @@ import bpy
import json
import ifcopenshell.api
import ifcopenshell.util.attribute
import blenderbim.tool as tool
import blenderbim.core.document as core
from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.document.data import Data
class LoadInformation(bpy.types.Operator):
bl_idname = "bim.load_information"
bl_label = "Load Information"
class LoadProjectDocuments(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.load_project_documents"
bl_label = "Load Project Documents"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
self.file = IfcStore.get_file()
props = context.scene.BIMDocumentProperties
props.documents.clear()
for information_id, information in Data.information.items():
new = props.documents.add()
new.name = information["Name"] or "Unnamed"
if self.file.schema == "IFC2X3":
new.identification = information["DocumentId"] or "*"
else:
new.identification = information["Identification"] or "*"
new.ifc_definition_id = information_id
props.is_editing = "information"
bpy.ops.bim.disable_editing_document()
return {"FINISHED"}
def _execute(self, context):
core.load_project_documents(tool.Document)
class LoadDocumentReferences(bpy.types.Operator):
bl_idname = "bim.load_document_references"
bl_label = "Load Document References"
class LoadDocument(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.load_document"
bl_label = "Load Document"
bl_options = {"REGISTER", "UNDO"}
document: bpy.props.IntProperty()
def _execute(self, context):
core.load_document(tool.Document, document=tool.Ifc.get().by_id(self.document))
class LoadParentDocument(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.load_parent_document"
bl_label = "Load Parent Document"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
self.file = IfcStore.get_file()
props = context.scene.BIMDocumentProperties
props.documents.clear()
for reference_id, reference in Data.references.items():
new = props.documents.add()
new.name = reference["Name"] or "Unnamed"
if self.file.schema == "IFC2X3":
new.identification = reference["ItemReference"] or "*"
else:
new.identification = reference["Identification"] or "*"
new.ifc_definition_id = reference_id
props.is_editing = "reference"
bpy.ops.bim.disable_editing_document()
return {"FINISHED"}
def _execute(self, context):
core.load_parent_document(tool.Document)
class DisableDocumentEditingUI(bpy.types.Operator):
class DisableDocumentEditingUI(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.disable_document_editing_ui"
bl_label = "Disable Document Editing UI"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
context.scene.BIMDocumentProperties.is_editing = ""
bpy.ops.bim.disable_editing_document()
return {"FINISHED"}
def _execute(self, context):
core.disable_document_editing_ui(tool.Document)
class EnableEditingDocument(bpy.types.Operator):
class EnableEditingDocument(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.enable_editing_document"
bl_label = "Enable Editing Document"
bl_options = {"REGISTER", "UNDO"}
document: bpy.props.IntProperty()
def execute(self, context):
props = context.scene.BIMDocumentProperties
props.document_attributes.clear()
if props.is_editing == "information":
data = Data.information[self.document]
ifc_class = "IfcDocumentInformation"
elif props.is_editing == "reference":
data = Data.references[self.document]
ifc_class = "IfcDocumentReference"
for attribute in IfcStore.get_schema().declaration_by_name(ifc_class).all_attributes():
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
if data_type == "entity":
continue
new = props.document_attributes.add()
new.name = attribute.name()
new.is_null = data[attribute.name()] is None
new.is_optional = attribute.optional()
new.data_type = data_type
if data_type == "string":
new.string_value = "" if new.is_null else data[attribute.name()]
elif data_type == "enum":
new.enum_items = json.dumps(ifcopenshell.util.attribute.get_enum_items(attribute))
if data[attribute.name()]:
new.enum_value = data[attribute.name()]
props.active_document_id = self.document
return {"FINISHED"}
def _execute(self, context):
core.enable_editing_document(tool.Document, document=tool.Ifc.get().by_id(self.document))
class DisableEditingDocument(bpy.types.Operator):
class DisableEditingDocument(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.disable_editing_document"
bl_label = "Disable Editing Document"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
context.scene.BIMDocumentProperties.active_document_id = 0
return {"FINISHED"}
def _execute(self, context):
core.disable_editing_document(tool.Document)
class AddInformation(bpy.types.Operator):
class AddInformation(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.add_information"
bl_label = "Add Information"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context):
result = ifcopenshell.api.run("document.add_information", IfcStore.get_file())
Data.load(IfcStore.get_file())
bpy.ops.bim.load_information()
bpy.ops.bim.enable_editing_document(document=result.id())
return {"FINISHED"}
core.add_information(tool.Ifc, tool.Document)
class AddDocumentReference(bpy.types.Operator):
class AddDocumentReference(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.add_document_reference"
bl_label = "Add Document Reference"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context):
result = ifcopenshell.api.run("document.add_reference", IfcStore.get_file())
Data.load(IfcStore.get_file())
bpy.ops.bim.load_document_references()
bpy.ops.bim.enable_editing_document(document=result.id())
return {"FINISHED"}
core.add_reference(tool.Ifc, tool.Document)
class EditInformation(bpy.types.Operator):
bl_idname = "bim.edit_information"
class EditDocument(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.edit_document"
bl_label = "Edit Information"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context):
props = context.scene.BIMDocumentProperties
attributes = {}
for attribute in props.document_attributes:
if attribute.is_null:
attributes[attribute.name] = None
elif attribute.enum_items:
attributes[attribute.name] = attribute.enum_value
else:
attributes[attribute.name] = attribute.string_value
self.file = IfcStore.get_file()
ifcopenshell.api.run(
"document.edit_information",
self.file,
**{"information": self.file.by_id(props.active_document_id), "attributes": attributes}
)
Data.load(IfcStore.get_file())
bpy.ops.bim.load_information()
return {"FINISHED"}
core.edit_document(tool.Ifc, tool.Document, document=tool.Ifc.get().by_id(props.active_document_id))
class EditDocumentReference(bpy.types.Operator):
bl_idname = "bim.edit_document_reference"
bl_label = "Edit Document Reference"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context):
props = context.scene.BIMDocumentProperties
attributes = {}
for attribute in props.document_attributes:
if attribute.is_null:
attributes[attribute.name] = None
else:
attributes[attribute.name] = attribute.string_value
self.file = IfcStore.get_file()
ifcopenshell.api.run(
"document.edit_reference",
self.file,
**{"reference": self.file.by_id(props.active_document_id), "attributes": attributes}
)
Data.load(IfcStore.get_file())
bpy.ops.bim.load_document_references()
return {"FINISHED"}
class RemoveDocument(bpy.types.Operator):
class RemoveDocument(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.remove_document"
bl_label = "Remove Document"
bl_options = {"REGISTER", "UNDO"}
document: bpy.props.IntProperty()
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context):
props = context.scene.BIMDocumentProperties
self.file = IfcStore.get_file()
ifcopenshell.api.run("document.remove_document", self.file, **{"document": self.file.by_id(self.document)})
Data.load(IfcStore.get_file())
if props.is_editing == "information":
bpy.ops.bim.load_information()
elif props.is_editing == "reference":
bpy.ops.bim.load_document_references()
return {"FINISHED"}
core.remove_document(tool.Ifc, tool.Document, document=tool.Ifc.get().by_id(self.document))
class EnableAssigningDocument(bpy.types.Operator):
bl_idname = "bim.enable_assigning_document"
bl_label = "Enable Assigning Document"
bl_options = {"REGISTER", "UNDO"}
obj: bpy.props.StringProperty()
def execute(self, context):
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
props = obj.BIMObjectDocumentProperties
if props.available_document_types == "IfcDocumentInformation":
bpy.ops.bim.load_information()
elif props.available_document_types == "IfcDocumentReference":
bpy.ops.bim.load_document_references()
props.is_adding = props.available_document_types
return {"FINISHED"}
class DisableAssigningDocument(bpy.types.Operator):
bl_idname = "bim.disable_assigning_document"
bl_label = "Disable Assigning Document"
bl_options = {"REGISTER", "UNDO"}
obj: bpy.props.StringProperty()
def execute(self, context):
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
props = obj.BIMObjectDocumentProperties
props.is_adding = ""
return {"FINISHED"}
class AssignDocument(bpy.types.Operator):
class AssignDocument(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.assign_document"
bl_label = "Assign Document"
bl_options = {"REGISTER", "UNDO"}
obj: bpy.props.StringProperty()
document: bpy.props.IntProperty()
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context):
self.file = IfcStore.get_file()
document = tool.Ifc.get().by_id(self.document)
objs = [bpy.data.objects.get(self.obj)] if self.obj else context.selected_objects
for obj in objs:
obj_id = obj.BIMObjectProperties.ifc_definition_id
if not obj_id:
continue
ifcopenshell.api.run(
"document.assign_document",
self.file,
**{
"product": self.file.by_id(obj_id),
"document": self.file.by_id(self.document),
}
)
Data.load(self.file, obj_id)
return {"FINISHED"}
element = tool.Ifc.get_entity(obj)
if element:
core.assign_document(tool.Ifc, product=element, document=document)
class UnassignDocument(bpy.types.Operator):
class UnassignDocument(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.unassign_document"
bl_label = "Unassign Document"
bl_options = {"REGISTER", "UNDO"}
obj: bpy.props.StringProperty()
document: bpy.props.IntProperty()
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context):
self.file = IfcStore.get_file()
document = tool.Ifc.get().by_id(self.document)
objs = [bpy.data.objects.get(self.obj)] if self.obj else context.selected_objects
for obj in objs:
obj_id = obj.BIMObjectProperties.ifc_definition_id
if not obj_id:
continue
ifcopenshell.api.run(
"document.unassign_document",
self.file,
**{
"product": self.file.by_id(obj_id),
"document": self.file.by_id(self.document),
}
)
Data.load(self.file, obj_id)
return {"FINISHED"}
element = tool.Ifc.get_entity(obj)
if element:
core.unassign_document(tool.Ifc, product=element, document=document)
@@ -34,6 +34,7 @@ from bpy.props import (
class Document(PropertyGroup):
name: StringProperty(name="Name")
identification: StringProperty(name="Identification")
is_information: BoolProperty(name="Is Information")
ifc_definition_id: IntProperty(name="IFC Definition ID")
@@ -41,12 +42,6 @@ class BIMDocumentProperties(PropertyGroup):
document_attributes: CollectionProperty(name="Document Attributes", type=Attribute)
active_document_id: IntProperty(name="Active Document Id")
documents: CollectionProperty(name="Documents", type=Document)
breadcrumbs: CollectionProperty(name="Breadcrumbs", type=StrProperty)
active_document_index: IntProperty(name="Active Document Index")
is_editing: StringProperty(name="Is Editing")
class BIMObjectDocumentProperties(PropertyGroup):
is_adding: StringProperty(name="Is Adding")
available_document_types: EnumProperty(
items=[(d, d, "") for d in ["IfcDocumentInformation", "IfcDocumentReference"]], name="Available Document Types"
)
is_editing: BoolProperty(name="Is Editing", default=False)
@@ -19,7 +19,7 @@
from bpy.types import Panel, UIList
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.helper import draw_attributes
from ifcopenshell.api.document.data import Data
from blenderbim.bim.module.document.data import DocumentData, ObjectDocumentData
class BIM_PT_documents(Panel):
@@ -29,45 +29,50 @@ class BIM_PT_documents(Panel):
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
bl_parent_id = "BIM_PT_project_setup"
@classmethod
def poll(cls, context):
return IfcStore.get_file()
def draw(self, context):
if not Data.is_loaded:
Data.load(IfcStore.get_file())
if not DocumentData.is_loaded:
DocumentData.load()
self.props = context.scene.BIMDocumentProperties
if not self.props.is_editing or self.props.is_editing == "information":
row = self.layout.row(align=True)
row.label(text="{} Documents Found".format(len(Data.information)), icon="FILE")
if self.props.is_editing == "information":
row.operator("bim.add_information", text="", icon="ADD")
row.operator("bim.disable_document_editing_ui", text="", icon="CANCEL")
else:
row.operator("bim.load_information", text="", icon="IMPORT")
if not self.props.is_editing or self.props.is_editing == "reference":
row = self.layout.row(align=True)
row.label(text="{} References Found".format(len(Data.references)), icon="FILE_HIDDEN")
if self.props.is_editing == "reference":
row.operator("bim.add_document_reference", text="", icon="ADD")
row.operator("bim.disable_document_editing_ui", text="", icon="CANCEL")
else:
row.operator("bim.load_document_references", text="", icon="IMPORT")
row = self.layout.row(align=True)
row.label(text="{} Documents Found".format(DocumentData.data["total_information"]), icon="FILE")
if self.props.is_editing:
self.layout.template_list(
"BIM_UL_documents", "", self.props, "documents", self.props, "active_document_index"
)
row.operator("bim.disable_document_editing_ui", text="", icon="CANCEL")
else:
row.operator("bim.load_project_documents", text="", icon="IMPORT")
if not self.props.is_editing:
return
row = self.layout.row(align=True)
if self.props.breadcrumbs:
row.operator("bim.load_parent_document", text="", icon="FRAME_PREV")
row.label(text=DocumentData.data["parent_document"])
else:
row.alignment = "RIGHT"
row.operator("bim.add_information", text="", icon="ADD")
if self.props.breadcrumbs:
row.operator("bim.add_document_reference", text="", icon="FILE_HIDDEN")
if self.props.active_document_id:
self.draw_editable_ui(context)
row.operator("bim.edit_document", text="", icon="CHECKMARK")
row.operator("bim.disable_editing_document", text="", icon="CANCEL")
elif self.props.documents and self.props.active_document_index < len(self.props.documents):
ifc_definition_id = self.props.documents[self.props.active_document_index].ifc_definition_id
row.operator("bim.enable_editing_document", text="", icon="GREASEPENCIL").document = ifc_definition_id
row.operator("bim.remove_document", text="", icon="X").document = ifc_definition_id
def draw_editable_ui(self, context):
draw_attributes(self.props.document_attributes, self.layout)
self.layout.template_list("BIM_UL_documents", "", self.props, "documents", self.props, "active_document_index")
if self.props.active_document_id:
draw_attributes(self.props.document_attributes, self.layout)
class BIM_PT_object_documents(Panel):
@@ -77,6 +82,7 @@ class BIM_PT_object_documents(Panel):
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "object"
bl_parent_id = "BIM_PT_misc_object"
@classmethod
def poll(cls, context):
@@ -87,88 +93,64 @@ class BIM_PT_object_documents(Panel):
return bool(context.active_object.BIMObjectProperties.ifc_definition_id)
def draw(self, context):
if not ObjectDocumentData.is_loaded:
ObjectDocumentData.load()
obj = context.active_object
self.oprops = obj.BIMObjectProperties
self.sprops = context.scene.BIMDocumentProperties
self.props = obj.BIMObjectDocumentProperties
self.props = context.scene.BIMDocumentProperties
self.file = IfcStore.get_file()
if not Data.is_loaded:
Data.load(IfcStore.get_file())
if self.oprops.ifc_definition_id not in Data.products:
Data.load(IfcStore.get_file(), self.oprops.ifc_definition_id)
self.draw_add_ui()
document_ids = Data.products[self.oprops.ifc_definition_id]
if not document_ids:
if not ObjectDocumentData.data["documents"]:
row = self.layout.row(align=True)
row.label(text="No Documents", icon="FILE")
for document_id in document_ids:
try:
document = Data.information[document_id]
document_type = "IfcDocumentInformation"
icon = "FILE"
except:
document = Data.references[document_id]
document_type = "IfcDocumentReference"
icon = "FILE_HIDDEN"
for document in ObjectDocumentData.data["documents"]:
row = self.layout.row(align=True)
if self.file.schema == "IFC2X3":
if document_type == "IfcDocumentInformation":
row.label(text=document.get("DocumentId") or "*", icon=icon)
elif document_type == "IfcDocumentReference":
row.label(text=document.get("ItemReference") or "*", icon=icon)
else:
row.label(text=document.get("Identification") or "*", icon=icon)
row.label(text=document.get("Name") or "Unnamed")
row.operator("bim.unassign_document", text="", icon="X").document = document_id
row.label(text=document["identification"] or "*", icon="FILE")
row.label(text=document["name"] or "Unnamed")
if document["location"]:
row.operator("bim.open_uri", icon="URL", text="").uri = document["location"]
row.operator("bim.unassign_document", text="", icon="X").document = document["id"]
def draw_add_ui(self):
if self.props.is_adding:
if not self.props.is_editing:
row = self.layout.row(align=True)
icon = "FILE" if self.props.is_adding == "IfcDocumentInformation" else "FILE_HIDDEN"
row.label(text="Adding {}".format(self.props.is_adding), icon=icon)
row.operator("bim.disable_assigning_document", text="", icon="CANCEL")
self.layout.template_list(
"BIM_UL_object_documents",
"",
self.sprops,
"documents",
self.sprops,
"active_document_index",
)
row.operator("bim.load_project_documents", text="Assign Document References", icon="ADD")
return
row = self.layout.row(align=True)
if self.props.breadcrumbs:
row.operator("bim.load_parent_document", text="", icon="FRAME_PREV")
row.label(text=DocumentData.data["parent_document"])
else:
row = self.layout.row(align=True)
row.prop(self.props, "available_document_types", text="")
row.operator("bim.enable_assigning_document", text="", icon="ADD")
row.alignment = "RIGHT"
if self.props.documents and self.props.active_document_index < len(self.props.documents):
document = self.props.documents[self.props.active_document_index]
if not document.is_information:
row.operator("bim.assign_document", text="", icon="ADD").document = document.ifc_definition_id
row.operator("bim.disable_document_editing_ui", text="", icon="CANCEL")
self.layout.template_list("BIM_UL_documents", "", self.props, "documents", self.props, "active_document_index")
class BIM_UL_documents(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
if item:
row = layout.row(align=True)
row.label(text=item.identification)
row.label(text=item.name)
if context.scene.BIMDocumentProperties.active_document_id == item.ifc_definition_id:
if context.scene.BIMDocumentProperties.is_editing == "information":
row.operator("bim.edit_information", text="", icon="CHECKMARK")
elif context.scene.BIMDocumentProperties.is_editing == "reference":
row.operator("bim.edit_document_reference", text="", icon="CHECKMARK")
row.operator("bim.disable_editing_document", text="", icon="CANCEL")
elif context.scene.BIMDocumentProperties.active_document_id:
row.operator("bim.remove_document", text="", icon="X").document = item.ifc_definition_id
else:
op = row.operator("bim.enable_editing_document", text="", icon="GREASEPENCIL")
if item.is_information:
op = row.operator("bim.load_document", text="", emboss=False, icon="DISCLOSURE_TRI_RIGHT")
op.document = item.ifc_definition_id
row.operator("bim.remove_document", text="", icon="X").document = item.ifc_definition_id
row.label(text="", icon="FILE")
else:
row.label(text="", icon="BLANK1")
row.label(text="", icon="FILE_HIDDEN")
class BIM_UL_object_documents(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
if item:
row = layout.row(align=True)
row.label(text=item.identification)
row.label(text=item.name)
row.operator("bim.assign_document", text="", icon="ADD").document = item.ifc_definition_id
split1 = row.split(factor=0.1)
split1.label(text=item.identification)
split2 = split1.split(factor=0.9)
split2.label(text=item.name)
@@ -20,40 +20,49 @@ import bpy
from . import ui, prop, operator, handler, gizmos
classes = (
operator.AddDrawing,
operator.CreateDrawing,
operator.AddAnnotation,
operator.AddSheet,
operator.OpenSheet,
operator.AddDrawingToSheet,
operator.CreateSheets,
operator.OpenView,
operator.OpenViewCamera,
operator.ActivateView,
operator.SelectDocIfcFile,
operator.GenerateReferences,
operator.ResizeText,
operator.AddVariable,
operator.RemoveVariable,
operator.PropagateTextData,
operator.RemoveDrawing,
operator.AddDrawingStyle,
operator.RemoveDrawingStyle,
operator.SaveDrawingStyle,
operator.ActivateDrawingStyle,
operator.EditVectorStyle,
operator.RemoveSheet,
operator.AddSchedule,
operator.RemoveSchedule,
operator.SelectScheduleFile,
operator.BuildSchedule,
operator.AddScheduleToSheet,
operator.ActivateView,
operator.AddAnnotation,
operator.AddDrawing,
operator.AddDrawingStyle,
operator.AddDrawingStyleAttribute,
operator.RemoveDrawingStyleAttribute,
operator.RefreshDrawingList,
operator.CleanWireframes,
operator.CopyGrid,
operator.AddDrawingToSheet,
operator.AddSchedule,
operator.AddScheduleToSheet,
operator.AddSectionsAnnotations,
operator.AddSheet,
operator.BuildSchedule,
operator.CleanWireframes,
operator.ContractSheet,
operator.CopyGrid,
operator.CreateDrawing,
operator.CreateSheets,
operator.DisableEditingAssignedProduct,
operator.DisableEditingDrawings,
operator.DisableEditingSchedules,
operator.DisableEditingSheets,
operator.DisableEditingText,
operator.EditAssignedProduct,
operator.EditText,
operator.EditVectorStyle,
operator.EnableEditingAssignedProduct,
operator.EnableEditingText,
operator.ExpandSheet,
operator.LoadDrawings,
operator.LoadSchedules,
operator.LoadSheets,
operator.OpenSchedule,
operator.OpenSheet,
operator.OpenView,
operator.RemoveDrawing,
operator.RemoveDrawingFromSheet,
operator.RemoveDrawingStyle,
operator.RemoveDrawingStyleAttribute,
operator.RemoveSchedule,
operator.RemoveSheet,
operator.ResizeText,
operator.SaveDrawingStyle,
operator.SelectDocIfcFile,
prop.Variable,
prop.Drawing,
prop.Schedule,
@@ -62,14 +71,17 @@ classes = (
prop.DocProperties,
prop.BIMCameraProperties,
prop.BIMTextProperties,
prop.BIMAssignedProductProperties,
ui.BIM_PT_camera,
ui.BIM_PT_drawing_underlay,
ui.BIM_PT_annotation_utilities,
ui.BIM_PT_sheets,
ui.BIM_PT_drawings,
ui.BIM_PT_schedules,
ui.BIM_PT_sheets,
ui.BIM_PT_product_assignments,
ui.BIM_PT_text,
ui.BIM_PT_annotation_utilities,
ui.BIM_UL_drawinglist,
ui.BIM_UL_sheets,
gizmos.UglyDotGizmo,
gizmos.DotGizmo,
gizmos.DimensionLabelGizmo,
@@ -81,6 +93,8 @@ classes = (
def register():
bpy.types.Scene.DocProperties = bpy.props.PointerProperty(type=prop.DocProperties)
bpy.types.Camera.BIMCameraProperties = bpy.props.PointerProperty(type=prop.BIMCameraProperties)
bpy.types.Object.BIMAssignedProductProperties = bpy.props.PointerProperty(type=prop.BIMAssignedProductProperties)
bpy.types.Object.BIMTextProperties = bpy.props.PointerProperty(type=prop.BIMTextProperties)
bpy.types.TextCurve.BIMTextProperties = bpy.props.PointerProperty(type=prop.BIMTextProperties)
bpy.app.handlers.load_post.append(handler.toggleDecorationsOnLoad)
bpy.app.handlers.depsgraph_update_pre.append(handler.depsgraph_update_pre_handler)
@@ -89,6 +103,8 @@ def register():
def unregister():
del bpy.types.Scene.DocProperties
del bpy.types.Camera.BIMCameraProperties
del bpy.types.Object.BIMAssignedProductProperties
del bpy.types.Object.BIMTextProperties
del bpy.types.TextCurve.BIMTextProperties
bpy.app.handlers.load_post.remove(handler.toggleDecorationsOnLoad)
bpy.app.handlers.depsgraph_update_pre.remove(handler.depsgraph_update_pre_handler)
@@ -18,6 +18,7 @@
import bpy
import os
import blenderbim.tool as tool
from mathutils import Vector
@@ -34,13 +35,13 @@ class Annotator:
return float(sizes[str(size)])
@staticmethod
def add_text(context, related_element=None):
def add_text(related_element=None):
curve = bpy.data.curves.new(type="FONT", name="Text")
curve.body = "TEXT"
obj = bpy.data.objects.new("Text", curve)
obj.matrix_world = context.scene.camera.matrix_world
obj.matrix_world = bpy.context.scene.camera.matrix_world
if related_element is None:
location, _, _, _ = Annotator.get_placeholder_coords(context)
location, _, _, _ = Annotator.get_placeholder_coords(bpy.context)
else:
obj.data.BIMTextProperties.related_element = related_element
location = related_element.location
@@ -49,12 +50,12 @@ class Annotator:
font = bpy.data.fonts.get("OpenGost TypeB TT")
if not font:
font = bpy.data.fonts.load(
os.path.join(context.scene.BIMProperties.data_dir, "fonts", "OpenGost Type B TT.ttf")
os.path.join(bpy.context.scene.BIMProperties.data_dir, "fonts", "OpenGost Type B TT.ttf")
)
font.name = "OpenGost Type B TT"
obj.data.font = font
obj.data.BIMTextProperties.font_size = "2.5"
collection = context.scene.camera.users_collection[0]
collection = bpy.context.scene.camera.users_collection[0]
collection.objects.link(obj)
Annotator.resize_text(obj)
return obj
@@ -82,9 +83,9 @@ class Annotator:
text_obj.data.size = font_size
@staticmethod
def add_line_to_annotation(obj, context, co1=None, co2=None):
def add_line_to_annotation(obj, co1=None, co2=None):
if co1 is None:
co1, co2, _, _ = Annotator.get_placeholder_coords(context)
co1, co2, _, _ = Annotator.get_placeholder_coords()
co1 = obj.matrix_world.inverted() @ co1
co2 = obj.matrix_world.inverted() @ co2
if isinstance(obj.data, bpy.types.Mesh):
@@ -101,8 +102,8 @@ class Annotator:
return obj
@staticmethod
def add_plane_to_annotation(obj, context):
co1, co2, co3, co4 = Annotator.get_placeholder_coords(context)
def add_plane_to_annotation(obj):
co1, co2, co3, co4 = Annotator.get_placeholder_coords()
co1 = obj.matrix_world.inverted() @ co1 # bot left
co2 = obj.matrix_world.inverted() @ co2 # top left
co3 = obj.matrix_world.inverted() @ co3 # bot right
@@ -150,27 +151,54 @@ class Annotator:
return obj
@staticmethod
def get_annotation_obj(name, data_type, context):
collection = context.scene.camera.users_collection[0]
for obj in collection.objects:
if name in obj.name:
return obj
if data_type == "mesh":
data = bpy.data.meshes.new(name)
elif data_type == "curve":
data = bpy.data.curves.new(name, type="CURVE")
def get_annotation_obj(drawing, object_type, data_type):
camera = tool.Ifc.get_object(drawing)
co1, _, _, _ = Annotator.get_placeholder_coords(camera)
matrix_world = camera.matrix_world.copy()
matrix_world[0][3] = co1.x
matrix_world[1][3] = co1.y
matrix_world[2][3] = co1.z
collection = camera.users_collection[0]
if object_type == "TEXT":
obj = bpy.data.objects.new(object_type, None)
obj.matrix_world = matrix_world
collection.objects.link(obj)
return obj
elif object_type in ("TEXT_LEADER", "SECTION_LEVEL"):
data = bpy.data.curves.new(object_type, type="CURVE")
data.dimensions = "3D"
data.resolution_u = 2
obj = bpy.data.objects.new(name, data)
obj = bpy.data.objects.new(object_type, data)
obj.matrix_world = matrix_world
collection.objects.link(obj)
return obj
if object_type != "ANGLE":
for obj in collection.objects:
element = tool.Ifc.get_entity(obj)
if element and element.ObjectType == object_type:
return obj
if data_type == "mesh":
data = bpy.data.meshes.new(object_type)
elif data_type == "curve":
data = bpy.data.curves.new(object_type, type="CURVE")
data.dimensions = "3D"
data.resolution_u = 2
obj = bpy.data.objects.new(object_type, data)
obj.matrix_world = matrix_world
collection.objects.link(obj)
return obj
@staticmethod
def get_placeholder_coords(context):
camera = context.scene.camera
def get_placeholder_coords(camera=None):
if not camera:
camera = bpy.context.scene.camera
z_offset = camera.matrix_world.to_quaternion() @ Vector((0, 0, -1))
if context.scene.render.resolution_x > context.scene.render.resolution_y:
y = camera.data.ortho_scale * (context.scene.render.resolution_y / context.scene.render.resolution_x) / 4
if bpy.context.scene.render.resolution_x > bpy.context.scene.render.resolution_y:
y = (
camera.data.ortho_scale
* (bpy.context.scene.render.resolution_y / bpy.context.scene.render.resolution_x)
/ 4
)
else:
y = camera.data.ortho_scale / 4
y_offset = camera.matrix_world.to_quaternion() @ Vector((0, y, 0))
@@ -0,0 +1,123 @@
# 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 ifcopenshell.util.representation
import blenderbim.tool as tool
def refresh():
ProductAssignmentsData.is_loaded = False
TextData.is_loaded = False
SheetsData.is_loaded = False
SchedulesData.is_loaded = False
DrawingsData.is_loaded = False
class ProductAssignmentsData:
data = {}
is_loaded = False
@classmethod
def load(cls):
cls.data = {"relating_product": cls.relating_product()}
cls.is_loaded = True
@classmethod
def relating_product(cls):
element = tool.Ifc.get_entity(bpy.context.active_object)
if not element or not element.is_a("IfcAnnotation"):
return
for rel in element.HasAssignments:
if rel.is_a("IfcRelAssignsToProduct"):
name = rel.RelatingProduct.Name or "Unnamed"
return f"{rel.RelatingProduct.is_a()}/{name}"
class TextData:
data = {}
is_loaded = False
@classmethod
def load(cls):
cls.data = {"attributes": cls.attributes()}
cls.is_loaded = True
@classmethod
def attributes(cls):
element = tool.Ifc.get_entity(bpy.context.active_object)
if not element or not element.is_a("IfcAnnotation") or element.ObjectType not in ["TEXT", "TEXT_LEADER"]:
return []
rep = ifcopenshell.util.representation.get_representation(element, "Plan", "Annotation")
text_literal = [i for i in rep.Items if i.is_a("IfcTextLiteral")][0]
return [
{"name": "Literal", "value": text_literal.Literal},
{"name": "BoxAlignment", "value": text_literal.BoxAlignment},
]
class SheetsData:
data = {}
is_loaded = False
@classmethod
def load(cls):
cls.data = {"total_sheets": cls.total_sheets()}
cls.is_loaded = True
@classmethod
def total_sheets(cls):
return len([d for d in tool.Ifc.get().by_type("IfcDocumentInformation") if d.Scope == "DOCUMENTATION"])
class DrawingsData:
data = {}
is_loaded = False
@classmethod
def load(cls):
cls.data = {"total_drawings": cls.total_drawings(), "location_hint": cls.location_hint()}
cls.is_loaded = True
@classmethod
def total_drawings(cls):
return len([e for e in tool.Ifc.get().by_type("IfcAnnotation") if e.ObjectType == "DRAWING"])
@classmethod
def location_hint(cls):
if bpy.context.scene.DocProperties.target_view in ["PLAN_VIEW", "REFLECTED_PLAN_VIEW"]:
results = [("0", "Origin", "")]
results.extend(
[(str(s.id()), s.Name or "Unnamed", "") for s in tool.Ifc.get().by_type("IfcBuildingStorey")]
)
return results
return [(h.upper(), h, "") for h in ["North", "South", "East", "West"]]
class SchedulesData:
data = {}
is_loaded = False
@classmethod
def load(cls):
cls.data = {"total_schedules": cls.total_schedules()}
cls.is_loaded = True
@classmethod
def total_schedules(cls):
return len([d for d in tool.Ifc.get().by_type("IfcDocumentInformation") if d.Scope == "SCHEDULE"])
File diff suppressed because it is too large Load Diff
@@ -45,6 +45,3 @@ def set_active_camera_resolution(scene):
scene.render.resolution_x = scene.camera.data.BIMCameraProperties.raster_x
scene.render.resolution_y = scene.camera.data.BIMCameraProperties.raster_y
current_drawing = scene.DocProperties.drawings[scene.DocProperties.current_drawing_index]
if scene.camera != current_drawing.camera:
scene.DocProperties.current_drawing_index = scene.DocProperties.drawings.find(scene.camera.name.split("/")[1])
bpy.ops.bim.activate_view(drawing_index=scene.DocProperties.current_drawing_index)
@@ -19,6 +19,7 @@
import bpy
import math
import mathutils.geometry
import blenderbim.tool as tool
from mathutils import Vector
# Code taken and updated from https://blenderartists.org/t/detecting-intersection-of-bounding-boxes/457520/2
@@ -259,13 +260,11 @@ def format_distance(value, isArea=False, hide_units=True):
def get_active_drawing(scene):
"""Get active drawing collection and camera"""
props = scene.DocProperties
if props.active_drawing_index is None or len(props.drawings) == 0:
return None, None
try:
drawing = props.active_drawing
return scene.collection.children["Views"].children[f"IfcGroup/{drawing.name}"], drawing.camera
except (KeyError, IndexError):
raise RuntimeError("missing drawing collection")
camera = tool.Ifc.get_object(tool.Ifc.get().by_id(props.active_drawing_id))
return camera.users_collection[0], camera
except:
return None, None
def get_project_collection(scene):
File diff suppressed because it is too large Load Diff
@@ -18,9 +18,14 @@
import os
import bpy
import json
import enum
import ifcopenshell
import blenderbim.tool as tool
import blenderbim.core.drawing as core
import blenderbim.bim.module.drawing.annotation as annotation
import blenderbim.bim.module.drawing.decoration as decoration
import enum
from blenderbim.bim.module.drawing.data import DrawingsData
from pathlib import Path
from blenderbim.bim.prop import Attribute, StrProperty
from bpy.types import PropertyGroup
@@ -53,6 +58,36 @@ def purge():
vector_styles_enum = []
def update_target_view(self, context):
DrawingsData.data["location_hint"] = DrawingsData.location_hint()
def get_location_hint(self, context):
if not DrawingsData.is_loaded:
DrawingsData.load()
return DrawingsData.data["location_hint"]
def update_diagram_scale(self, context):
scale = self.diagram_scale
if scale == "CUSTOM":
scale = self.custom_diagram_scale
if "|" not in scale:
return
human_scale, scale = scale.split("|")
element = tool.Ifc.get_entity(context.active_object)
if not element:
return
pset = ifcopenshell.util.element.get_psets(element).get("EPset_Drawing")
if pset:
pset = tool.Ifc.get().by_id(pset["id"])
else:
pset = ifcopenshell.api.run("pset.add_pset", tool.Ifc.get(), product=element, name="EPset_Drawing")
ifcopenshell.api.run(
"pset.edit_pset", tool.Ifc.get(), pset=pset, properties={"Scale": scale, "HumanScale": human_scale}
)
def get_diagram_scales(self, context):
global diagram_scales_enum
if (
@@ -64,7 +99,8 @@ def get_diagram_scales(self, context):
diagram_scales_enum = [
("CUSTOM", "Custom", ""),
("1'=1'-0\"|1/1", "1'=1'-0\"", ""),
('6"=1\'-0"|1/6', '6"=1\'-0"', ""),
('6"=1\'-0"|1/2', '6"=1\'-0"', ""),
('3"=1\'-0"|1/4', '3"=1\'-0"', ""),
('1-1/2"=1\'-0"|1/8', '1-1/2"=1\'-0"', ""),
('1"=1\'-0"|1/12', '1"=1\'-0"', ""),
('3/4"=1\'-0"|1/16', '3/4"=1\'-0"', ""),
@@ -113,31 +149,47 @@ def get_diagram_scales(self, context):
return diagram_scales_enum
def updateDrawingName(self, context):
if not self.camera:
return
if self.camera.name == self.name:
return
self.camera.name = "IfcAnnotation/{}".format(self.name)
unique_name = "/".join(self.camera.name.split("/")[1:])
self.camera.users_collection[0].name = "IfcGroup/{}".format(unique_name)
if self.name != unique_name:
self.name = unique_name
def update_drawing_name(self, context):
drawing = tool.Ifc.get().by_id(self.ifc_definition_id)
core.update_drawing_name(tool.Ifc, tool.Drawing, drawing=drawing, name=self.name)
def refreshActiveDrawingIndex(self, context):
bpy.ops.bim.activate_view(drawing_index=context.scene.DocProperties.active_drawing_index)
def update_schedule_name(self, context):
schedule = tool.Ifc.get().by_id(self.ifc_definition_id)
core.update_schedule_name(tool.Ifc, tool.Drawing, schedule=schedule, name=self.name)
def update_has_underlay(self, context):
update_layer(self, context, "HasUnderlay", self.has_underlay)
def update_has_linework(self, context):
update_layer(self, context, "HasLinework", self.has_linework)
def update_has_annotation(self, context):
update_layer(self, context, "HasAnnotation", self.has_annotation)
def update_layer(self, context, name, value):
element = tool.Ifc.get_entity(context.active_object)
if not element:
return
pset = ifcopenshell.util.element.get_psets(element).get("EPset_Drawing")
if pset:
pset = tool.Ifc.get().by_id(pset["id"])
else:
pset = ifcopenshell.api.run("pset.add_pset", tool.Ifc.get(), product=element, name="EPset_Drawing")
ifcopenshell.api.run("pset.edit_pset", tool.Ifc.get(), pset=pset, properties={name: value})
def getTitleblocks(self, context):
global titleblocks_enum
if len(titleblocks_enum) < 1:
titleblocks_enum.clear()
for filename in Path(os.path.join(context.scene.BIMProperties.data_dir, "templates", "titleblocks")).glob(
"*.svg"
):
f = str(filename.stem)
titleblocks_enum.append((f, f, ""))
files = Path(os.path.join(context.scene.BIMProperties.data_dir, "templates", "titleblocks")).glob("*.svg")
files = sorted([str(f.stem) for f in files])
titleblocks_enum.extend([(f, f, "") for f in files])
return titleblocks_enum
@@ -150,6 +202,10 @@ def refreshTitleblocks(self, context):
def toggleDecorations(self, context):
toggle = self.should_draw_decorations
if toggle:
# TODO: design a proper text variable templating renderer
collection = context.scene.camera.users_collection[0]
for obj in collection.objects:
tool.Drawing.update_text_value(obj)
decoration.DecorationsHandler.install(context)
else:
decoration.DecorationsHandler.uninstall()
@@ -175,13 +231,15 @@ class Variable(PropertyGroup):
class Drawing(PropertyGroup):
name: StringProperty(name="Name", update=updateDrawingName)
camera: PointerProperty(name="Camera", type=bpy.types.Object)
ifc_definition_id: IntProperty(name="IFC Definition ID")
name: StringProperty(name="Name", update=update_drawing_name)
target_view: StringProperty(name="Target View")
class Schedule(PropertyGroup):
name: StringProperty(name="Name")
file: StringProperty(name="File")
ifc_definition_id: IntProperty(name="IFC Definition ID")
name: StringProperty(name="Name", update=update_schedule_name)
identification: StringProperty(name="Identification")
class Sheet(PropertyGroup):
@@ -195,9 +253,12 @@ class Sheet(PropertyGroup):
def get_name(self):
return self.get("name")
ifc_definition_id: IntProperty(name="IFC Definition ID")
identification: StringProperty(name="Identification")
name: StringProperty(name="Name", get=get_name, set=set_name)
drawings: CollectionProperty(name="Drawings", type=Drawing)
active_drawing_index: IntProperty(name="Active Drawing Index")
is_sheet: BoolProperty(name="Is Sheet", default=False)
reference_type: StringProperty(name="Reference Type")
is_expanded: BoolProperty(name="Is Expanded", default=False)
class DrawingStyle(PropertyGroup):
@@ -246,76 +307,78 @@ class RasterStyleProperty(enum.Enum):
class DocProperties(PropertyGroup):
has_underlay: BoolProperty(name="Underlay", default=False)
has_linework: BoolProperty(name="Linework", default=True)
has_annotation: BoolProperty(name="Annotation", default=True)
should_use_underlay_cache: BoolProperty(name="Use Underlay Cache", default=False)
should_use_linework_cache: BoolProperty(name="Use Linework Cache", default=False)
should_use_annotation_cache: BoolProperty(name="Use Annotation Cache", default=False)
should_extract: BoolProperty(name="Should Extract", default=True)
is_editing_drawings: BoolProperty(name="Is Editing Drawings", default=False)
is_editing_schedules: BoolProperty(name="Is Editing Schedules", default=False)
target_view: EnumProperty(
items=[
("PLAN_VIEW", "Plan", ""),
("ELEVATION_VIEW", "Elevation", ""),
("SECTION_VIEW", "Section", ""),
("REFLECTED_PLAN_VIEW", "RCP", ""),
("MODEL_VIEW", "Model", ""),
],
name="Target View",
default="PLAN_VIEW",
update=update_target_view,
)
location_hint: EnumProperty(items=get_location_hint, name="Location Hint")
drawings: CollectionProperty(name="Drawings", type=Drawing)
active_drawing_index: IntProperty(name="Active Drawing Index", update=refreshActiveDrawingIndex)
active_drawing_id: IntProperty(name="Active Drawing Id")
active_drawing_index: IntProperty(name="Active Drawing Index")
current_drawing_index: IntProperty(name="Current Drawing Index")
schedules: CollectionProperty(name="Schedules", type=Schedule)
active_schedule_index: IntProperty(name="Active Schedule Index")
titleblock: EnumProperty(items=getTitleblocks, name="Titleblock", update=refreshTitleblocks)
is_editing_sheets: BoolProperty(name="Is Editing Sheets", default=False)
sheets: CollectionProperty(name="Sheets", type=Sheet)
active_sheet_index: IntProperty(name="Active Sheet Index")
ifc_files: CollectionProperty(name="IFCs", type=StrProperty)
drawing_styles: CollectionProperty(name="Drawing Styles", type=DrawingStyle)
should_draw_decorations: BoolProperty(name="Should Draw Decorations", update=toggleDecorations)
decorations_colour: FloatVectorProperty(
name="Decorations Colour", subtype="COLOR", default=(1, 0, 0, 1), min=0.0, max=1.0, size=4
name="Decorations Colour", subtype="COLOR", default=(1, 1, 1, 1), min=0.0, max=1.0, size=4
)
@property
def active_schedule(self):
return self.schedules[self.active_schedule_index]
@property
def active_drawing(self):
return self.drawings[self.active_drawing_index]
@property
def active_sheet(self):
return self.sheets[self.active_sheet_index]
class BIMCameraProperties(PropertyGroup):
has_underlay: BoolProperty(name="Underlay", default=False, update=update_has_underlay)
has_linework: BoolProperty(name="Linework", default=True, update=update_has_linework)
has_annotation: BoolProperty(name="Annotation", default=True, update=update_has_annotation)
representation: StringProperty(name="Representation")
view_name: StringProperty(name="View Name")
target_view: EnumProperty(
items=[
("PLAN_VIEW", "PLAN_VIEW", ""),
("ELEVATION_VIEW", "ELEVATION_VIEW", ""),
("SECTION_VIEW", "SECTION_VIEW", ""),
("REFLECTED_PLAN_VIEW", "REFLECTED_PLAN_VIEW", ""),
("MODEL_VIEW", "MODEL_VIEW", ""),
],
name="Target View",
default="PLAN_VIEW",
)
diagram_scale: EnumProperty(items=get_diagram_scales, name="Drawing Scale")
custom_diagram_scale: StringProperty(name="Custom Scale")
diagram_scale: EnumProperty(items=get_diagram_scales, name="Drawing Scale", update=update_diagram_scale)
custom_diagram_scale: StringProperty(name="Custom Scale", update=update_diagram_scale)
raster_x: IntProperty(name="Raster X", default=1000)
raster_y: IntProperty(name="Raster Y", default=1000)
is_nts: BoolProperty(name="Is NTS")
cut_objects: EnumProperty(
items=[
(
".IfcWall|.IfcSlab|.IfcCurtainWall|.IfcStair|.IfcStairFlight|.IfcColumn|.IfcBeam|.IfcMember|.IfcCovering|.IfcSpace",
"Overall Plan / Section",
"",
),
(".IfcElement", "Detail Drawing", ""),
("CUSTOM", "Custom", ""),
],
name="Cut Objects",
)
cut_objects_custom: StringProperty(name="Custom Cut")
active_drawing_style_index: IntProperty(name="Active Drawing Style Index")
# For now, this JSON dump are all the parameters that determine a camera's "Block representation"
# By checking this, you will know whether or not the camera IFC representation needs to be refreshed
def update_representation(self, obj):
representation = json.dumps(
{
"matrix": [list(x) for x in obj.matrix_world],
"raster_x": self.raster_x,
"raster_y": self.raster_y,
"ortho_scale": obj.data.ortho_scale,
"clip_end": obj.data.clip_end,
}
)
if self.representation != representation:
self.representation = representation
return True
return False
class BIMTextProperties(PropertyGroup):
is_editing: BoolProperty(name="Is Editing", default=False)
attributes: CollectionProperty(name="Attributes", type=Attribute)
value: StringProperty(name="Value", default="TEXT")
font_size: EnumProperty(
items=[
("1.8", "1.8 - Small", ""),
@@ -324,17 +387,11 @@ class BIMTextProperties(PropertyGroup):
("5.0", "5.0 - Header", ""),
("7.0", "7.0 - Title", ""),
],
default="2.5",
update=refreshFontSize,
name="Font Size",
)
symbol: EnumProperty(
items=[
("None", "None", ""),
("rectangle-tag", "Rectangle Tag", ""),
("door-tag", "Door Tag", ""),
],
update=refreshFontSize,
name="Symbol",
)
related_element: PointerProperty(name="Related Element", type=bpy.types.Object)
variables: CollectionProperty(name="Variables", type=Variable)
class BIMAssignedProductProperties(PropertyGroup):
is_editing_product: BoolProperty(name="Is Editing Product", default=False)
relating_product: PointerProperty(name="Relating Product", type=bpy.types.Object)
@@ -16,11 +16,12 @@
# 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 xml.etree.ElementTree as ET
import urllib.parse
import pystache
import ntpath
import os
import ntpath
import pystache
import urllib.parse
import xml.etree.ElementTree as ET
import blenderbim.tool as tool
from shutil import copy
from xml.dom import minidom
@@ -30,8 +31,7 @@ class SheetBuilder:
self.data_dir = None
self.scale = "NTS"
def create(self, name, titleblock_name):
sheet_path = os.path.join(self.data_dir, f"{name}.svg")
def create(self, sheet_path, titleblock_name):
root = ET.Element("svg")
root.attrib["xmlns"] = "http://www.w3.org/2000/svg"
root.attrib["xmlns:xlink"] = "http://www.w3.org/1999/xlink"
@@ -59,11 +59,16 @@ class SheetBuilder:
with open(sheet_path, "w") as f:
f.write(minidom.parseString(ET.tostring(root)).toprettyxml(indent=" "))
def add_drawing(self, view_name, sheet_name):
sheet_path = os.path.join(self.data_dir, "sheets", sheet_name + ".svg")
view_path = os.path.join(self.data_dir, "diagrams", view_name + ".svg")
def add_drawing(self, reference, drawing, sheet):
filename = drawing.Name
sheet_name = os.path.splitext(os.path.basename(tool.Drawing.get_document_uri(sheet)))[0]
sheet_dir = os.path.join(self.data_dir, "sheets")
drawing_dir = os.path.join(self.data_dir, "diagrams")
sheet_path = os.path.join(sheet_dir, sheet_name + ".svg")
drawing_path = os.path.join(drawing_dir, filename + ".svg")
underlay_path = os.path.join(drawing_dir, filename + "-underlay.png")
if not os.path.isfile(view_path):
if not os.path.isfile(sheet_path):
raise FileNotFoundError
ET.register_namespace("", "http://www.w3.org/2000/svg")
@@ -72,7 +77,7 @@ class SheetBuilder:
sheet_tree = ET.parse(sheet_path)
sheet_root = sheet_tree.getroot()
view_tree = ET.parse(view_path)
view_tree = ET.parse(drawing_path)
view_root = view_tree.getroot()
# The view is placed into a group with a background image element.
@@ -80,29 +85,50 @@ class SheetBuilder:
# here to accommodate browsers which do not nest images.
view = ET.SubElement(sheet_root, "g")
view.attrib["data-type"] = "drawing"
view.attrib["data-id"] = str(reference.id())
view.attrib["data-drawing"] = drawing.GlobalId
view_width = self.convert_to_mm(view_root.attrib.get("width"))
view_height = self.convert_to_mm(view_root.attrib.get("height"))
background = ET.SubElement(view, "image")
background.attrib["xlink:href"] = "../diagrams/{}.png".format(view_name)
background.attrib["x"] = "30"
background.attrib["y"] = "30"
background.attrib["width"] = str(view_width)
background.attrib["height"] = str(view_height)
if os.path.isfile(underlay_path):
background = ET.SubElement(view, "image")
background.attrib["data-type"] = "background"
background.attrib["xlink:href"] = os.path.relpath(underlay_path, sheet_dir)
background.attrib["x"] = "30"
background.attrib["y"] = "30"
background.attrib["width"] = str(view_width)
background.attrib["height"] = str(view_height)
foreground = ET.SubElement(view, "image")
foreground.attrib["xlink:href"] = "../diagrams/{}.svg".format(view_name)
foreground.attrib["x"] = "30"
foreground.attrib["y"] = "30"
foreground.attrib["width"] = str(view_width)
foreground.attrib["height"] = str(view_height)
if os.path.isfile(drawing_path):
foreground = ET.SubElement(view, "image")
foreground.attrib["data-type"] = "foreground"
foreground.attrib["xlink:href"] = os.path.relpath(drawing_path, sheet_dir)
foreground.attrib["x"] = "30"
foreground.attrib["y"] = "30"
foreground.attrib["width"] = str(view_width)
foreground.attrib["height"] = str(view_height)
self.add_view_title(30, view_height + 35, view)
sheet_tree.write(sheet_path)
def add_schedule(self, schedule_name, sheet_name):
sheet_path = os.path.join(self.data_dir, "sheets", sheet_name + ".svg")
view_path = os.path.join(self.data_dir, "schedules", schedule_name + ".svg")
def remove_drawing(self, reference, sheet):
ET.register_namespace("", "http://www.w3.org/2000/svg")
sheet_path = tool.Drawing.get_document_uri(sheet)
sheet_tree = ET.parse(sheet_path)
sheet_root = sheet_tree.getroot()
for g in sheet_root.findall('{http://www.w3.org/2000/svg}g'):
if g.attrib.get("data-id") == str(reference.id()):
sheet_root.remove(g)
break
sheet_tree.write(sheet_path)
def add_schedule(self, reference, schedule, sheet):
view_path = tool.Drawing.get_document_uri(schedule)
schedule_name = os.path.splitext(os.path.basename(view_path))[0]
sheet_path = tool.Drawing.get_document_uri(sheet)
ET.register_namespace("", "http://www.w3.org/2000/svg")
ET.register_namespace("xlink", "http://www.w3.org/1999/xlink")
@@ -115,30 +141,35 @@ class SheetBuilder:
view_width = self.convert_to_mm(view_root.attrib.get("width"))
view_height = self.convert_to_mm(view_root.attrib.get("height"))
group = ET.SubElement(sheet_root, "g")
group.attrib["data-type"] = "schedule"
view = ET.SubElement(sheet_root, "g")
view.attrib["data-type"] = "schedule"
view.attrib["data-id"] = str(reference.id())
view.attrib["data-schedule"] = str(schedule.id())
foreground = ET.SubElement(group, "image")
foreground = ET.SubElement(view, "image")
foreground.attrib["data-type"] = "table"
foreground.attrib["xlink:href"] = "../schedules/{}.svg".format(schedule_name)
foreground.attrib["x"] = "30"
foreground.attrib["y"] = "30"
foreground.attrib["width"] = str(view_width)
foreground.attrib["height"] = str(view_height)
self.add_view_title(30, view_height + 35, group)
self.add_view_title(30, view_height + 35, view)
sheet_tree.write(sheet_path)
def add_view_title(self, x, y, parent):
title_tree = ET.parse(os.path.join(self.data_dir, "templates", "view-title.svg"))
title_root = title_tree.getroot()
title = ET.SubElement(parent, "image")
title.attrib["data-type"] = "view-title"
title.attrib["xlink:href"] = "../templates/view-title.svg"
title.attrib["x"] = str(x)
title.attrib["y"] = str(y)
title.attrib["width"] = str(self.convert_to_mm(title_root.attrib.get("width")))
title.attrib["height"] = str(self.convert_to_mm(title_root.attrib.get("height")))
def build(self, sheet_name):
def build(self, sheet):
sheet_name = os.path.splitext(os.path.basename(tool.Drawing.get_document_uri(sheet)))[0]
os.makedirs(os.path.join(self.data_dir, "build", sheet_name), exist_ok=True)
sheet_path = os.path.join(self.data_dir, "sheets", f"{sheet_name}.svg")
@@ -149,68 +180,86 @@ class SheetBuilder:
tree = ET.parse(sheet_path)
root = tree.getroot()
titleblock = root.findall('{http://www.w3.org/2000/svg}g[@data-type="titleblock"]')[0]
image = titleblock.findall("{http://www.w3.org/2000/svg}image")[0]
titleblock.append(self.parse_embedded_svg(image, {"number": sheet_name, "revision": "A"}))
titleblock.remove(image)
self.group_number = 1
self.build_drawings(root.findall('{http://www.w3.org/2000/svg}g[@data-type="drawing"]'), sheet_name)
self.build_schedules(root.findall('{http://www.w3.org/2000/svg}g[@data-type="schedule"]'))
self.build_titleblock(root, sheet)
self.build_drawings(root, sheet)
self.build_schedules(root)
with open(os.path.join(self.data_dir, "build", sheet_name, f"{sheet_name}.svg"), "wb") as output:
tree.write(output)
def build_drawings(self, drawings, sheet_name):
for view in drawings:
def build_titleblock(self, root, sheet):
titleblock = root.findall('{http://www.w3.org/2000/svg}g[@data-type="titleblock"]')[0]
image = titleblock.findall("{http://www.w3.org/2000/svg}image")[0]
titleblock.append(self.parse_embedded_svg(image, sheet.get_info()))
titleblock.remove(image)
def build_drawings(self, root, sheet):
sheet_name = os.path.splitext(os.path.basename(tool.Drawing.get_document_uri(sheet)))[0]
for view in root.findall('{http://www.w3.org/2000/svg}g[@data-type="drawing"]'):
reference = tool.Ifc.get().by_id(int(view.attrib["data-id"]))
drawing = tool.Ifc.get().by_id(view.attrib["data-drawing"])
images = view.findall("{http://www.w3.org/2000/svg}image")
background = images[0]
foreground = images[1]
view_title = images[2]
self.scale = "NTS"
# Add foreground
view.append(self.parse_embedded_svg(foreground, {}))
background = None
foreground = None
view_title = None
# Add background
background_path = os.path.join(self.data_dir, "sheets", self.get_href(background))
for image in images:
if image.attrib["data-type"] == "background":
background = image
elif image.attrib["data-type"] == "foreground":
foreground = image
elif image.attrib["data-type"] == "view-title":
view_title = image
copy(background_path, os.path.join(self.data_dir, "build", sheet_name))
if foreground is not None:
view.append(self.parse_embedded_svg(foreground, {}))
# Add view title
foreground_path = self.get_href(foreground)
view.append(
self.parse_embedded_svg(
view_title,
{"no": self.group_number, "name": ntpath.basename(foreground_path)[0:-4], "scale": self.scale},
)
)
if background is not None:
background_path = os.path.join(self.data_dir, "sheets", self.get_href(background))
copy(background_path, os.path.join(self.data_dir, "build", sheet_name))
if view_title is not None:
foreground_path = self.get_href(foreground)
data = reference.get_info()
if not data["Name"]:
data["Name"] = ntpath.basename(foreground_path)[0:-4]
data["Scale"] = tool.Drawing.get_drawing_human_scale(drawing)
view.append(self.parse_embedded_svg(view_title, data))
for image in images:
view.remove(image)
self.group_number += 1
def build_schedules(self, root):
for view in root.findall('{http://www.w3.org/2000/svg}g[@data-type="schedule"]'):
reference = tool.Ifc.get().by_id(int(view.attrib["data-id"]))
schedule = tool.Ifc.get().by_id(int(view.attrib["data-schedule"]))
def build_schedules(self, schedules):
for group in schedules:
images = group.findall("{http://www.w3.org/2000/svg}image")
schedule = images[0]
group_title = images[1]
self.scale = "NTS"
images = view.findall("{http://www.w3.org/2000/svg}image")
group.append(self.parse_embedded_svg(schedule, {}))
path = self.get_href(schedule)
group.append(
self.parse_embedded_svg(
group_title, {"no": self.group_number, "name": ntpath.basename(path)[0:-4], "scale": self.scale}
)
)
table = None
view_title = None
for image in images:
group.remove(image)
if image.attrib["data-type"] == "table":
table = image
elif image.attrib["data-type"] == "view-title":
view_title = image
self.group_number += 1
if table is not None:
view.append(self.parse_embedded_svg(table, {}))
if view_title is not None:
path = self.get_href(table)
data = reference.get_info()
if not data["Name"]:
data["Name"] = schedule.Name or "Unnamed"
view.append(self.parse_embedded_svg(view_title, data))
for image in images:
view.remove(image)
def get_href(self, element):
return urllib.parse.unquote(element.attrib.get("{http://www.w3.org/1999/xlink}href"))
File diff suppressed because it is too large Load Diff

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