Compare commits

..

182 Commits

Author SHA1 Message Date
Dion Moult e0a5c9c8f3 You can now assign derived quantities from products to cost items 2021-04-27 12:09:52 +10:00
Dion Moult 22e087c82a Fix #1443 bug where UI update of type name was not refreshed when you changed it 2021-04-26 19:46:27 +10:00
Dion Moult 57396e1bb9 Make cha-ching noise whenever you're checking out cost schedules 2021-04-26 18:46:35 +10:00
Dion Moult 389076bff4 Implement feature for manipulating cost values, with support for calculation of subtotals 2021-04-26 18:18:42 +10:00
Dion Moult bfcc067257 New feature to manipulate cost item quantities 2021-04-26 12:41:19 +10:00
Dion Moult f88ac5de62 Minor code review to assign / unassign resource products 2021-04-26 09:38:45 +10:00
bosonprojets cdd456bede Assign and Unassign resources to scene objects in BlenderBIM 2021-04-25 23:25:05 +00:00
bosonprojets 7041d17c16 Unassign Resources Usecase added to the ifcopenshel.api 2021-04-25 22:59:01 +00:00
Dion Moult de842f7aba Code review with myoualid for IFC resource module 2021-04-25 12:29:11 +10:00
bosonprojets 6107c39c1c Update to IfcResources panel and authoring subtypes of IfcConstructionResource with ifcopenshell.api 2021-04-24 23:34:38 +00:00
Thomas Krijnen 9584687fcf Ensure solid from tapered extrusions 2021-04-24 20:47:25 +02:00
Thomas Krijnen b231e9051b Update main.py 2021-04-24 15:23:36 +02:00
Thomas Krijnen 40f9875930 Merge branch 'v0.6.0' of https://github.com/IfcOpenShell/IfcOpenShell into v0.6.0 2021-04-24 14:19:10 +02:00
Thomas Krijnen 328773550d SVG: Text annotations in elevations 2021-04-24 14:18:53 +02:00
Dion Moult 303544dc5c Merge branch 'v0.6.0' of github.com:IfcOpenShell/IfcOpenShell into v0.6.0 2021-04-24 20:32:10 +10:00
Dion Moult 1c66f986cc Implement basebones bSDD API client library. 2021-04-24 20:31:50 +10:00
Thomas Krijnen e801629f86 Add BRepExtrema_DistShapeShape option to tree with extend keyword 2021-04-24 11:06:13 +02:00
Thomas Krijnen 74bbd6d116 Merge branch 'v0.6.0' of https://github.com/IfcOpenShell/IfcOpenShell into v0.6.0 2021-04-24 11:05:31 +02:00
Thomas Krijnen e06794ef3d #1153 SVG OBB check 2021-04-24 11:05:18 +02:00
bosonprojets cd7062c905 UX layout for IfcResources in blenderBIM 2021-04-24 05:59:17 +00:00
LaurensJN 42d476cbb0 Replace old naming of ifccityjson README.md 2021-04-23 17:12:29 +02:00
Laurens Oostwegel cd0d3908d3 Add ifccityjson to start with supporting conversions between IFC and CityJSON files 2021-04-23 17:05:59 +02:00
Thomas Krijnen cb70f97789 Update ids.py 2021-04-23 12:14:52 +02:00
Thomas Krijnen 572ed0db0f Update ids.py 2021-04-23 12:13:41 +02:00
Dion Moult 43d78af767 Fix crash when pressing undo after assigning an IFC class to an object. Baby steps to solving the "Undo Sync". 2021-04-23 17:44:50 +10:00
bosonprojets c7aa42bfd5 Feature to Add Crew Resources and SubContract Resources with the ifcopenshell.api and blenderB 2021-04-23 06:38:11 +00:00
Dion Moult 18d7193efc Fix bug where a new Blender file would remember old pset templates and libraries 2021-04-22 18:37:41 +10:00
Dion Moult 8da92557cc Fix bug where deletion syncing may fail on freshly created objects 2021-04-22 18:37:03 +10:00
Dion Moult 761b4c035f Optimisations for construction sequencing task trees where there are thousands of tasks 2021-04-22 18:34:45 +10:00
Dion Moult ee0f7f1677 You can now import an IFC work schedule from a P6 XML export 2021-04-22 15:44:58 +10:00
Dion Moult dca2e58586 Fix bug where you shouldn't be able to reassign classes of non-rooted elements 2021-04-22 14:00:18 +10:00
Dion Moult 3810255f04 Fix bug where syncing was not maintained across file saves and collection names were not synced 2021-04-22 13:56:01 +10:00
Dion Moult d3ca647cd5 Minor code review fix 2021-04-22 10:53:25 +10:00
bosonprojets 8b35a3aadd Assign control to cost items and UX Workschedules and Costchedules cleanup 2021-04-22 00:18:22 +00:00
Dion Moult 78314b1ec7 Forgot to commit append_asset usecase, also minor fixes 2021-04-21 20:49:58 +10:00
Dion Moult ef88b30639 Fix bug where import mesh cleaning would inadvertently mark meshes as edited 2021-04-21 20:40:45 +10:00
Dion Moult 5e815e7832 You can now append object types from an IFC project library to your active project 2021-04-21 16:45:11 +10:00
Dion Moult e7b153cbf6 You can now generate gantt charts from work schedules 2021-04-21 12:57:31 +10:00
Dion Moult 9b855bed2d Implement unassigning products to tasks 2021-04-21 11:53:33 +10:00
Dion Moult 3143f8c9a5 Code review for assign product with myoualid 2021-04-21 11:11:08 +10:00
Dion Moult 8dd781aea2 Fix bug where you couldn't assign objects to a fresh layer 2021-04-21 11:03:36 +10:00
bosonprojets 0506475f26 Feature to add building elements to construction scheduling tasks in blenderBIM and assign product to process with the ifcopenshell.api 2021-04-20 23:51:55 +00:00
Dion Moult ac092fb6f8 Minor fix - add dateutil as dependency 2021-04-21 09:14:57 +10:00
Dion Moult 9beddca2f7 You can now toggle showing time columns in work schedule, minor fixes and ui cleanup 2021-04-20 23:34:10 +10:00
Dion Moult 6d9770542f Fixes to editing task times 2021-04-20 17:11:06 +10:00
bosonprojets 544d6ee695 Add IfcTaskTime to IfckTask 2021-04-20 05:13:03 +00:00
Dion Moult b8963b3d6e You can now modify project context declarations for project libraries, denoting which assets are part of the library 2021-04-20 13:51:41 +10:00
Dion Moult f2bd46d699 Selecting an IFC file dialog now filters ifc files for convenience 2021-04-20 12:45:42 +10:00
Dion Moult 8ac8ad33b3 You can now load and browse a tree of project library types products 2021-04-20 12:45:20 +10:00
Dion Moult 44bbd17d97 Collection assignment is now synced on export, so you don't need to manually manage your tree 2021-04-19 16:08:57 +10:00
Dion Moult 8f0e67a35b You can now assign task successor and predecessors. Sweet. Thanks myoualid! 2021-04-19 13:09:00 +10:00
Dion Moult d2d4112724 Clean up UX to either edit a work schedule attributes or task tree but not both simultaneously 2021-04-19 11:42:39 +10:00
Dion Moult 209eea6a18 You can now edit tasks. Thanks myoualid! 2021-04-19 11:10:49 +10:00
Dion Moult 85426b3fef Minor tweak 2021-04-19 10:41:25 +10:00
Thomas Krijnen c602f0f233 Merge branch 'v0.6.0' of https://github.com/IfcOpenShell/IfcOpenShell into v0.6.0 2021-04-18 15:06:34 +02:00
Thomas Krijnen bb6312e174 #1153 --svg-without-storeys and correct positioning and scale based on annotation block 2021-04-18 15:03:28 +02:00
Dion Moult d6d70af5a0 You can now copy classes in bulk. 2021-04-18 20:24:08 +10:00
Dion Moult 8187c75183 If you are already authoring a file, exporting now no longer prompts for a file location. 2021-04-18 20:17:54 +10:00
Rick Brice 01cfcebb09 Fixes problem with dereferencing the end iterator for empty std::string objects 2021-04-18 12:00:48 +02:00
Rick Brice 75040b6ffb Fixes initialization issues in IfcEntityInstanceData constructors
attributes_ was allocated as an array of Argument pointers but the pointers were uninitialized in the construction. Initialization was done in the HeaderEntity class constructor.

offset_in_file_ is uninitialized in one constructor
2021-04-18 12:00:48 +02:00
Dion Moult 6c8858e51d Fix bug where edit mode syncing didn't work if you edited many objects at once 2021-04-18 19:58:07 +10:00
Dion Moult 6bb63817af Fix #1435. Fix bug where box reps could be duplicated upon adding a new body, and ensure all styles are created prior to adding new body geometry. 2021-04-18 19:28:05 +10:00
Dion Moult 1e299a0ddd Make remove_deep_batched the default since I'm the only guy using it so far and I'm feeling lucky! Let's battle test it! 2021-04-18 19:02:39 +10:00
bosonprojets 3201c234fa Feature to create IfcTask trees for entities of IfcWorkSchedule 2021-04-18 00:49:43 +00:00
Dion Moult f5d27621e7 Fix #1436. Bug where saving is done twice in authoring mode. 2021-04-17 20:18:01 +10:00
Dion Moult 3e1eb9648f Fix showstopper bug with editing pset templates module 2021-04-17 17:59:12 +10:00
bosonprojets 5f9474acb7 Feature to add multiple summary cost items and remove cost items 2021-04-15 23:16:32 +00:00
Dion Moult f4e6e924b1 Spaces are now always imported, and consistent with the rest of the spatial tree. Yes, I don't know why I held off for so long. Deprecate "import spaces". Spaces are important. 2021-04-15 19:56:18 +10:00
Dion Moult bcfc97037c Spatial aggregations are auto assigned when you create them. Neat. 2021-04-15 19:33:01 +10:00
Dion Moult 65d0abd072 Material profile set usages are now supported. Hooray! Thanks Jesusbill! 2021-04-15 18:38:51 +10:00
Thomas Krijnen 776c68e2b0 --svg-write-poly 2021-04-15 10:36:37 +02:00
Dion Moult dab2d5ce04 You can now toggle expansion of the cost item tree 2021-04-15 11:14:03 +10:00
Dion Moult 29b33661d7 You can now assign summary tasks to a work schedule. Thanks myoualid! 2021-04-15 10:38:45 +10:00
Sigma Dimensions 5dec636584 New Feature to add Tasks and Task Relationships in the python ifcopenshell API (#1432) 2021-04-15 09:40:29 +10:00
Dion Moult d11b91f152 Fix bug where adding a new material didn't make it immediately available 2021-04-14 18:41:13 +10:00
Dion Moult b0fc98e384 Fix bug where Blender prop enums persisted when starting new files 2021-04-14 18:41:13 +10:00
Tigran Khachatryan 56f09d2541 Broken link fixes
Links to Open Cascade were broken
2021-04-13 16:57:15 +02:00
Tigran Khachatryan 4484b77232 broken link fix
OCCT download link has changed
2021-04-13 16:57:15 +02:00
Dion Moult f6c054066b You can now assign spatial containers in bulk 2021-04-13 20:09:01 +10:00
Dion Moult d5c70e51f2 You can now edit structural member axis orientations. Thanks Jesusbill! 2021-04-13 13:04:05 +10:00
Dion Moult cab9e4d892 You can now assign aggregates in bulk 2021-04-13 08:25:43 +10:00
Dion Moult 14cd8289ea You can now create IFC project libraries 2021-04-11 21:56:45 +10:00
Dion Moult 4202c08487 You can now select stuff like doors / windows instead of having to select the opening element itself when adding voids, with some assumptions. See #1426. 2021-04-11 21:15:39 +10:00
Julien Moutinho a70fb41652 blenderbim: fix reference before assignment in PieAddOpening (#1426)
Co-authored-by: Julien Moutinho <julm+blenderbim@sourcephile.fr>
2021-04-11 20:52:42 +10:00
Dion Moult df437fd3e3 You can now add cost items into a cost schedule, and as sub items to a parent cost item. Thanks myoualid and Jesusbill! 2021-04-10 20:40:35 +10:00
Dion Moult 3102dbcd44 Fix bug where you couldn't add a presentation layer 2021-04-10 20:01:37 +10:00
Dion Moult 9d838db096 You can now edit cost schedule attributes. Thanks Jesusbill and myoualid! 2021-04-10 18:10:33 +10:00
johltn 0c9d555693 Handle more restrictions, save validation results to JSON objects, modification to classification class, and partly handle material terms 2021-04-09 10:56:52 +02:00
johltn 4b637c7049 Typo fix 2021-04-09 10:56:52 +02:00
Dion Moult fd3a873bbd Fix bug where you couldn't do a partial import when whitelisting only spatial elements 2021-04-09 15:43:41 +10:00
Dion Moult 5cea988911 You can now assign parameterized profiles to material profiles. Thanks Jesusbill! 2021-04-08 21:03:37 +10:00
Dion Moult 70fcbfa990 Fix bug where extrusion direction was incorrectly detected for some automagic mesh to parametric solid conversions 2021-04-08 17:15:05 +10:00
Dion Moult c541aa78e5 Fix bug where syncing upon export was broken 2021-04-08 12:22:59 +10:00
Dion Moult b821e62391 You can now add/remove material profiles in a profile set. Thanks Jesusbill! 2021-04-08 08:57:45 +10:00
Dion Moult 41141b8fc9 Geolocation micromvd now supports checking true north 2021-04-07 16:08:02 +10:00
Dion Moult ae0789e4ec New assign / unassign declaration usecase, minor code & formatting cleanup 2021-04-07 13:15:16 +10:00
Sigma Dimensions 13d4a99358 Feature to manipulate Work Calendars (#1420) 2021-04-07 09:51:20 +10:00
Sigma Dimensions becb375ec2 Feature work schedule (#1419)
* ifcopenshell.api modules Persons and Organisations account for IFC4 SCHEMA CHANGE

* New Feature to manpiulate Work Plans
2021-04-07 09:31:16 +10:00
Sigma Dimensions a777a558bd Feature edit work plan (#1418)
* ifcopenshell.api modules Persons and Organisations account for IFC4 SCHEMA CHANGE

* New Feature to Edit Workplans
2021-04-07 09:04:57 +10:00
Bruno Postle de37aeb515 faces in DXF polyfacemesh objects can have three or four sides (#1414) 2021-04-07 08:28:11 +10:00
Sigma Dimensions 23be1a6939 ifcopenshell.api modules Persons and Organisations account for IFC4 SCHEMA CHANGE (#1417) 2021-04-07 08:28:02 +10:00
Dion Moult 8edced5a0c Fix bug where rotation check in geolocation micromvd broke in the last release 2021-04-07 08:03:01 +10:00
Dion Moult 21937e88e8 Hotfix 2021-04-05 21:20:05 +10:00
Thomas Krijnen f7da83737a SVG fix bug in point interpolation 2021-04-04 22:34:16 +02:00
Thomas Krijnen 27663d80ba SVG sample more points for space name position check 2021-04-04 21:15:13 +02:00
Thomas Krijnen 4ec5d4fc65 Don't fail on lack of IfcAnnotation 2021-04-04 21:06:38 +02:00
Dion Moult 2b34ca9eb7 Minor fix 2021-04-04 12:59:39 +10:00
Dion Moult 2bb9810072 Bump IfcOpenShell 2021-04-04 12:54:08 +10:00
Dion Moult c7e87324de Fix bug where Blender floats lost precision for geolocation. Everything is now a string. 2021-04-04 12:43:41 +10:00
Dion Moult ec62d8fbb9 Fix bug where assigning a spatial container didn't update the object placement tree 2021-04-04 09:02:14 +10:00
Dion Moult 7b5aae9487 Implement getting and setting true north rotation independent of grid north 2021-04-04 08:58:24 +10:00
Dion Moult 12c94fafab Minor code shuffling to reflect the fact that projected CRS has a higher priority than map conversion 2021-04-03 22:05:20 +11:00
Dion Moult 09be170820 Geolocation utilities now support converting either X axis or Y axis to an angle 2021-04-03 21:58:35 +11:00
Dion Moult 966af5a757 You can now add / remove cost schedules 2021-04-03 19:46:52 +11:00
Thomas Krijnen 89557460c8 SVG text annotation descriptions 2021-04-02 21:59:49 +02:00
Thomas Krijnen 2bad6b2722 yet another todo note 2021-04-02 21:25:01 +02:00
Thomas Krijnen e5cd30c6d0 SVG dimension text offset depending on scale 2021-04-02 21:21:27 +02:00
Thomas Krijnen 6f2da2896e try catch convert_placement() 2021-04-02 21:20:53 +02:00
Dion Moult 0387c778f4 Fix bug where you couldn't add type instances for IFC4 2021-04-01 15:03:42 +11:00
Dion Moult 76568ea67c Fix bug where express has a typo that broke the ability to add ifc transformers 2021-04-01 14:55:10 +11:00
Dion Moult 8e2a1a1649 A reference graph view context is now created automatically for convenience when you create an analytical model 2021-04-01 12:06:25 +11:00
Dion Moult cb5f57d68a Point connections are now treated as regular geometry, not an edge case (get it?). See #1409. Thanks @Jesusbill! 2021-04-01 11:58:46 +11:00
Dion Moult e7c6f28312 You can now add structural connections to members 2021-04-01 11:09:44 +11:00
Jesusbill 1820f72bc4 You can now remove structural connection conditions + minor mods 2021-03-31 18:58:02 +02:00
Dion Moult 1631dc39f5 You can now add and remove work plans with a new UI. Thanks @bosonprojets and @jesusbill! 2021-03-31 11:35:40 +11:00
Dion Moult d2f19e7c28 You can now edit structural boundary conditions on member connections 2021-03-30 12:00:25 +11:00
Jesusbill 736703336c work on structural connection conditions 2021-03-30 01:12:33 +02:00
Dion Moult 44df1f676d Finish decoupling ownership history code from Blender 2021-03-29 16:53:11 +11:00
Dion Moult f1ffb81c86 See #841. Minor optimisation and fixes. Thanks @ecke101 ! 2021-03-29 13:55:05 +11:00
Thomas Krijnen 08ec3fc73f SVG --space-name-transform 2021-03-28 22:16:58 +02:00
Thomas Krijnen f1d7cf1dfb SVG --storey-height-line-length 2021-03-28 21:38:29 +02:00
Thomas Krijnen 1ee488b148 Quick solution for inline dasharray 2021-03-28 16:55:52 +02:00
Thomas Krijnen ecb9827e7f Explicit cast 2021-03-28 13:04:16 +02:00
Thomas Krijnen 88f26eaead Work towards #1153 2021-03-28 12:48:20 +02:00
Dion Moult ce03742aab New ifcopenshell.api.run syntax, and rename create_product to create_entity. See #1399. 2021-03-27 22:53:13 +11:00
Thomas Krijnen 62540f0b00 #1404 2021-03-27 10:00:41 +01:00
Dion Moult 406b8b4540 IfcOpenShell API now uses kwargs to improve user syntax. See #1399. 2021-03-27 19:14:46 +11:00
Dion Moult ca24838a3f Don't load OCC upon startup, in preparation for deprecation when IfcConvert can handle #1153. See #1400 and #1358. 2021-03-27 18:38:20 +11:00
Thomas Krijnen ff7219b557 CMake < 3.7 compatibility
See https://github.com/glfw/glfw/issues/1584
2021-03-26 20:40:01 +01:00
Jesusbill 711acfe120 revert erroneous mod from bulk change 2021-03-26 17:51:35 +01:00
Dion Moult f1acda85c9 Call the daily builds "developer testing builds" to prevent misleading users 2021-03-26 20:52:32 +11:00
Dion Moult 228f62d218 Minor fix 2021-03-26 20:49:57 +11:00
Dion Moult 6186a88568 Rename misleading "ifcblenderexport" directory to "blenderbim" 2021-03-26 20:44:14 +11:00
Dion Moult 074a8c287f Support owner histories for a bunch more usecases. About half done. 2021-03-26 10:39:07 +11:00
Dion Moult d4736710a5 Thanks @ihabelaghoury! You can now select objects in the viewport by GlobalId from the IFC inspector 2021-03-25 20:29:37 +11:00
Dion Moult 0bc86631a5 Minor fixes after the refactor mess 2021-03-25 20:19:48 +11:00
Dion Moult 9751b6af05 Deletions are now synced on export, so you don't need to delete via the UI. 2021-03-25 16:48:58 +11:00
Dion Moult 6d641aaac1 Still cleaning up related to ownership histories. See #1399. 2021-03-25 16:32:43 +11:00
Dion Moult 489a248e9a Refactor ownership history system to be simpler and just a usecase. See #1399. 2021-03-25 11:42:12 +11:00
Jesusbill 9c00872a57 replace settings=None with {} 2021-03-25 00:54:42 +01:00
Dion Moult d77bc6bdaa First attempt at ripping out agnostic code into ifcopenshell.api namespace. See #1399. 2021-03-25 10:48:48 +11:00
Priit Laes 911d703f57 build: Use -Wno-deprecated-copy for GCC 9+
OpenCascade causes a lot of warnings due to -Wdeprecated-copy on GCC 9+.
These warnings are also emitted with clang, but only once per file.
2021-03-24 09:25:13 +01:00
Dion Moult 09bf5b85aa Refactor getting primitive data type into util 2021-03-24 16:09:29 +11:00
Dion Moult 41a694d0dc Fix bug where you couldn't set logical properties in psets. See #1086. 2021-03-24 14:57:52 +11:00
Jesusbill 6232ee36f9 add structural connection connected members - wip 2021-03-24 00:15:22 +01:00
Jesusbill 5b1cc2cab5 rename connection to boundary condition + minor edits 2021-03-24 00:10:07 +01:00
Thomas Krijnen f706adfb81 OCCT pre 7 compatibility 2021-03-23 13:27:31 +01:00
Thomas Krijnen b0358a5a7b Keep a set of visited edges in sort 2021-03-23 10:43:12 +01:00
Thomas Krijnen eb09cc8af8 Costrain number of edges in sorted_edges 2021-03-22 14:03:24 +01:00
Dion Moult 7c61c7d200 You can now import and export IFCCSV to the current file in memory without writing to disk 2021-03-22 16:19:29 +11:00
Dion Moult 92d28901ac Fix bug where IFCCSV delimiter was not able to be specified on read/write properly 2021-03-22 15:35:17 +11:00
Dion Moult 32769184b0 Attempt number 4 2021-03-21 20:13:27 +11:00
Dion Moult de7f1d9d46 Attempt number 3 2021-03-21 20:10:53 +11:00
Dion Moult cbd0c8a410 Attempt number 2 2021-03-21 20:07:40 +11:00
Dion Moult f4f40c4b16 Fix fatal bug with BIMTester where languages were not bundled in installation. 2021-03-21 20:04:24 +11:00
Dion Moult f9d70fd51d Whoops 2021-03-21 12:13:42 +11:00
Dion Moult bb43405299 Fix #1294 bug where BIMTester doesn't run if you have multiple drives on Windows. Thanks @rbertucat! 2021-03-21 12:07:27 +11:00
Priit Laes 00cb3cd0db travis: Bump to Ubuntu 20.04, Python3 and use apt addon as well 2021-03-20 10:58:47 +01:00
Priit Laes 823f988536 travis: Use system JSON package 2021-03-20 10:58:47 +01:00
Priit Laes d95977a784 travis: Simplify directory "shuffling" 2021-03-20 10:58:47 +01:00
Priit Laes 4f9991fb13 travis: Use make -j $(nproc) 2021-03-20 10:58:47 +01:00
Priit Laes d8b2cc77cc travis: Enable ccache 2021-03-20 10:58:47 +01:00
Priit Laes 077f9b701d ifcgeom: Remove unneeded cast causing warning by -Wignored-qualifiers 2021-03-20 10:56:57 +01:00
Priit Laes 36ade628cf Fix grammar 2021-03-20 10:56:57 +01:00
Priit Laes 0f59f3e35a ifcgeom: Remove unused variable 2021-03-20 10:56:57 +01:00
Priit Laes 31e7bad056 ifcgeom: Reorder the members in constructors to avoid side effects (Wreorder) 2021-03-20 10:56:57 +01:00
TestPrab eee693dfcb Added requirements file (#1397) 2021-03-20 20:31:36 +11:00
Dion Moult 4a8e03862b Improve usability that you can run a patch recipe without providing arguments. See #1393. 2021-03-20 20:04:10 +11:00
Dion Moult 04f7fc0bdb Minor fix 2021-03-20 20:02:53 +11:00
Dion Moult fbe9bb1ee2 Fix bcf bug for optional project.bcfp and optional project data 2021-03-20 17:56:43 +11:00
Jesusbill ca59b3220b small fix and mod in ifc2ca 2021-03-19 23:39:25 +01:00
Jesusbill 39c8c8db37 small fix and mods 2021-03-19 23:38:28 +01:00
508 changed files with 9278 additions and 2938 deletions
+5 -5
View File
@@ -3,7 +3,7 @@ name: Publish-blenderbim-multiplatform
on:
push:
paths:
- 'src/ifcblenderexport/**'
- 'src/blenderbim/**'
- '.github/workflows/ci-blenderbim-matrix.yml'
env:
@@ -52,15 +52,15 @@ jobs:
run: echo "::set-output name=date::$(date +'%y%m%d')"
- name: Compile
run: |
cp -r src/ifcblenderexport src/ifcblenderexport_${{ matrix.config.short_name }}_${{ matrix.pyver }} &&
cd src/ifcblenderexport_${{ matrix.config.short_name }}_${{ matrix.pyver }} &&
cp -r src/blenderbim src/blenderbim_${{ matrix.config.short_name }}_${{ matrix.pyver }} &&
cd src/blenderbim_${{ matrix.config.short_name }}_${{ matrix.pyver }} &&
make dist PLATFORM=${{ matrix.config.short_name }} PYVERSION=${{ matrix.pyver }}
- name: Upload Zip file to release
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: src/ifcblenderexport_${{ matrix.config.short_name }}_${{ matrix.pyver }}/dist/blender28-bim-${{steps.date.outputs.date}}-${{ matrix.pyver }}-${{ matrix.config.short_name }}.zip
file: src/blenderbim_${{ matrix.config.short_name }}_${{ matrix.pyver }}/dist/blender28-bim-${{steps.date.outputs.date}}-${{ matrix.pyver }}-${{ matrix.config.short_name }}.zip
asset_name: blender28-bim-${{steps.date.outputs.date}}-${{ matrix.pyver }}-${{ matrix.config.short_name }}.zip
tag: "blenderbim-${{steps.date.outputs.date}}"
overwrite: true
body: "Package release blenderbim-${{steps.date.outputs.date}}"
body: "Daily developer testing build blenderbim-${{steps.date.outputs.date}}"
+5
View File
@@ -1,4 +1,7 @@
# Dependency and build folders created by the build scripts
/_build-vs2017-x64/
/_deps-vs2017-x64-installed/
/_deps/
/deps*/
/build*/
/install*/
@@ -23,3 +26,5 @@ __pycache__
*.mo
# Vim
*.swp
+41 -34
View File
@@ -1,56 +1,60 @@
language: cpp
compiler: gcc
cache: ccache
os: linux
dist: bionic
sudo: required
dist: focal
addons:
apt:
update: true
packages:
- libboost-date-time-dev
- libboost-filesystem-dev
- libboost-iostreams-dev
- libboost-program-options-dev
- libboost-regex-dev
- libboost-system-dev
- libboost-thread-dev
- libocct-data-exchange-dev
- libocct-foundation-dev
- libocct-modeling-algorithms-dev
- libocct-modeling-data-dev
- libxml2-dev
- nlohmann-json3-dev
- opencollada-dev
- python3-all-dev
- swig
before_install:
- "sudo add-apt-repository -y ppa:freecad-community/ppa"
- sudo apt-get update -qq
install:
- |
sudo apt-get install -y \
libocct-data-exchange-dev libocct-foundation-dev libocct-modeling-algorithms-dev libocct-modeling-data-dev \
libboost-system-dev libboost-program-options-dev libboost-regex-dev libboost-thread-dev libboost-date-time-dev libboost-iostreams-dev libboost-filesystem-dev \
build-essential cmake python2.7 libpython2.7-dev swig zlib1g liblzma5 opencollada-dev wget apt-transport-https
- sudo mkdir -p /usr/include/json/nlohmann/
- sudo wget https://github.com/nlohmann/json/releases/download/v3.6.1/json.hpp -O /usr/include/json/nlohmann/json.hpp
# - sudo sh -c 'curl https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -'
# - sudo sh -c 'curl https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list > /etc/apt/sources.list.d/dart_stable.list'
# - sudo apt-get update -qq && sudo apt-get install -y dart
# - export PATH=$PATH:/usr/lib/dart/bin:$HOME/.pub-cache/bin
# - git clone https://github.com/KhronosGroup/glTF-Validator && pushd glTF-Validator && pub get && pub global activate --source path ./ && popd
before_script:
- if [ $TRAVIS_OS_NAME == "linux" ]; then ccache -z; fi
script:
- pwd
- cd cmake
- mkdir build
- cd build
- mkdir build && cd build
- |
cmake \
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DOCC_INCLUDE_DIR=/usr/include/opencascade \
-DOCC_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu \
-DPYTHON_LIBRARY=/usr/lib/python2.7/config-x86_64-linux-gnu/libpython2.7.so \
-DPYTHON_INCLUDE_DIR=/usr/include/python2.7 \
-DPYTHON_EXECUTABLE=/usr/bin/python2.7 \
-DLIBXML2_INCLUDE_DIR=/usr/include/libxml2 \
-DLIBXML2_LIBRARIES="/usr/lib/x86_64-linux-gnu/libxml2.so;/lib/x86_64-linux-gnu/libz.so.1;/lib/x86_64-linux-gnu/liblzma.so.5;/usr/lib/x86_64-linux-gnu/libicuuc.so.60;/usr/lib/x86_64-linux-gnu/libicudata.so.60" \
-DPYTHON_EXECUTABLE:FILEPATH=/usr/bin/python3 \
-DPYTHON_INCLUDE_DIR:PATH=/usr/include/python3.8 \
-DPYTHON_LIBRARY:FILEPATH=/usr/lib/x86_64-linux-gnu/libpython3.8.so \
-DGLTF_SUPPORT=On \
-DJSON_INCLUDE_DIR=/usr/include/json \
..
- sudo make -j2 install
-DJSON_INCLUDE_DIR=/usr/include \
../cmake
# TODO: Drop sudo
- sudo make -j $(nproc) install
- cd ../../test
- /usr/bin/python2.7 tests.py
- cd ../test
- /usr/bin/python tests.py
- cd input
- /usr/local/bin/IfcConvert -yv acad2010_walls.ifc acad2010_walls.glb
# - gltf_validator acad2010_walls.glb
- /usr/bin/python2.7 -c "from __future__ import print_function; from io import open; import ifcopenshell; f = ifcopenshell.open('encoding.ifc'); assert list(map(ord, f[1][0])) == [39, 97, 39, 32, 49, 109, 179, 32, 8804, 32, 53, 109, 179, 32, 8805, 32, 49, 48, 109, 179]"
- /usr/bin/python -c "from io import open; import ifcopenshell; f = ifcopenshell.open('encoding.ifc'); assert list(map(ord, f[1][0])) == [39, 97, 39, 32, 49, 109, 179, 32, 8804, 32, 53, 109, 179, 32, 8805, 32, 49, 48, 109, 179]"
- |
(for i in *.ifc; do \
@@ -64,3 +68,6 @@ script:
- echo Succeeded
- grep 0$ statuses
- grep 0$ statuses | wc -l
after_script:
- if [ $TRAVIS_OS_NAME == "linux" ]; then ccache -s; fi
+3 -3
View File
@@ -19,8 +19,8 @@ Prerequisites
Dependencies
-------------
* [Boost](http://www.boost.org/)
* [Open Cascade](http://opencascade.org) - *optional*, but required for building IfcGeom
([official](http://www.opencascade.org/getocc/download/loadocc/), "OCCT", or [community edition](https://github.com/tpaviot/oce), "OCE")
* [Open Cascade](https://dev.opencascade.org/) - *optional*, but required for building IfcGeom
([official](https://dev.opencascade.org/release), "OCCT", or [community edition](https://github.com/tpaviot/oce), "OCE")
For converting IFC representation items into BRep solids and tesselated meshes
* [OpenCOLLADA](https://github.com/khronosGroup/OpenCOLLADA/) - *optional*
For IfcConvert to be able to write tessellated Collada (.dae) files
@@ -101,7 +101,7 @@ 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 http://www.opencascade.org/getocc/download/loadocc/
**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:
+4
View File
@@ -466,6 +466,10 @@ ElSE()
else()
add_definitions(-Wno-maybe-uninitialized)
endif()
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU" AND (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 9.0 OR CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 9.0))
# OpenCascade spews a lot of deprecated-copy warnings
add_definitions(-Wno-deprecated-copy)
endif()
# -fPIC is not relevant on Windows and creates pointless warnings
if (UNIX)
add_definitions(-fPIC)
+6 -3
View File
@@ -50,9 +50,12 @@ class BcfXml:
zip_file = zipfile.ZipFile(filepath)
self.filepath = tempfile.mkdtemp()
zip_file.extractall(self.filepath)
data = self._read_xml("project.bcfp", "project.xsd")
self.project.project_id = data["Project"]["@ProjectId"]
self.project.name = data["Project"]["Name"]
if os.path.isfile(os.path.join(self.filepath, "project.bcfp")):
data = self._read_xml("project.bcfp", "project.xsd")
self.project.extension_schema = data["ExtensionSchema"]
if "Project" in data:
self.project.project_id = data["Project"]["@ProjectId"]
self.project.name = data["Project"].get("Name")
return self.project
def edit_project(self):
+1
View File
@@ -2,6 +2,7 @@ class Project:
def __init__(self):
self.project_id = ""
self.name = ""
self.extension_schema = ""
class BimSnippet:
+1
View File
@@ -0,0 +1 @@
xmlschema
@@ -52,16 +52,16 @@ endif
# Provides IfcOpenShell Python functionality
ifeq ($(PYVERSION), py37)
cd dist/working && wget https://s3.amazonaws.com/ifcopenshell-builds/ifcblender-python-37-v0.6.0-517b819-$(PLATFORM)64.zip
cd dist/working && wget https://s3.amazonaws.com/ifcopenshell-builds/ifcblender-python-37-v0.6.0-ff7219b-$(PLATFORM)64.zip
endif
ifeq ($(PYVERSION), py39)
cd dist/working && wget https://s3.amazonaws.com/ifcopenshell-builds/ifcblender-python-39-v0.6.0-517b819-$(PLATFORM)64.zip
cd dist/working && wget https://s3.amazonaws.com/ifcopenshell-builds/ifcblender-python-39-v0.6.0-ff7219b-$(PLATFORM)64.zip
endif
cd dist/working && unzip ifcblender*
cp -r dist/working/io_import_scene_ifc/ifcopenshell dist/blenderbim/libs/site/packages/
# See bug #812
cd dist/working && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcblenderexport/occ_utils.py
cd dist/working && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/blenderbim/occ_utils.py
cd dist/working && mv occ_utils.py ../blenderbim/libs/site/packages/ifcopenshell/geom/occ_utils.py
rm -rf dist/working
@@ -142,11 +142,16 @@ endif
cd dist/working && unzip v0.6.0.zip
# IfcOpenBot sometimes lags behind, so we hotfix the Python utilities
cp -r dist/working/IfcOpenShell-0.6.0/src/ifcopenshell-python/ifcopenshell/util/* dist/blenderbim/libs/site/packages/ifcopenshell/util/
cp -r dist/working/IfcOpenShell-0.6.0/src/ifcopenshell-python/ifcopenshell/api/* dist/blenderbim/libs/site/packages/ifcopenshell/api/
# Provides bcf functionality
cp -r dist/working/IfcOpenShell-0.6.0/src/bcf/bcf dist/blenderbim/libs/site/packages/
# Provides IFCClash functionality
cp -r dist/working/IfcOpenShell-0.6.0/src/ifcclash/* dist/blenderbim/libs/site/packages/
# Provides BIMTester functionality
cd dist/working && python -m venv env
cd dist/working && . env/bin/activate && pip install pybabel
cd dist/working && . env/bin/activate && pip install babel
cd dist/working && . env/bin/activate && ./env/bin/pybabel compile -d ./IfcOpenShell-0.6.0/src/ifcbimtester/bimtester/locale/
cp -r dist/working/IfcOpenShell-0.6.0/src/ifcbimtester/bimtester dist/blenderbim/libs/site/packages/
# Provides IFCCOBie functionality
cp -r dist/working/IfcOpenShell-0.6.0/src/ifccobie/* dist/blenderbim/libs/site/packages/
@@ -172,6 +177,20 @@ endif
cp -r dist/working/svgwrite-1.3.1/svgwrite dist/blenderbim/libs/site/packages/
rm -rf dist/working
# Provides fuzzy date parsing for construction sequencing
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/be/ed/5bbc91f03fa4c839c4c7360375da77f9659af5f7086b7a7bdda65771c8e0/python-dateutil-2.8.1.tar.gz
cd dist/working && tar -xzvf python-dateutil*
cp -r dist/working/python-dateutil-2.8.1/dateutil dist/blenderbim/libs/site/packages/
rm -rf dist/working
# Provides jsgantt-improved supports for web-based construction sequencing gantt charts
mkdir dist/working
cd dist/working && wget https://raw.githubusercontent.com/jsGanttImproved/jsgantt-improved/master/dist/jsgantt.js
cd dist/working && wget https://raw.githubusercontent.com/jsGanttImproved/jsgantt-improved/master/dist/jsgantt.css
cp dist/working/jsgantt* dist/blenderbim/bim/data/gantt/
rm -rf dist/working
# Required by IFCDiff
mkdir dist/working
cd dist/working && wget https://github.com/Moult/deepdiff/archive/master.zip
@@ -249,6 +268,11 @@ endif
cd dist/working && wget https://files.pythonhosted.org/packages/c8/4b/d0a8c23b6c8985e5544ea96d27105a273ea22051317f850c2cdbf2029fe4/behave-1.2.6.tar.gz
cd dist/working && tar -xzvf behave*
cd dist/working/behave-1.2.6/ && cp -r behave ../../blenderbim/libs/site/packages/
# See bug #1294
cd dist/working/ && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcbimtester/patch/model_core.patch
cd dist/working/ && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcbimtester/patch/runner_util.patch
cd dist/working/ && patch ../blenderbim/libs/site/packages/behave/model_core.py < model_core.patch
cd dist/working/ && patch ../blenderbim/libs/site/packages/behave/runner_util.py < runner_util.patch
rm -rf dist/working
# Required by behave

Before

Width:  |  Height:  |  Size: 399 KiB

After

Width:  |  Height:  |  Size: 399 KiB

@@ -24,6 +24,8 @@ if bpy is not None:
"aggregate": None,
"geometry": None,
"cobie": None,
"resource": None,
"cost": None,
"sequence": None,
"group": None,
"structural": None,
@@ -124,7 +126,6 @@ if bpy is not None:
prop.BIMProperties,
prop.DocProperties,
prop.IfcParameter,
prop.BoundaryCondition,
prop.PsetQto,
prop.GlobalId,
prop.RepresentationItem,
@@ -173,7 +174,6 @@ if bpy is not None:
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.app.handlers.save_pre.append(handler.storeIdMap)
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)
@@ -200,7 +200,6 @@ if bpy is not None:
bpy.utils.unregister_class(cls)
bpy.app.handlers.load_post.remove(handler.setDefaultProperties)
bpy.app.handlers.load_post.remove(handler.loadIfcStore)
bpy.app.handlers.save_pre.remove(handler.storeIdMap)
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)
@@ -6,68 +6,71 @@ import numpy
import pickle
import multiprocessing
try:
from OCC.Core import (
gp,
Geom,
Bnd,
BRepBndLib,
BRep,
BRepPrimAPI,
BRepAlgoAPI,
BRepBuilderAPI,
TopOpeBRepTool,
TopOpeBRepBuild,
ShapeExtend,
GProp,
BRepGProp,
GC,
ShapeAnalysis,
TopTools,
TopExp,
TopAbs,
HLRAlgo,
HLRBRep,
TopLoc,
Bnd,
BRepBndLib,
BRepTools,
TopoDS,
GeomLProp,
IntCurvesFace,
)
from OCC.Core.TopoDS import topods
except ImportError:
from OCC import (
gp,
Geom,
Bnd,
BRepBndLib,
BRep,
BRepPrimAPI,
BRepAlgoAPI,
BRepBuilderAPI,
TopOpeBRepTool,
TopOpeBRepBuild,
ShapeExtend,
GProp,
BRepGProp,
GC,
ShapeAnalysis,
TopTools,
TopExp,
TopAbs,
HLRAlgo,
HLRBRep,
TopLoc,
Bnd,
BRepBndLib,
BRepTools,
TopoDS,
GeomLProp,
IntCurvesFace,
)
from OCC.TopoDS import topods
def load_occ():
# Don't import until we really need to, as a temporary step before we can purge OCC
try:
from OCC.Core import (
gp,
Geom,
Bnd,
BRepBndLib,
BRep,
BRepPrimAPI,
BRepAlgoAPI,
BRepBuilderAPI,
TopOpeBRepTool,
TopOpeBRepBuild,
ShapeExtend,
GProp,
BRepGProp,
GC,
ShapeAnalysis,
TopTools,
TopExp,
TopAbs,
HLRAlgo,
HLRBRep,
TopLoc,
Bnd,
BRepBndLib,
BRepTools,
TopoDS,
GeomLProp,
IntCurvesFace,
)
from OCC.Core.TopoDS import topods
except ImportError:
from OCC import (
gp,
Geom,
Bnd,
BRepBndLib,
BRep,
BRepPrimAPI,
BRepAlgoAPI,
BRepBuilderAPI,
TopOpeBRepTool,
TopOpeBRepBuild,
ShapeExtend,
GProp,
BRepGProp,
GC,
ShapeAnalysis,
TopTools,
TopExp,
TopAbs,
HLRAlgo,
HLRBRep,
TopLoc,
Bnd,
BRepBndLib,
BRepTools,
TopoDS,
GeomLProp,
IntCurvesFace,
)
from OCC.TopoDS import topods
import ifcopenshell
import ifcopenshell.geom
@@ -79,6 +82,7 @@ this_file = os.path.join(cwd, "cut_ifc.py")
def get_booleaned_edges(shape):
load_occ()
edges = []
exp = TopExp.TopExp_Explorer(shape, TopAbs.TopAbs_EDGE)
while exp.More():
@@ -88,6 +92,7 @@ def get_booleaned_edges(shape):
def connect_edges_into_wires(unconnected_edges):
load_occ()
edges = TopTools.TopTools_HSequenceOfShape()
edges_handle = TopTools.Handle_TopTools_HSequenceOfShape(edges)
wires = TopTools.TopTools_HSequenceOfShape()
@@ -101,6 +106,7 @@ def connect_edges_into_wires(unconnected_edges):
def do_cut(process_data):
load_occ()
global_id, shape, section, trsf_data = process_data
axis = gp.gp_Ax2(
@@ -320,6 +326,7 @@ class IfcCutter:
return False
def create_section_box(self):
load_occ()
top_left_corner = gp.gp_Pnt(
self.section_box["top_left_corner"][0],
self.section_box["top_left_corner"][1],
@@ -350,6 +357,7 @@ class IfcCutter:
self.transformation.SetDisplacement(source, destination)
def get_bbox(self, shape):
load_occ()
bbox = Bnd.Bnd_Box()
BRepBndLib.brepbndlib_Add(shape, bbox)
return bbox
@@ -361,6 +369,7 @@ class IfcCutter:
return zpos, zmax
def get_booleaned_edges(self, shape):
load_occ()
edges = []
exp = TopExp.TopExp_Explorer(shape, TopAbs.TopAbs_EDGE)
while exp.More():
@@ -377,6 +386,7 @@ class IfcCutter:
def get_annotation(self):
import mathutils
load_occ()
self.annotation_objs = []
settings_2d = ifcopenshell.geom.settings()
Binary file not shown.
@@ -0,0 +1,11 @@
<link href="jsgantt.css" rel="stylesheet" type="text/css"/>
<script src="jsgantt.js" type="text/javascript"></script>
<div style="position:relative" class="gantt" id="GanttChartDIV"></div>
<script type="text/javascript">
var g = new JSGantt.GanttChart(document.getElementById('GanttChartDIV'), 'day');
var json_data = `
{{{json_data}}}
`;
JSGantt.parseJSONString(json_data, g);
g.Draw();
</script>

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 65 KiB

Before

Width:  |  Height:  |  Size: 572 B

After

Width:  |  Height:  |  Size: 572 B

@@ -25,7 +25,7 @@
</rdf:RDF>
</metadata>
<desc
id="desc89610">/home/dion/Projects/IfcOpenShell/src/ifcblenderexport/titleblock.dxf - scale = 1.000000, origin = (0.000000, 0.000000), method = manual</desc>
id="desc89610">/home/dion/Projects/IfcOpenShell/src/blenderbim/titleblock.dxf - scale = 1.000000, origin = (0.000000, 0.000000), method = manual</desc>
<rect
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2.07874;stroke-linecap:round;stroke-linejoin:round;stop-color:#000000"
id="rect1036"

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

@@ -25,7 +25,7 @@
</rdf:RDF>
</metadata>
<desc
id="desc89610">/home/dion/Projects/IfcOpenShell/src/ifcblenderexport/titleblock.dxf - scale = 1.000000, origin = (0.000000, 0.000000), method = manual</desc>
id="desc89610">/home/dion/Projects/IfcOpenShell/src/blenderbim/titleblock.dxf - scale = 1.000000, origin = (0.000000, 0.000000), method = manual</desc>
<rect
y="3.1753578e-05"
x="-4.3300333e-05"

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

@@ -25,7 +25,7 @@
</rdf:RDF>
</metadata>
<desc
id="desc89610">/home/dion/Projects/IfcOpenShell/src/ifcblenderexport/titleblock.dxf - scale = 1.000000, origin = (0.000000, 0.000000), method = manual</desc>
id="desc89610">/home/dion/Projects/IfcOpenShell/src/blenderbim/titleblock.dxf - scale = 1.000000, origin = (0.000000, 0.000000), method = manual</desc>
<rect
y="3.1753578e-05"
x="-4.3300333e-05"

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

@@ -7,6 +7,8 @@ import zipfile
import tempfile
import ifcopenshell
import ifcopenshell.util.placement
import ifcopenshell.api
from ifcopenshell.api.spatial.data import Data as SpatialData
from blenderbim.bim.ifc import IfcStore
import addon_utils
@@ -19,7 +21,7 @@ class IfcExporter:
self.file = IfcStore.get_file()
self.set_header()
if bpy.context.scene.BIMProjectProperties.is_authoring:
self.sync_object_placements()
self.sync_object_placements_and_deletions()
self.sync_edited_objects()
extension = self.ifc_export_settings.output_file.split(".")[-1]
if extension == "ifczip":
@@ -45,9 +47,6 @@ class IfcExporter:
jsonData = ifcjson.IFC2JSON5a(self.file, self.ifc_export_settings.json_compact).spf2Json()
with open(self.ifc_export_settings.output_file, "w") as outfile:
json.dump(jsonData, outfile, indent=None if self.ifc_export_settings.json_compact else 4)
if bpy.context.scene.BIMProjectProperties.is_authoring:
if bpy.data.filepath:
bpy.ops.wm.save_mainfile()
def set_header(self):
# TODO: add all metadata, pending bug #747
@@ -74,13 +73,25 @@ class IfcExporter:
# else:
# self.file.wrapped_data.header.file_name.authorization = "Nobody"
def sync_object_placements(self):
def sync_object_placements_and_deletions(self):
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(self.file)
for obj in bpy.data.objects:
to_delete = []
for ifc_definition_id, obj in IfcStore.id_map.items():
try:
self.sync_object_placement(obj)
except:
pass
self.sync_object_container(ifc_definition_id, obj)
except ReferenceError:
pass # The object is likely deleted
if self.should_delete(obj):
to_delete.append(ifc_definition_id)
SpatialData.purge()
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})
def sync_edited_objects(self):
for obj_name in IfcStore.edited_objs.copy():
@@ -94,9 +105,10 @@ class IfcExporter:
def sync_object_placement(self, obj):
blender_matrix = np.matrix(obj.matrix_world)
ifc_matrix = ifcopenshell.util.placement.get_local_placement(
self.file.by_id(obj.BIMObjectProperties.ifc_definition_id).ObjectPlacement
)
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
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
@@ -114,6 +126,43 @@ class IfcExporter:
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"):
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
return False
except:
return True
def get_application_name(self):
return "BlenderBIM"
@@ -1,23 +1,27 @@
import bpy
import json
import addon_utils
import blenderbim.bim.decoration as decoration
import ifcopenshell.api.owner.settings
from bpy.app.handlers import persistent
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.module.attribute.data import Data as AttributeData
from ifcopenshell.api.attribute.data import Data as AttributeData
from ifcopenshell.api.type.data import Data as TypeData
def mode_callback(obj, data):
if (
obj.mode != "OBJECT"
or not obj.data
or not isinstance(obj.data, bpy.types.Mesh)
or not obj.data.BIMMeshProperties.ifc_definition_id
or not bpy.context.scene.BIMProjectProperties.is_authoring
):
return
representation = IfcStore.get_file().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
if representation.RepresentationType == "Tessellation" or representation.RepresentationType == "Brep":
IfcStore.edited_objs.add(obj.name)
for obj in bpy.context.selected_objects:
if (
obj.mode != "EDIT"
or not obj.data
or not isinstance(obj.data, bpy.types.Mesh)
or not obj.data.BIMMeshProperties.ifc_definition_id
or not bpy.context.scene.BIMProjectProperties.is_authoring
):
return
representation = IfcStore.get_file().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
if representation.RepresentationType == "Tessellation" or representation.RepresentationType == "Brep":
IfcStore.edited_objs.add(obj.name)
def name_callback(obj, data):
@@ -27,8 +31,13 @@ def name_callback(obj, data):
element = IfcStore.get_file().by_id(obj.BIMObjectProperties.ifc_definition_id)
if not element.is_a("IfcRoot"):
return
if element.is_a("IfcSpatialStructureElement") or (hasattr(element, "IsDecomposedBy") and element.IsDecomposedBy):
collection = obj.users_collection[0]
collection.name = obj.name
if element.is_a("IfcTypeProduct"):
TypeData.purge()
element.Name = "/".join(obj.name.split("/")[1:])
AttributeData.load(obj.BIMObjectProperties.ifc_definition_id)
AttributeData.load(IfcStore.get_file(), obj.BIMObjectProperties.ifc_definition_id)
def subscribe_to(object, data_path, callback):
@@ -47,59 +56,122 @@ def subscribe_to(object, data_path, callback):
)
@persistent
def loadIfcStore(scene):
IfcStore.file = None
IfcStore.schema = None
props = bpy.context.scene.BIMProperties
IfcStore.id_map = (
{int(k): bpy.data.objects.get(v) for k, v in json.loads(props.id_map).items()} if props.id_map else {}
)
IfcStore.guid_map = (
{k: bpy.data.objects.get(v) for k, v in json.loads(props.guid_map).items()} if props.id_map else {}
)
# Purge data cache
def purge_module_data():
from blenderbim.bim import modules
for module in modules.values():
if not module:
continue
for name, value in modules.items():
try:
getattr(getattr(module, "data"), "Data").purge()
getattr(getattr(getattr(ifcopenshell.api, name), "data"), "Data").purge()
except AttributeError:
pass
try:
getattr(value, "prop").purge()
except AttributeError:
pass
@persistent
def ensureIfcExported(scene):
if IfcStore.get_file() and not bpy.context.scene.BIMProperties.ifc_file:
# The invocation pops up a file select window.
# This is non-blocking, therefore the Blend file is saved before we export.
bpy.ops.export_ifc.bim("INVOKE_DEFAULT", should_force_resave=True)
def loadIfcStore(scene):
IfcStore.purge()
ifc_file = IfcStore.get_file()
IfcStore.get_schema()
[
IfcStore.link_element(ifc_file.by_id(o.BIMObjectProperties.ifc_definition_id), o)
for o in bpy.data.objects
if o.BIMObjectProperties.ifc_definition_id
]
purge_module_data()
@persistent
def storeIdMap(scene):
try:
bpy.context.scene.BIMProperties.id_map = json.dumps({k: v.name for k, v in IfcStore.id_map.items()})
bpy.context.scene.BIMProperties.guid_map = json.dumps({k: v.name for k, v in IfcStore.guid_map.items()})
except:
# Regenerate maps. Is there a better solution for this? It seems fragile.
file = IfcStore.get_file()
IfcStore.id_map = {
o.ifc_definition_id: o.name for o in bpy.data.objects if o.BIMObjectProperties.ifc_definition_id
}
IfcStore.guid_map = {
file.by_id(i).GlobalId: n for i, n in IfcStore.id_map.items() if file.by_id(i).is_a("IfcRoot")
}
# Then attempt to store it again
bpy.context.scene.BIMProperties.id_map = json.dumps({k: v.name for k, v in IfcStore.id_map.items()})
bpy.context.scene.BIMProperties.guid_map = json.dumps({k: v.name for k, v in IfcStore.guid_map.items()})
def ensureIfcExported(scene):
if IfcStore.get_file() and not bpy.context.scene.BIMProperties.ifc_file:
bpy.ops.export_ifc.bim("INVOKE_DEFAULT")
def get_application(ifc):
version = get_application_version()
for element in ifc.by_type("IfcApplication"):
if element.ApplicationIdentifier == "BlenderBIM" and element.Version == version:
return element
return ifc.create_entity(
"IfcApplication",
**{
"ApplicationDeveloper": create_application_organisation(ifc),
"Version": get_application_version(),
"ApplicationFullName": "BlenderBIM Add-on",
"ApplicationIdentifier": "BlenderBIM",
},
)
def get_application_version():
return ".".join(
[
str(x)
for x in [
addon.bl_info.get("version", (-1, -1, -1))
for addon in addon_utils.modules()
if addon.bl_info["name"] == "BlenderBIM"
][0]
]
)
def create_application_organisation(ifc):
return ifc.create_entity(
"IfcOrganization",
**{
"Name": "IfcOpenShell",
"Description": "IfcOpenShell is an open source (LGPL) software library that helps users and software developers to work with the IFC file format.",
"Roles": [ifc.create_entity("IfcActorRole", **{"Role": "USERDEFINED", "UserDefinedRole": "CONTRIBUTOR"})],
"Addresses": [
ifc.create_entity(
"IfcTelecomAddress",
**{
"Purpose": "USERDEFINED",
"UserDefinedPurpose": "WEBPAGE",
"Description": "The main webpage of the software collection.",
"WWWHomePageURL": "https://ifcopenshell.org",
},
),
ifc.create_entity(
"IfcTelecomAddress",
**{
"Purpose": "USERDEFINED",
"UserDefinedPurpose": "WEBPAGE",
"Description": "The BlenderBIM Add-on webpage of the software collection.",
"WWWHomePageURL": "https://blenderbim.org",
},
),
ifc.create_entity(
"IfcTelecomAddress",
**{
"Purpose": "USERDEFINED",
"UserDefinedPurpose": "REPOSITORY",
"Description": "The source code repository of the software collection.",
"WWWHomePageURL": "https://github.com/IfcOpenShell/IfcOpenShell.git",
},
),
],
},
)
@persistent
def setDefaultProperties(scene):
ifcopenshell.api.owner.settings.get_person = (
lambda ifc: ifc.by_id(int(bpy.context.scene.BIMOwnerProperties.user_person))
if bpy.context.scene.BIMOwnerProperties.user_person
else None
)
ifcopenshell.api.owner.settings.get_organisation = (
lambda ifc: ifc.by_id(int(bpy.context.scene.BIMOwnerProperties.user_organisation))
if bpy.context.scene.BIMOwnerProperties.user_organisation
else None
)
ifcopenshell.api.owner.settings.get_application = get_application
if len(bpy.context.scene.DocProperties.drawing_styles) == 0:
drawing_style = bpy.context.scene.DocProperties.drawing_styles.add()
drawing_style.name = "Technical"
@@ -12,6 +12,21 @@ class IfcStore:
edited_objs = set()
pset_template_path = ""
pset_template_file = None
library_path = ""
library_file = None
@staticmethod
def purge():
IfcStore.path = ""
IfcStore.file = None
IfcStore.schema = None
IfcStore.id_map = {}
IfcStore.guid_map = {}
IfcStore.edited_objs = set()
IfcStore.pset_template_path = ""
IfcStore.pset_template_file = None
IfcStore.library_path = ""
IfcStore.library_file = None
@staticmethod
def get_file():
@@ -21,7 +21,6 @@ import numpy as np
from pathlib import Path
from itertools import cycle
from datetime import datetime
from blenderbim.bim.module.context.data import Data as ContextData
from blenderbim.bim.ifc import IfcStore
from . import schema
@@ -415,12 +414,16 @@ class IfcImporter:
self.exclude_elements |= self.native_elements
def is_native(self, element):
if not element.Representation or not element.Representation.Representations or element.HasOpenings:
if (
not element.Representation
or not element.Representation.Representations
or getattr(element, "HasOpenings", None)
):
return
representations = self.get_transformed_body_representations(element.Representation.Representations)
# Single swept disk solids (e.g. rebar) are better natively represented as beveled curves
if [r for r in representations if self.is_native_swept_disk_solid(r)]:
if self.is_native_swept_disk_solid(representations):
self.native_data[element.GlobalId] = {
"representations": representations,
"representation": self.get_body_representation(element.Representation.Representations),
@@ -429,19 +432,26 @@ class IfcImporter:
return True
# FacetedBreps (without voids) are meshes. See #841.
# Commented out as seems currently too slow.
# if [r for r in representations if self.is_native_faceted_brep(r)]:
# self.native_data[element.GlobalId] = {
# "representations": representations,
# "representation": self.get_body_representation(element.Representation.Representations),
# "type": "IfcFacetedBrep",
# }
# return True
# if self.is_native_faceted_brep(representations):
# self.native_data[element.GlobalId] = {
# "representations": representations,
# "representation": self.get_body_representation(element.Representation.Representations),
# "type": "IfcFacetedBrep",
# }
# return True
def is_native_swept_disk_solid(self, representation):
return len(representation["raw"].Items) == 1 and representation["raw"].Items[0].is_a("IfcSweptDiskSolid")
def is_native_swept_disk_solid(self, representations):
for representation in representations:
if len(representation["raw"].Items) > 1 or not representation["raw"].Items[0].is_a("IfcSweptDiskSolid"):
return False
return True
def is_native_faceted_brep(self, representation):
return bool([i for i in representation["raw"].Items if i.is_a() == "IfcFacetedBrep"])
def is_native_faceted_brep(self, representations):
for representation in representations:
for i in representation["raw"].Items:
if i.is_a() != "IfcFacetedBrep":
return False
return True
def get_products_from_shape_representation(self, element):
products = [pr.ShapeOfProduct[0] for pr in element.OfProductRepresentation]
@@ -634,6 +644,7 @@ class IfcImporter:
def create_native_products(self):
total = 0
checkpoint = time.time()
bm = bmesh.new()
for element in self.native_elements:
total += 1
if total % 250 == 0:
@@ -641,6 +652,8 @@ class IfcImporter:
checkpoint = time.time()
native_data = self.native_data[element.GlobalId]
representation = native_data["representation"]
if not representation:
continue
context_id = representation.ContextOfItems.id() if hasattr(representation, "ContextOfItems") else 0
mesh_name = f"{context_id}/{representation.id()}"
mesh = self.meshes.get(mesh_name)
@@ -650,16 +663,17 @@ class IfcImporter:
elif native_data["type"] == "IfcFacetedBrep":
mesh = self.create_native_faceted_brep(element, mesh_name)
mesh.BIMMeshProperties.ifc_definition_id = representation.id()
mesh.name = mesh_name
self.meshes[mesh_name] = mesh
self.create_product(element, mesh=mesh)
if native_data["type"] == "IfcFacetedBrep":
# The current implementation doesn't reuse vertices, so we weld it after assigning materials.
# This welding isn't true to the representation, but is easy and seems inexpensive.
bm = bmesh.new()
bm.from_mesh(mesh)
bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.001)
bm.to_mesh(mesh)
bm.free()
bm.clear()
bm.free()
print("Done creating geometry")
@@ -715,21 +729,27 @@ class IfcImporter:
def create_structural_point_connections(self):
for product in self.file.by_type("IfcStructuralPointConnection"):
# TODO: make this based off ifcopenshell. See #1409
placement_matrix = ifcopenshell.util.placement.get_local_placement(product.ObjectPlacement)
vertex = None
for subelement in self.file.traverse(product):
context = None
representation = None
for subelement in self.file.traverse(product.Representation):
if subelement.is_a("IfcVertex") and subelement.VertexGeometry.is_a("IfcCartesianPoint"):
vertex = list(subelement.VertexGeometry.Coordinates)
break
if not vertex:
elif subelement.is_a("IfcGeometricRepresentationContext"):
context = subelement
elif subelement.is_a("IfcTopologyRepresentation"):
representation = subelement
if not vertex or not context or not representation:
continue # TODO implement non cartesian point vertexes
placement_matrix[0, 3] += vertex[0] * self.unit_scale
placement_matrix[1, 3] += vertex[1] * self.unit_scale
placement_matrix[2, 3] += vertex[2] * self.unit_scale
obj = bpy.data.objects.new("{}/{}".format(product.is_a(), product.Name), None)
mesh_name = f"{context.id()}/{representation.id()}"
mesh = bpy.data.meshes.new(mesh_name)
mesh.from_pydata([mathutils.Vector(vertex) * self.unit_scale], [], [])
obj = bpy.data.objects.new("{}/{}".format(product.is_a(), product.Name), mesh)
obj.matrix_world = mathutils.Matrix(placement_matrix.tolist())
obj.empty_display_type = "SPHERE"
obj.empty_display_size = 0.1
self.link_element(product, obj)
def create_curve_products(self, products):
@@ -760,9 +780,6 @@ class IfcImporter:
if element is None:
return
if not self.ifc_import_settings.should_import_spaces and element.is_a("IfcSpace"):
return
self.ifc_import_settings.logger.info("Creating object %s", element)
if mesh:
@@ -843,7 +860,7 @@ class IfcImporter:
points = outer_bound[0].copy()
[points.extend(p) for p in inner_bounds]
tessellated_polygons = mathutils.geometry.tessellate_polygon(outer_bound + inner_bounds)
tessellated_faces = [[{"Coordinates": points[pi]} for pi in t] for t in tessellated_polygons]
tessellated_faces = [({"Coordinates": points[pi]} for pi in t) for t in tessellated_polygons]
else:
tessellated_faces = [face["Bounds"][0]["Bound"]["Polygon"]]
@@ -855,7 +872,7 @@ class IfcImporter:
for point in tessellated_face:
co.extend(
representation["matrix"]
@ mathutils.Vector([c * self.unit_scale for c in point["Coordinates"]])
@ mathutils.Vector((c * self.unit_scale for c in point["Coordinates"]))
)
total_verts += 1
loop_count += 1
@@ -983,6 +1000,7 @@ class IfcImporter:
bpy.ops.mesh.tris_convert_to_quads(context_override)
bpy.ops.mesh.normals_make_consistent(context_override)
bpy.ops.object.editmode_toggle(context_override)
IfcStore.edited_objs.clear()
def add_opening_relation(self, element, obj):
if not element.is_a("IfcOpeningElement"):
@@ -1064,8 +1082,6 @@ class IfcImporter:
def add_related_objects(self, parent, related_objects):
for element in related_objects:
if element.is_a("IfcSpace"):
continue
global_id = element.GlobalId
collection = bpy.data.collections.new(self.get_name(element))
self.spatial_structure_elements[global_id] = {"blender": collection}
@@ -1109,8 +1125,6 @@ class IfcImporter:
container = element.ContainedInStructure[0].RelatingStructure
elif hasattr(element, "Decomposes") and element.Decomposes:
container = element.Decomposes[0].RelatingObject
if container.is_a("IfcSpace"):
return self.get_aggregate_container(container)
return container
def create_openings_collection(self):
@@ -1159,9 +1173,7 @@ class IfcImporter:
and element.ContainedInStructure[0].RelatingStructure
):
container = element.ContainedInStructure[0].RelatingStructure
if container.is_a("IfcSpace"):
return self.place_object_in_spatial_tree(container, obj)
elif element.is_a("IfcGrid"):
if element.is_a("IfcGrid"):
grid_collection = bpy.data.collections.get(obj.name)
if grid_collection: # Just in case we ran into invalid grids from Revit
self.spatial_structure_elements[container.GlobalId]["blender"].children.link(grid_collection)
@@ -1173,22 +1185,15 @@ class IfcImporter:
if element.Decomposes[0].RelatingObject.is_a("IfcProject"):
collection = self.project["blender"]
elif element.Decomposes[0].RelatingObject.is_a("IfcSpatialStructureElement"):
if element.is_a("IfcSpatialStructureElement") and not element.is_a("IfcSpace"):
if element.is_a("IfcSpatialStructureElement"):
global_id = element.GlobalId
else:
global_id = element.Decomposes[0].RelatingObject.GlobalId
if global_id in self.spatial_structure_elements:
if (
element.is_a("IfcSpatialStructureElement")
and not element.is_a("IfcSpace")
and "blender_obj" in self.spatial_structure_elements[global_id]
):
bpy.data.objects.remove(self.spatial_structure_elements[global_id]["blender_obj"])
collection = self.spatial_structure_elements[global_id]["blender"]
# This may occur if we are nesting an IfcSpace (which is special
# since it does not have a collection within an IfcSpace
if not collection:
return self.place_object_in_spatial_tree(element.Decomposes[0].RelatingObject, obj)
else:
collection = self.aggregates[element.Decomposes[0].RelatingObject.GlobalId]["blender"]
if collection:
@@ -1412,7 +1417,6 @@ class IfcImportSettings:
self.logger = None
self.input_file = None
self.diff_file = None
self.should_import_spaces = False
self.should_auto_set_workarounds = True
self.should_use_cpu_multiprocessing = True
self.should_merge_by_class = False
@@ -1,7 +1,7 @@
import bpy
import blenderbim.bim.module.aggregate.assign_object as assign_object
import ifcopenshell.api
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.module.aggregate.data import Data
from ifcopenshell.api.aggregate.data import Data
class AssignObject(bpy.types.Operator):
@@ -12,36 +12,41 @@ class AssignObject(bpy.types.Operator):
def execute(self, context):
self.file = IfcStore.get_file()
related_object = bpy.data.objects.get(self.related_object) if self.related_object else bpy.context.active_object
props = related_object.BIMObjectProperties
relating_object = bpy.data.objects.get(self.relating_object) if self.relating_object else props.relating_object
related_objects = (
[bpy.data.objects.get(self.related_object)] if self.related_object else bpy.context.selected_objects
)
relating_object = bpy.data.objects.get(self.relating_object)
if not relating_object or not relating_object.BIMObjectProperties.ifc_definition_id:
return {"FINISHED"}
product = self.file.by_id(props.ifc_definition_id)
assign_object.Usecase(
self.file,
{
"product": product,
"relating_object": self.file.by_id(relating_object.BIMObjectProperties.ifc_definition_id),
},
).execute()
bpy.ops.bim.edit_object_placement(obj=related_object.name)
Data.load(props.ifc_definition_id)
bpy.ops.bim.disable_editing_aggregate(obj=related_object.name)
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(bpy.context.scene.collection, spatial_collection)
for collection in bpy.data.collections:
if collection == relating_collection:
collection.children.link(spatial_collection)
continue
self.remove_collection(collection, spatial_collection)
else:
for collection in related_object.users_collection:
collection.objects.unlink(related_object)
relating_collection.objects.link(related_object)
spatial_collection = bpy.data.collections.get(related_object.name)
relating_collection = bpy.data.collections.get(relating_object.name)
if spatial_collection:
self.remove_collection(bpy.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:
collection.objects.unlink(related_object)
relating_collection.objects.link(related_object)
return {"FINISHED"}
def remove_collection(self, parent, child):
@@ -1,5 +1,5 @@
from bpy.types import Panel
from blenderbim.bim.module.aggregate.data import Data
from ifcopenshell.api.aggregate.data import Data
from blenderbim.bim.ifc import IfcStore
@@ -19,7 +19,7 @@ class BIM_PT_aggregate(Panel):
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(props.ifc_definition_id)
Data.load(IfcStore.get_file(), props.ifc_definition_id)
if not Data.products[props.ifc_definition_id]:
return False
return True
@@ -29,12 +29,13 @@ class BIM_PT_aggregate(Panel):
if not props.ifc_definition_id:
return
if props.ifc_definition_id not in Data.products:
Data.load(props.ifc_definition_id)
Data.load(IfcStore.get_file(), props.ifc_definition_id)
if props.is_editing_aggregate:
row = self.layout.row(align=True)
row.prop(props, "relating_object", text="")
row.operator("bim.assign_object", icon="CHECKMARK", text="")
if props.relating_object:
row.operator("bim.assign_object", icon="CHECKMARK", text="").relating_object = props.relating_object.name
row.operator("bim.disable_editing_aggregate", icon="X", text="")
else:
row = self.layout.row(align=True)
@@ -1,9 +1,9 @@
import bpy
import json
import ifcopenshell
import blenderbim.bim.module.attribute.edit_attributes as edit_attributes
import ifcopenshell.api
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.module.attribute.data import Data
from ifcopenshell.api.attribute.data import Data
class EnableEditingAttributes(bpy.types.Operator):
@@ -33,7 +33,7 @@ class EnableEditingAttributes(bpy.types.Operator):
elif attribute["type"] == "integer":
new.int_value = attribute["value"] or 0
elif attribute["type"] == "float":
new.float_value = attribute["value"] or 0.
new.float_value = attribute["value"] or 0.0
elif attribute["type"] == "enum":
new.enum_items = json.dumps(attribute["enum_items"])
if attribute["value"]:
@@ -95,17 +95,16 @@ class EditAttributes(bpy.types.Operator):
elif attribute["type"] == "enum":
attributes[attribute["name"]] = blender_attribute.enum_value
product = self.file.by_id(oprops.ifc_definition_id)
edit_attributes.Usecase(self.file, {
"product": product,
"attributes": attributes
}).execute()
ifcopenshell.api.run(
"attribute.edit_attributes", self.file, **{"product": product, "attributes": attributes}
)
if "Name" in attributes:
new_name = "{}/{}".format(product.is_a(), product.Name or "Unnamed")
collection = bpy.data.collections.get(obj.name)
if collection:
collection.name = new_name
obj.name = new_name
Data.load(oprops.ifc_definition_id)
Data.load(IfcStore.get_file(), oprops.ifc_definition_id)
bpy.ops.bim.disable_editing_attributes(obj=obj.name, obj_type=self.obj_type)
return {"FINISHED"}
@@ -1,6 +1,5 @@
import bpy
import blenderbim.bim.schema # refactor
from blenderbim.bim.module.material.data import Data
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.prop import StrProperty, Attribute
from bpy.types import PropertyGroup
@@ -1,5 +1,6 @@
from bpy.types import Panel
from blenderbim.bim.module.attribute.data import Data
from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.attribute.data import Data
def draw_ui(context, layout, obj_type):
@@ -7,7 +8,7 @@ def draw_ui(context, layout, obj_type):
oprops = obj.BIMObjectProperties
props = obj.BIMAttributeProperties
if oprops.ifc_definition_id not in Data.products:
Data.load(oprops.ifc_definition_id)
Data.load(IfcStore.get_file(), oprops.ifc_definition_id)
if props.is_editing_attributes:
row = layout.row(align=True)
@@ -17,6 +17,11 @@ from bpy.props import (
bcfviewpoints_enum = None
def purge():
global bcfviewpoints_enum
bcfviewpoints_enum = None
def updateBcfReferenceLink(self, context):
if bpy.context.scene.BCFProperties.is_loaded:
bpy.ops.bim.edit_bcf_reference_links()
@@ -18,6 +18,13 @@ scenarios_enum = []
classes_enum = []
def purge():
global scenarios_enum
global classes_enum
scenarios_enum = []
classes_enum = []
def getScenarios(self, context):
global scenarios_enum
if len(scenarios_enum) < 1:
@@ -1,6 +1,5 @@
import bpy
from blenderbim.bim.prop import StrProperty, Attribute
from blenderbim.bim.module.owner.data import Data
from bpy.types import PropertyGroup
from bpy.props import (
PointerProperty,
@@ -1,13 +1,8 @@
import bpy
import json
import blenderbim.bim.module.classification.add_classification as add_classification
import blenderbim.bim.module.classification.remove_classification as remove_classification
import blenderbim.bim.module.classification.edit_classification as edit_classification
import blenderbim.bim.module.classification.add_reference as add_reference
import blenderbim.bim.module.classification.remove_reference as remove_reference
import blenderbim.bim.module.classification.edit_reference as edit_reference
import ifcopenshell.api
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.module.classification.data import Data
from ifcopenshell.api.classification.data import Data
from blenderbim.bim.module.classification.prop import getClassifications, getReferences
@@ -34,10 +29,12 @@ class AddClassification(bpy.types.Operator):
def execute(self, context):
props = context.scene.BIMClassificationProperties
add_classification.Usecase(
IfcStore.get_file(), {"classification": Data.library_file.by_id(int(props.available_classifications))}
).execute()
Data.load()
ifcopenshell.api.run(
"classification.add_classification",
IfcStore.get_file(),
**{"classification": Data.library_file.by_id(int(props.available_classifications))},
)
Data.load(IfcStore.get_file())
return {"FINISHED"}
@@ -80,8 +77,12 @@ class RemoveClassification(bpy.types.Operator):
def execute(self, context):
self.file = IfcStore.get_file()
remove_classification.Usecase(self.file, {"classification": self.file.by_id(self.classification)}).execute()
Data.load()
ifcopenshell.api.run(
"classification.remove_classification",
self.file,
**{"classification": self.file.by_id(self.classification)},
)
Data.load(IfcStore.get_file())
return {"FINISHED"}
@@ -100,10 +101,12 @@ class EditClassification(bpy.types.Operator):
else:
attributes[attribute.name] = attribute.string_value
self.file = IfcStore.get_file()
edit_classification.Usecase(
self.file, {"classification": self.file.by_id(props.active_classification_id), "attributes": attributes}
).execute()
Data.load()
ifcopenshell.api.run(
"classification.edit_classification",
self.file,
**{"classification": self.file.by_id(props.active_classification_id), "attributes": attributes},
)
Data.load(IfcStore.get_file())
bpy.ops.bim.disable_editing_classification()
return {"FINISHED"}
@@ -152,15 +155,16 @@ class RemoveClassificationReference(bpy.types.Operator):
def execute(self, context):
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
self.file = IfcStore.get_file()
remove_reference.Usecase(
ifcopenshell.api.run(
"classification.remove_reference",
self.file,
{
**{
"reference": self.file.by_id(self.reference),
"product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id),
},
).execute()
Data.load(obj.BIMObjectProperties.ifc_definition_id)
Data.load()
)
Data.load(IfcStore.get_file(), obj.BIMObjectProperties.ifc_definition_id)
Data.load(IfcStore.get_file())
return {"FINISHED"}
@@ -179,10 +183,12 @@ class EditClassificationReference(bpy.types.Operator):
else:
attributes[attribute.name] = attribute.string_value
self.file = IfcStore.get_file()
edit_reference.Usecase(
self.file, {"reference": self.file.by_id(props.active_reference_id), "attributes": attributes}
).execute()
Data.load()
ifcopenshell.api.run(
"classification.edit_reference",
self.file,
**{"reference": self.file.by_id(props.active_reference_id), "attributes": attributes},
)
Data.load(IfcStore.get_file())
bpy.ops.bim.disable_editing_classification_reference()
return {"FINISHED"}
@@ -206,16 +212,17 @@ class AddClassificationReference(bpy.types.Operator):
classification = self.file.by_id(classification_id)
break
add_reference.Usecase(
ifcopenshell.api.run(
"classification.add_reference",
self.file,
{
**{
"reference": Data.library_file.by_id(self.reference),
"product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id),
"classification": classification
"classification": classification,
},
).execute()
Data.load(obj.BIMObjectProperties.ifc_definition_id)
Data.load()
)
Data.load(IfcStore.get_file(), obj.BIMObjectProperties.ifc_definition_id)
Data.load(IfcStore.get_file())
return {"FINISHED"}
@@ -1,6 +1,6 @@
import bpy
from blenderbim.bim.prop import StrProperty, Attribute
from blenderbim.bim.module.classification.data import Data
from ifcopenshell.api.classification.data import Data
from bpy.types import PropertyGroup
from bpy.props import (
PointerProperty,
@@ -16,6 +16,11 @@ from bpy.props import (
classification_enum = []
def purge():
global classification_enum
classification_enum = []
def getClassifications(self, context):
global classification_enum
if len(classification_enum) < 1:
@@ -1,6 +1,6 @@
from bpy.types import Panel, UIList
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.module.classification.data import Data
from ifcopenshell.api.classification.data import Data
class BIM_PT_classifications(Panel):
@@ -17,7 +17,7 @@ class BIM_PT_classifications(Panel):
def draw(self, context):
if not Data.is_loaded:
Data.load()
Data.load(IfcStore.get_file())
self.props = context.scene.BIMClassificationProperties
@@ -79,9 +79,9 @@ class BIM_PT_classification_references(Panel):
self.props = obj.BIMClassificationReferenceProperties
self.file = IfcStore.get_file()
if not Data.is_loaded:
Data.load()
Data.load(IfcStore.get_file())
if self.oprops.ifc_definition_id not in Data.products:
Data.load(self.oprops.ifc_definition_id)
Data.load(IfcStore.get_file(), self.oprops.ifc_definition_id)
self.draw_add_ui()
@@ -1,12 +1,9 @@
import bpy
import json
import blenderbim.bim.module.constraint.add_objective as add_objective
import blenderbim.bim.module.constraint.edit_objective as edit_objective
import blenderbim.bim.module.constraint.remove_constraint as remove_constraint
import blenderbim.bim.module.constraint.assign_constraint as assign_constraint
import blenderbim.bim.module.constraint.unassign_constraint as unassign_constraint
import ifcopenshell.api
import ifcopenshell.util.attribute
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.module.constraint.data import Data
from ifcopenshell.api.constraint.data import Data
class LoadObjectives(bpy.types.Operator):
@@ -50,19 +47,18 @@ class EnableEditingConstraint(bpy.types.Operator):
data = Data.objectives[self.constraint]
for attribute in IfcStore.get_schema().declaration_by_name(props.is_editing).all_attributes():
data_type = str(attribute.type_of_attribute)
if "<entity" in data_type:
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
if data_type == "entity":
continue
new = props.constraint_attributes.add()
new.name = attribute.name()
new.is_null = data[attribute.name()] is None
new.is_optional = attribute.optional()
if "<string>" in data_type:
new.data_type = data_type
if data_type == "string":
new.string_value = "" if new.is_null else data[attribute.name()]
new.data_type = "string"
elif "<enumeration" in data_type:
new.enum_items = json.dumps(attribute.type_of_attribute().declared_type().enumeration_items())
new.data_type = "enum"
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_constraint_id = self.constraint
@@ -83,8 +79,8 @@ class AddObjective(bpy.types.Operator):
bl_label = "Add Objective"
def execute(self, context):
result = add_objective.Usecase(IfcStore.get_file()).execute()
Data.load()
result = ifcopenshell.api.run("constraint.add_objective", IfcStore.get_file())
Data.load(IfcStore.get_file())
bpy.ops.bim.load_objectives()
bpy.ops.bim.enable_editing_constraint(constraint=result.id())
return {"FINISHED"}
@@ -105,10 +101,12 @@ class EditObjective(bpy.types.Operator):
else:
attributes[attribute.name] = attribute.string_value
self.file = IfcStore.get_file()
edit_objective.Usecase(
self.file, {"objective": self.file.by_id(props.active_constraint_id), "attributes": attributes}
).execute()
Data.load()
ifcopenshell.api.run(
"constraint.edit_objective",
self.file,
**{"objective": self.file.by_id(props.active_constraint_id), "attributes": attributes}
)
Data.load(IfcStore.get_file())
bpy.ops.bim.load_objectives()
return {"FINISHED"}
@@ -121,8 +119,10 @@ class RemoveConstraint(bpy.types.Operator):
def execute(self, context):
props = context.scene.BIMConstraintProperties
self.file = IfcStore.get_file()
remove_constraint.Usecase(self.file, {"constraint": self.file.by_id(self.constraint)}).execute()
Data.load()
ifcopenshell.api.run(
"constraint.remove_constraint", self.file, **{"constraint": self.file.by_id(self.constraint)}
)
Data.load(IfcStore.get_file())
if props.is_editing == "IfcObjective":
bpy.ops.bim.load_objectives()
return {"FINISHED"}
@@ -163,11 +163,15 @@ class AssignConstraint(bpy.types.Operator):
def execute(self, context):
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
self.file = IfcStore.get_file()
assign_constraint.Usecase(self.file, {
"product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id),
"constraint": self.file.by_id(self.constraint)
}).execute()
Data.load(obj.BIMObjectProperties.ifc_definition_id)
ifcopenshell.api.run(
"constraint.assign_constraint",
self.file,
**{
"product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id),
"constraint": self.file.by_id(self.constraint),
}
)
Data.load(IfcStore.get_file(), obj.BIMObjectProperties.ifc_definition_id)
return {"FINISHED"}
@@ -180,9 +184,13 @@ class UnassignConstraint(bpy.types.Operator):
def execute(self, context):
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
self.file = IfcStore.get_file()
unassign_constraint.Usecase(self.file, {
"product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id),
"constraint": self.file.by_id(self.constraint)
}).execute()
Data.load(obj.BIMObjectProperties.ifc_definition_id)
ifcopenshell.api.run(
"constraint.unassign_constraint",
self.file,
**{
"product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id),
"constraint": self.file.by_id(self.constraint),
}
)
Data.load(IfcStore.get_file(), obj.BIMObjectProperties.ifc_definition_id)
return {"FINISHED"}
@@ -1,6 +1,6 @@
from bpy.types import Panel, UIList
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.module.constraint.data import Data
from ifcopenshell.api.constraint.data import Data
class BIM_PT_constraints(Panel):
@@ -17,7 +17,7 @@ class BIM_PT_constraints(Panel):
def draw(self, context):
if not Data.is_loaded:
Data.load()
Data.load(IfcStore.get_file())
self.props = context.scene.BIMConstraintProperties
@@ -73,9 +73,9 @@ class BIM_PT_object_constraints(Panel):
self.props = obj.BIMObjectConstraintProperties
self.file = IfcStore.get_file()
if not Data.is_loaded:
Data.load()
Data.load(IfcStore.get_file())
if self.oprops.ifc_definition_id not in Data.products:
Data.load(self.oprops.ifc_definition_id)
Data.load(IfcStore.get_file(), self.oprops.ifc_definition_id)
self.draw_add_ui()
@@ -1,8 +1,7 @@
import bpy
import blenderbim.bim.module.context.add_context as add_context
import blenderbim.bim.module.context.remove_context as remove_context
import ifcopenshell.api
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.module.context.data import Data
from ifcopenshell.api.context.data import Data
class AddSubcontext(bpy.types.Operator):
@@ -14,15 +13,16 @@ class AddSubcontext(bpy.types.Operator):
def execute(self, context):
self.file = IfcStore.get_file()
add_context.Usecase(
ifcopenshell.api.run(
"context.add_context",
self.file,
{
**{
"context": self.context or bpy.context.scene.BIMProperties.available_contexts,
"subcontext": self.subcontext or bpy.context.scene.BIMProperties.available_subcontexts,
"target_view": self.target_view or bpy.context.scene.BIMProperties.available_target_views,
},
).execute()
Data.load()
)
Data.load(IfcStore.get_file())
return {"FINISHED"}
@@ -33,7 +33,8 @@ class RemoveSubcontext(bpy.types.Operator):
def execute(self, context):
self.file = IfcStore.get_file()
usecase = remove_context.Usecase(self.file, {"context": self.file.by_id(self.ifc_definition_id)})
usecase.execute()
Data.load()
ifcopenshell.api.run(
"context.remove_context", self.file, **{"context": self.file.by_id(self.ifc_definition_id)}
)
Data.load(IfcStore.get_file())
return {"FINISHED"}
@@ -1,5 +1,5 @@
from bpy.types import Panel
from blenderbim.bim.module.context.data import Data
from ifcopenshell.api.context.data import Data
from blenderbim.bim.ifc import IfcStore
@@ -17,7 +17,7 @@ class BIM_PT_context(Panel):
def draw(self, context):
if not Data.is_loaded:
Data.load()
Data.load(IfcStore.get_file())
props = context.scene.BIMProperties
@@ -0,0 +1,45 @@
import bpy
from . import ui, prop, operator
classes = (
operator.AddCostSchedule,
operator.RemoveCostSchedule,
operator.EditCostSchedule,
operator.EditCostItem,
operator.EditCostItemQuantity,
operator.EditCostItemValue,
operator.EnableEditingCostSchedule,
operator.EnableEditingCostItems,
operator.EnableEditingCostItem,
operator.EnableEditingCostItemQuantities,
operator.EnableEditingCostItemQuantity,
operator.EnableEditingCostItemValues,
operator.EnableEditingCostItemValue,
operator.DisableEditingCostItem,
operator.DisableEditingCostSchedule,
operator.DisableEditingCostItemQuantity,
operator.DisableEditingCostItemValue,
operator.AddCostItem,
operator.AddSummaryCostItem,
operator.ExpandCostItem,
operator.ContractCostItem,
operator.RemoveCostItem,
operator.AssignControl,
operator.UnassignControl,
operator.AddCostItemQuantity,
operator.RemoveCostItemQuantity,
operator.AddCostItemValue,
operator.RemoveCostItemValue,
prop.CostItem,
prop.BIMCostProperties,
ui.BIM_PT_cost_schedules,
ui.BIM_UL_cost_items,
)
def register():
bpy.types.Scene.BIMCostProperties = bpy.props.PointerProperty(type=prop.BIMCostProperties)
def unregister():
del bpy.types.Scene.BIMCostProperties
@@ -0,0 +1,608 @@
import os
import bpy
import json
import ifcopenshell.api
from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.cost.data import Data
class AddCostSchedule(bpy.types.Operator):
bl_idname = "bim.add_cost_schedule"
bl_label = "Add Cost Schedule"
def execute(self, context):
ifcopenshell.api.run("cost.add_cost_schedule", IfcStore.get_file())
Data.load(IfcStore.get_file())
return {"FINISHED"}
class EditCostSchedule(bpy.types.Operator):
bl_idname = "bim.edit_cost_schedule"
bl_label = "Edit Cost Schedule"
def execute(self, context):
props = context.scene.BIMCostProperties
attributes = {}
for attribute in props.cost_schedule_attributes:
if attribute.is_null:
attributes[attribute.name] = None
else:
if attribute.data_type == "string":
attributes[attribute.name] = attribute.string_value
elif attribute.data_type == "enum":
attributes[attribute.name] = attribute.enum_value
self.file = IfcStore.get_file()
ifcopenshell.api.run(
"cost.edit_cost_schedule",
self.file,
**{"cost_schedule": self.file.by_id(props.active_cost_schedule_id), "attributes": attributes},
)
Data.load(IfcStore.get_file())
bpy.ops.bim.disable_editing_cost_schedule()
return {"FINISHED"}
class RemoveCostSchedule(bpy.types.Operator):
bl_idname = "bim.remove_cost_schedule"
bl_label = "Remove Cost Schedule"
cost_schedule: bpy.props.IntProperty()
def execute(self, context):
ifcopenshell.api.run(
"cost.remove_cost_schedule",
IfcStore.get_file(),
cost_schedule=IfcStore.get_file().by_id(self.cost_schedule),
)
Data.load(IfcStore.get_file())
return {"FINISHED"}
class EnableEditingCostSchedule(bpy.types.Operator):
bl_idname = "bim.enable_editing_cost_schedule"
bl_label = "Enable Editing Cost Schedule"
cost_schedule: bpy.props.IntProperty()
def execute(self, context):
self.props = context.scene.BIMCostProperties
self.props.active_cost_schedule_id = self.cost_schedule
while len(self.props.cost_schedule_attributes) > 0:
self.props.cost_schedule_attributes.remove(0)
self.enable_editing_cost_schedule()
self.props.is_editing = "COST_SCHEDULE"
return {"FINISHED"}
def enable_editing_cost_schedule(self):
data = Data.cost_schedules[self.cost_schedule]
for attribute in IfcStore.get_schema().declaration_by_name("IfcCostSchedule").all_attributes():
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
if data_type == "entity":
continue
new = self.props.cost_schedule_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 attribute.name() in ["SubmittedOn", "UpdateDate"]:
new.string_value = "" if new.is_null else data[attribute.name()].isoformat()
elif 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()]
class EnableEditingCostItems(bpy.types.Operator):
bl_idname = "bim.enable_editing_cost_items"
bl_label = "Enable Editing Cost Items"
cost_schedule: bpy.props.IntProperty()
def execute(self, context):
if context.preferences.addons["blenderbim"].preferences.should_play_chaching_sound:
# lol
# TODO: make pitch higher as costs rise
try:
import aud
device = aud.Device()
# chaching.mp3 is by Lucish_ CC-BY-3.0 https://freesound.org/people/Lucish_/sounds/554841/
sound = aud.Sound(os.path.join(context.scene.BIMProperties.data_dir, "chaching.mp3"))
handle = device.play(sound)
sound_buffered = aud.Sound.buffer(sound)
handle_buffered = device.play(sound_buffered)
handle.stop()
handle_buffered.stop()
except:
pass # ah well
self.props = context.scene.BIMCostProperties
self.props.active_cost_schedule_id = self.cost_schedule
while len(self.props.cost_items) > 0:
self.props.cost_items.remove(0)
self.contracted_cost_items = json.loads(self.props.contracted_cost_items)
for related_object_id in Data.cost_schedules[self.cost_schedule]["Controls"]:
self.create_new_cost_item_li(related_object_id, 0)
self.props.is_editing = "COST_ITEMS"
return {"FINISHED"}
def create_new_cost_item_li(self, related_object_id, level_index):
cost_item = Data.cost_items[related_object_id]
new = self.props.cost_items.add()
new.ifc_definition_id = related_object_id
new.name = cost_item["Name"] or "Unnamed"
new.is_expanded = related_object_id not in self.contracted_cost_items
new.level_index = level_index
if cost_item["IsNestedBy"]:
new.has_children = True
if new.is_expanded:
for related_object_id in cost_item["IsNestedBy"]:
self.create_new_cost_item_li(related_object_id, level_index + 1)
return {"FINISHED"}
class DisableEditingCostSchedule(bpy.types.Operator):
bl_idname = "bim.disable_editing_cost_schedule"
bl_label = "Disable Editing Cost Schedule"
def execute(self, context):
context.scene.BIMCostProperties.active_cost_schedule_id = 0
return {"FINISHED"}
class AddSummaryCostItem(bpy.types.Operator):
bl_idname = "bim.add_summary_cost_item"
bl_label = "Add Cost Item"
cost_schedule: bpy.props.IntProperty()
def execute(self, context):
props = context.scene.BIMCostProperties
self.file = IfcStore.get_file()
ifcopenshell.api.run("cost.add_cost_item", self.file, **{"cost_schedule": self.file.by_id(self.cost_schedule)})
Data.load(self.file)
bpy.ops.bim.enable_editing_cost_items(cost_schedule=self.cost_schedule)
return {"FINISHED"}
class AddCostItem(bpy.types.Operator):
bl_idname = "bim.add_cost_item"
bl_label = "Add Cost Item"
cost_item: bpy.props.IntProperty()
def execute(self, context):
props = context.scene.BIMCostProperties
self.file = IfcStore.get_file()
ifcopenshell.api.run("cost.add_cost_item", self.file, **{"cost_item": self.file.by_id(self.cost_item)})
Data.load(self.file)
bpy.ops.bim.enable_editing_cost_items(cost_schedule=props.active_cost_schedule_id)
return {"FINISHED"}
class ExpandCostItem(bpy.types.Operator):
bl_idname = "bim.expand_cost_item"
bl_label = "Expand Cost Item"
cost_item: bpy.props.IntProperty()
def execute(self, context):
props = context.scene.BIMCostProperties
self.file = IfcStore.get_file()
contracted_cost_items = json.loads(props.contracted_cost_items)
contracted_cost_items.remove(self.cost_item)
props.contracted_cost_items = json.dumps(contracted_cost_items)
bpy.ops.bim.enable_editing_cost_items(cost_schedule=props.active_cost_schedule_id)
return {"FINISHED"}
class ContractCostItem(bpy.types.Operator):
bl_idname = "bim.contract_cost_item"
bl_label = "Contract Cost Item"
cost_item: bpy.props.IntProperty()
def execute(self, context):
props = context.scene.BIMCostProperties
self.file = IfcStore.get_file()
contracted_cost_items = json.loads(props.contracted_cost_items)
contracted_cost_items.append(self.cost_item)
props.contracted_cost_items = json.dumps(contracted_cost_items)
bpy.ops.bim.enable_editing_cost_items(cost_schedule=props.active_cost_schedule_id)
return {"FINISHED"}
class RemoveCostItem(bpy.types.Operator):
bl_idname = "bim.remove_cost_item"
bl_label = "Remove Cost item"
cost_item: bpy.props.IntProperty()
def execute(self, context):
props = context.scene.BIMCostProperties
self.file = IfcStore.get_file()
ifcopenshell.api.run(
"cost.remove_cost_item",
self.file,
cost_item=self.file.by_id(self.cost_item),
)
contracted_cost_items = json.loads(props.contracted_cost_items)
if props.active_cost_item_index in contracted_cost_items:
contracted_cost_items.remove(props.active_cost_item_index)
props.contracted_cost_items = json.dumps(contracted_cost_items)
Data.load(self.file)
bpy.ops.bim.enable_editing_cost_items(cost_schedule=props.active_cost_schedule_id)
return {"FINISHED"}
class EnableEditingCostItem(bpy.types.Operator):
bl_idname = "bim.enable_editing_cost_item"
bl_label = "Enable Editing Cost Item"
cost_item: bpy.props.IntProperty()
def execute(self, context):
props = context.scene.BIMCostProperties
while len(props.cost_item_attributes) > 0:
props.cost_item_attributes.remove(0)
data = Data.cost_items[self.cost_item]
for attribute in IfcStore.get_schema().declaration_by_name("IfcCostItem").all_attributes():
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
if data_type == "entity" or isinstance(data_type, tuple):
continue
new = props.cost_item_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_cost_item_id = self.cost_item
props.cost_item_editing_type = "ATTRIBUTES"
return {"FINISHED"}
class DisableEditingCostItem(bpy.types.Operator):
bl_idname = "bim.disable_editing_cost_item"
bl_label = "Disable Editing Cost Item"
def execute(self, context):
context.scene.BIMCostProperties.active_cost_item_id = 0
return {"FINISHED"}
class EditCostItem(bpy.types.Operator):
bl_idname = "bim.edit_cost_item"
bl_label = "Edit Cost Item"
def execute(self, context):
props = context.scene.BIMCostProperties
attributes = {}
for attribute in props.cost_item_attributes:
if attribute.is_null:
attributes[attribute.name] = None
else:
if attribute.data_type == "string":
attributes[attribute.name] = attribute.string_value
elif attribute.data_type == "boolean":
attributes[attribute.name] = attribute.bool_value
elif attribute.data_type == "integer":
attributes[attribute.name] = attribute.int_value
elif attribute.data_type == "enum":
attributes[attribute.name] = attribute.enum_value
self.file = IfcStore.get_file()
ifcopenshell.api.run(
"cost.edit_cost_item",
self.file,
**{"cost_item": self.file.by_id(props.active_cost_item_id), "attributes": attributes},
)
Data.load(IfcStore.get_file())
bpy.ops.bim.disable_editing_cost_item()
bpy.ops.bim.enable_editing_cost_items(cost_schedule=props.active_cost_schedule_id)
return {"FINISHED"}
class AssignControl(bpy.types.Operator):
bl_idname = "bim.assign_control"
bl_label = "Assign Control"
cost_item: bpy.props.IntProperty()
related_object: bpy.props.StringProperty()
def execute(self, context):
related_objects = (
[bpy.data.objects.get(self.related_object)] if self.related_object else bpy.context.selected_objects
)
for related_object in related_objects:
self.file = IfcStore.get_file()
ifcopenshell.api.run(
"control.assign_control",
self.file,
related_object=self.file.by_id(related_object.BIMObjectProperties.ifc_definition_id),
relating_control=self.file.by_id(self.cost_item),
)
Data.load(self.file)
return {"FINISHED"}
class UnassignControl(bpy.types.Operator):
bl_idname = "bim.unassign_control"
bl_label = "Unassign Control"
cost_item: bpy.props.IntProperty()
related_object: bpy.props.StringProperty()
def execute(self, context):
related_objects = (
[bpy.data.objects.get(self.related_object)] if self.related_object else bpy.context.selected_objects
)
for related_object in related_objects:
self.file = IfcStore.get_file()
ifcopenshell.api.run(
"control.unassign_control",
self.file,
related_object=self.file.by_id(related_object.BIMObjectProperties.ifc_definition_id),
relating_control=self.file.by_id(self.cost_item),
)
Data.load(self.file)
return {"FINISHED"}
class EnableEditingCostItemQuantities(bpy.types.Operator):
bl_idname = "bim.enable_editing_cost_item_quantities"
bl_label = "Enable Editing Cost Item Quantities"
cost_item: bpy.props.IntProperty()
def execute(self, context):
props = context.scene.BIMCostProperties
props.active_cost_item_id = self.cost_item
props.cost_item_editing_type = "QUANTITIES"
return {"FINISHED"}
class EnableEditingCostItemValues(bpy.types.Operator):
bl_idname = "bim.enable_editing_cost_item_values"
bl_label = "Enable Editing Cost Item Values"
cost_item: bpy.props.IntProperty()
def execute(self, context):
props = context.scene.BIMCostProperties
props.active_cost_item_id = self.cost_item
props.cost_item_editing_type = "VALUES"
return {"FINISHED"}
class AddCostItemQuantity(bpy.types.Operator):
bl_idname = "bim.add_cost_item_quantity"
bl_label = "Add Cost Item Quantity"
cost_item: bpy.props.IntProperty()
ifc_class: bpy.props.StringProperty()
def execute(self, context):
self.file = IfcStore.get_file()
self.props = context.scene.BIMCostProperties
if self.props.quantity_types == "QTO":
self.add_quantities_from_qto_filter()
else:
self.add_manual_quantity()
Data.load(self.file)
return {"FINISHED"}
def add_quantities_from_qto_filter(self):
ifcopenshell.api.run(
"cost.assign_cost_item_product_quantities",
self.file,
cost_item=self.file.by_id(self.cost_item),
qto_name=self.props.qto_name,
prop_name=self.props.prop_name
)
def add_manual_quantity(self):
ifcopenshell.api.run(
"cost.add_cost_item_quantity",
self.file,
cost_item=self.file.by_id(self.cost_item),
ifc_class=self.ifc_class,
)
class RemoveCostItemQuantity(bpy.types.Operator):
bl_idname = "bim.remove_cost_item_quantity"
bl_label = "Add Cost Item Quantity"
cost_item: bpy.props.IntProperty()
physical_quantity: bpy.props.IntProperty()
def execute(self, context):
self.file = IfcStore.get_file()
ifcopenshell.api.run(
"cost.remove_cost_item_quantity",
self.file,
cost_item=self.file.by_id(self.cost_item),
physical_quantity=self.file.by_id(self.physical_quantity),
)
Data.load(self.file)
return {"FINISHED"}
class EnableEditingCostItemQuantity(bpy.types.Operator):
bl_idname = "bim.enable_editing_cost_item_quantity"
bl_label = "Enable Editing Cost Item Quantity"
physical_quantity: bpy.props.IntProperty()
def execute(self, context):
self.props = context.scene.BIMCostProperties
while len(self.props.quantity_attributes) > 0:
self.props.quantity_attributes.remove(0)
self.props.active_cost_item_quantity_id = self.physical_quantity
data = Data.physical_quantities[self.physical_quantity]
for attribute in IfcStore.get_schema().declaration_by_name(data["type"]).all_attributes():
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
if data_type == "entity":
continue
new = self.props.quantity_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 == "float":
new.float_value = 0.0 if new.is_null else data[attribute.name()]
elif data_type == "integer":
new.int_value = 0 if new.is_null else data[attribute.name()]
return {"FINISHED"}
class DisableEditingCostItemQuantity(bpy.types.Operator):
bl_idname = "bim.disable_editing_cost_item_quantity"
bl_label = "Disable Editing Cost Item Quantity"
def execute(self, context):
props = context.scene.BIMCostProperties
props.active_cost_item_quantity_id = 0
return {"FINISHED"}
class EditCostItemQuantity(bpy.types.Operator):
bl_idname = "bim.edit_cost_item_quantity"
bl_label = "Edit Cost Item Quantity"
physical_quantity: bpy.props.IntProperty()
def execute(self, context):
props = context.scene.BIMCostProperties
attributes = {}
for attribute in props.quantity_attributes:
if attribute.is_null:
attributes[attribute.name] = None
else:
if attribute.data_type == "string":
attributes[attribute.name] = attribute.string_value
if attribute.data_type == "float":
attributes[attribute.name] = attribute.float_value
if attribute.data_type == "integer":
attributes[attribute.name] = attribute.int_value
self.file = IfcStore.get_file()
ifcopenshell.api.run(
"cost.edit_cost_item_quantity",
self.file,
**{"physical_quantity": self.file.by_id(self.physical_quantity), "attributes": attributes},
)
Data.load(IfcStore.get_file())
bpy.ops.bim.disable_editing_cost_item_quantity()
return {"FINISHED"}
class AddCostItemValue(bpy.types.Operator):
bl_idname = "bim.add_cost_item_value"
bl_label = "Add Cost Item Value"
cost_item: bpy.props.IntProperty()
cost_type: bpy.props.StringProperty()
cost_category: bpy.props.StringProperty()
def execute(self, context):
self.file = IfcStore.get_file()
if self.cost_type == "FIXED":
category = None
elif self.cost_type == "SUM":
category = "*"
elif self.cost_type == "CATEGORY":
category = self.cost_category
value = ifcopenshell.api.run("cost.add_cost_item_value", self.file, cost_item=self.file.by_id(self.cost_item))
ifcopenshell.api.run(
"cost.edit_cost_item_value", self.file, cost_value=value, attributes={"Category": category}
)
Data.load(self.file)
return {"FINISHED"}
class RemoveCostItemValue(bpy.types.Operator):
bl_idname = "bim.remove_cost_item_value"
bl_label = "Add Cost Item Value"
cost_value: bpy.props.IntProperty()
def execute(self, context):
self.file = IfcStore.get_file()
ifcopenshell.api.run(
"cost.remove_cost_item_value",
self.file,
cost_value=self.file.by_id(self.cost_value),
)
Data.load(self.file)
return {"FINISHED"}
class EnableEditingCostItemValue(bpy.types.Operator):
bl_idname = "bim.enable_editing_cost_item_value"
bl_label = "Enable Editing Cost Item Value"
cost_value: bpy.props.IntProperty()
def execute(self, context):
self.props = context.scene.BIMCostProperties
while len(self.props.cost_value_attributes) > 0:
self.props.cost_value_attributes.remove(0)
self.props.active_cost_item_value_id = self.cost_value
data = Data.cost_values[self.cost_value]
for attribute in IfcStore.get_schema().declaration_by_name(data["type"]).all_attributes():
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
if data_type == "entity" or isinstance(data_type, tuple):
continue
new = self.props.cost_value_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 attribute.name() == "AppliedValue":
# TODO: for now, only support simple values
new.data_type = "float"
new.float_value = 0.0 if new.is_null else data[attribute.name()]
if data_type == "string":
new.string_value = "" 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 == "integer":
new.int_value = 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()]
return {"FINISHED"}
class DisableEditingCostItemValue(bpy.types.Operator):
bl_idname = "bim.disable_editing_cost_item_value"
bl_label = "Disable Editing Cost Item Value"
def execute(self, context):
props = context.scene.BIMCostProperties
props.active_cost_item_value_id = 0
return {"FINISHED"}
class EditCostItemValue(bpy.types.Operator):
bl_idname = "bim.edit_cost_item_value"
bl_label = "Edit Cost Item Value"
cost_value: bpy.props.IntProperty()
def execute(self, context):
props = context.scene.BIMCostProperties
attributes = {}
for attribute in props.cost_value_attributes:
if attribute.is_null:
attributes[attribute.name] = None
else:
if attribute.data_type == "string":
attributes[attribute.name] = attribute.string_value
if attribute.data_type == "float":
attributes[attribute.name] = attribute.float_value
if attribute.data_type == "integer":
attributes[attribute.name] = attribute.int_value
self.file = IfcStore.get_file()
ifcopenshell.api.run(
"cost.edit_cost_item_value",
self.file,
**{"cost_value": self.file.by_id(self.cost_value), "attributes": attributes},
)
Data.load(IfcStore.get_file())
bpy.ops.bim.disable_editing_cost_item_value()
return {"FINISHED"}
@@ -0,0 +1,90 @@
import bpy
import ifcopenshell.api
from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.cost.data import Data
from blenderbim.bim.prop import StrProperty, Attribute
from bpy.types import PropertyGroup
from bpy.props import (
PointerProperty,
StringProperty,
EnumProperty,
BoolProperty,
IntProperty,
FloatProperty,
FloatVectorProperty,
CollectionProperty,
)
quantitytypes_enum = []
def purge():
global quantitytypes_enum
quantitytypes_enum = []
def getQuantityTypes(self, context):
global quantitytypes_enum
if len(quantitytypes_enum) == 0 and IfcStore.get_schema():
quantitytypes_enum.clear()
quantitytypes_enum = [("QTO", "Qto", "Derive quantities from IFC quantity sets")]
quantitytypes_enum.extend(
[
(t.name(), t.name(), "")
for t in IfcStore.get_schema().declaration_by_name("IfcPhysicalSimpleQuantity").subtypes()
]
)
return quantitytypes_enum
def updateCostItemName(self, context):
if self.name == "Unnamed":
return
self.file = IfcStore.get_file()
props = context.scene.BIMCostProperties
ifcopenshell.api.run(
"cost.edit_cost_item",
self.file,
**{"cost_item": self.file.by_id(self.ifc_definition_id), "attributes": {"Name": self.name}},
)
Data.load(IfcStore.get_file())
if props.active_cost_item_id == self.ifc_definition_id:
attribute = props.cost_item_attributes.get("Name")
attribute.string_value = self.name
class CostItem(PropertyGroup):
name: StringProperty(name="Name", update=updateCostItemName)
ifc_definition_id: IntProperty(name="IFC Definition ID")
has_children: BoolProperty(name="Has Children")
is_expanded: BoolProperty(name="Is Expanded")
level_index: IntProperty(name="Level Index")
class BIMCostProperties(PropertyGroup):
cost_schedule_attributes: CollectionProperty(name="Cost Schedule Attributes", type=Attribute)
is_editing: StringProperty(name="Is Editing")
active_cost_schedule_id: IntProperty(name="Active Cost Schedule Id")
cost_items: CollectionProperty(name="Work Calendar", type=CostItem)
active_cost_item_id: IntProperty(name="Active Cost Id")
cost_item_editing_type: StringProperty(name="Cost Item Editing Type")
active_cost_item_index: IntProperty(name="Active Cost Item Index")
cost_item_attributes: CollectionProperty(name="Task Attributes", type=Attribute)
contracted_cost_items: StringProperty(name="Contracted Cost Items", default="[]")
quantity_types: EnumProperty(items=getQuantityTypes, name="Quantity Types")
qto_name: StringProperty(name="Qto Name")
prop_name: StringProperty(name="Prop Name")
active_cost_item_quantity_id: IntProperty(name="Active Cost Item Quantity Id")
quantity_attributes: CollectionProperty(name="Quantity Attributes", type=Attribute)
cost_types: EnumProperty(
items=[
("FIXED", "Fixed", "The cost value is a fixed number"),
("SUM", "Sum", "The cost value is automatically derived from the sum of all nested cost items"),
("CATEGORY", "Category", "The cost value represents a single category"),
],
name="Cost Types",
)
cost_category: StringProperty(name="Cost Category")
active_cost_item_value_id: IntProperty(name="Active Cost Item Value Id")
cost_value_attributes: CollectionProperty(name="Cost Value Attributes", type=Attribute)
@@ -0,0 +1,249 @@
from bpy.types import Panel, UIList
from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.cost.data import Data
class BIM_PT_cost_schedules(Panel):
bl_label = "IFC Cost Schedules"
bl_idname = "BIM_PT_cost_schedules"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
@classmethod
def poll(cls, context):
return IfcStore.get_file()
def draw(self, context):
self.props = context.scene.BIMCostProperties
if not Data.is_loaded:
Data.load(IfcStore.get_file())
row = self.layout.row()
row.operator("bim.add_cost_schedule", icon="ADD")
for cost_schedule_id, cost_schedule in Data.cost_schedules.items():
self.draw_cost_schedule_ui(cost_schedule_id, cost_schedule)
def draw_cost_schedule_ui(self, cost_schedule_id, cost_schedule):
row = self.layout.row(align=True)
row.label(text=cost_schedule["Name"] or "Unnamed", icon="LINENUMBERS_ON")
if self.props.active_cost_schedule_id and self.props.active_cost_schedule_id == cost_schedule_id:
if self.props.is_editing == "COST_SCHEDULE":
row.operator("bim.edit_cost_schedule", text="", icon="CHECKMARK")
elif self.props.is_editing == "COST_ITEMS":
row.operator("bim.add_summary_cost_item", text="", icon="ADD").cost_schedule = cost_schedule_id
row.operator("bim.disable_editing_cost_schedule", text="", icon="CANCEL")
elif self.props.active_cost_schedule_id:
row.operator("bim.remove_cost_schedule", text="", icon="X").cost_schedule = cost_schedule_id
else:
row.operator("bim.enable_editing_cost_items", text="", icon="OUTLINER").cost_schedule = cost_schedule_id
row.operator(
"bim.enable_editing_cost_schedule", text="", icon="GREASEPENCIL"
).cost_schedule = cost_schedule_id
row.operator("bim.remove_cost_schedule", text="", icon="X").cost_schedule = cost_schedule_id
if self.props.active_cost_schedule_id == cost_schedule_id:
if self.props.is_editing == "COST_SCHEDULE":
self.draw_editable_cost_schedule_ui()
elif self.props.is_editing == "COST_ITEMS":
self.draw_editable_cost_item_ui(cost_schedule_id)
def draw_editable_cost_schedule_ui(self):
for attribute in self.props.cost_schedule_attributes:
row = self.layout.row(align=True)
if attribute.data_type == "string":
row.prop(attribute, "string_value", text=attribute.name)
elif attribute.data_type == "enum":
row.prop(attribute, "enum_value", text=attribute.name)
if attribute.is_optional:
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
def draw_editable_cost_item_ui(self, cost_schedule_id):
self.layout.template_list(
"BIM_UL_cost_items",
"",
self.props,
"cost_items",
self.props,
"active_cost_item_index",
)
if self.props.active_cost_item_id:
if self.props.cost_item_editing_type == "ATTRIBUTES":
self.draw_editable_cost_item_attributes_ui()
elif self.props.cost_item_editing_type == "QUANTITIES":
self.draw_editable_cost_item_quantities_ui()
elif self.props.cost_item_editing_type == "VALUES":
self.draw_editable_cost_item_values_ui()
def draw_editable_cost_item_attributes_ui(self):
for attribute in self.props.cost_item_attributes:
row = self.layout.row(align=True)
if attribute.data_type == "string":
row.prop(attribute, "string_value", text=attribute.name)
elif attribute.data_type == "boolean":
row.prop(attribute, "bool_value", text=attribute.name)
elif attribute.data_type == "integer":
row.prop(attribute, "int_value", text=attribute.name)
elif attribute.data_type == "enum":
row.prop(attribute, "enum_value", text=attribute.name)
if attribute.is_optional:
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
def draw_editable_cost_item_quantities_ui(self):
row = self.layout.row(align=True)
row.prop(self.props, "quantity_types", text="")
if self.props.quantity_types == "QTO":
row.prop(self.props, "qto_name", text="")
row.prop(self.props, "prop_name", text="")
op = row.operator("bim.add_cost_item_quantity", text="", icon="ADD")
op.cost_item = self.props.active_cost_item_id
op.ifc_class = self.props.quantity_types
for quantity_id in Data.cost_items[self.props.active_cost_item_id]["CostQuantities"]:
quantity = Data.physical_quantities[quantity_id]
value = quantity[[k for k in quantity.keys() if "Value" in k][0]]
row = self.layout.row(align=True)
row.label(text=quantity["Name"])
row.label(text=str(value))
if self.props.active_cost_item_quantity_id and self.props.active_cost_item_quantity_id == quantity_id:
op = row.operator("bim.edit_cost_item_quantity", text="", icon="CHECKMARK")
op.physical_quantity = quantity_id
row.operator("bim.disable_editing_cost_item_quantity", text="", icon="CANCEL")
elif self.props.active_cost_item_quantity_id:
op = row.operator("bim.remove_cost_item_quantity", text="", icon="X")
op.cost_item = self.props.active_cost_item_id
op.physical_quantity = quantity_id
else:
op = row.operator("bim.enable_editing_cost_item_quantity", text="", icon="GREASEPENCIL")
op.physical_quantity = quantity_id
op = row.operator("bim.remove_cost_item_quantity", text="", icon="X")
op.cost_item = self.props.active_cost_item_id
op.physical_quantity = quantity_id
if self.props.active_cost_item_quantity_id and self.props.active_cost_item_quantity_id == quantity_id:
box = self.layout.box()
self.draw_editable_cost_item_quantity_ui(box)
def draw_editable_cost_item_quantity_ui(self, layout):
for attribute in self.props.quantity_attributes:
row = layout.row(align=True)
if attribute.data_type == "string":
row.prop(attribute, "string_value", text=attribute.name)
elif attribute.data_type == "boolean":
row.prop(attribute, "bool_value", text=attribute.name)
elif attribute.data_type == "integer":
row.prop(attribute, "int_value", text=attribute.name)
elif attribute.data_type == "float":
row.prop(attribute, "float_value", text=attribute.name)
elif attribute.data_type == "enum":
row.prop(attribute, "enum_value", text=attribute.name)
if attribute.is_optional:
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
def draw_editable_cost_item_values_ui(self):
row = self.layout.row(align=True)
row.prop(self.props, "cost_types", text="")
if self.props.cost_types == "CATEGORY":
row.prop(self.props, "cost_category", text="")
op = row.operator("bim.add_cost_item_value", text="", icon="ADD")
op.cost_item = self.props.active_cost_item_id
op.cost_type = self.props.cost_types
if self.props.cost_types == "CATEGORY":
op.cost_category = self.props.cost_category
for cost_value_id in Data.cost_items[self.props.active_cost_item_id]["CostValues"]:
cost_value = Data.cost_values[cost_value_id]
row = self.layout.row(align=True)
row.label(text=str(cost_value["Category"]))
row.label(text=str(cost_value["AppliedValue"]))
if self.props.active_cost_item_value_id and self.props.active_cost_item_value_id == cost_value_id:
op = row.operator("bim.edit_cost_item_value", text="", icon="CHECKMARK")
op.cost_value = cost_value_id
row.operator("bim.disable_editing_cost_item_value", text="", icon="CANCEL")
elif self.props.active_cost_item_value_id:
op = row.operator("bim.remove_cost_item_value", text="", icon="X")
op.cost_value = cost_value_id
else:
op = row.operator("bim.enable_editing_cost_item_value", text="", icon="GREASEPENCIL")
op.cost_value = cost_value_id
op = row.operator("bim.remove_cost_item_value", text="", icon="X")
op.cost_value = cost_value_id
if self.props.active_cost_item_value_id and self.props.active_cost_item_value_id == cost_value_id:
box = self.layout.box()
self.draw_editable_cost_item_value_ui(box)
def draw_editable_cost_item_value_ui(self, layout):
for attribute in self.props.cost_value_attributes:
row = layout.row(align=True)
if attribute.data_type == "string":
row.prop(attribute, "string_value", text=attribute.name)
elif attribute.data_type == "boolean":
row.prop(attribute, "bool_value", text=attribute.name)
elif attribute.data_type == "integer":
row.prop(attribute, "int_value", text=attribute.name)
elif attribute.data_type == "float":
row.prop(attribute, "float_value", text=attribute.name)
elif attribute.data_type == "enum":
row.prop(attribute, "enum_value", text=attribute.name)
if attribute.is_optional:
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
class BIM_UL_cost_items(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
if item:
props = context.scene.BIMCostProperties
cost_item = Data.cost_items[item.ifc_definition_id]
row = layout.row(align=True)
for i in range(0, item.level_index):
row.label(text="", icon="BLANK1")
if item.has_children:
if item.is_expanded:
row.operator(
"bim.contract_cost_item", text="", emboss=False, icon="DISCLOSURE_TRI_DOWN"
).cost_item = item.ifc_definition_id
else:
row.operator(
"bim.expand_cost_item", text="", emboss=False, icon="DISCLOSURE_TRI_RIGHT"
).cost_item = item.ifc_definition_id
else:
row.label(text="", icon="DOT")
row.prop(item, "name", emboss=False, text="")
row.label(text="M3")
op = row.operator("bim.enable_editing_cost_item_quantities", text="", icon="PROPERTIES")
op.cost_item = item.ifc_definition_id
row.label(text=str(cost_item["TotalCostQuantity"]))
op = row.operator("bim.enable_editing_cost_item_values", text="", icon="DISC")
op.cost_item = item.ifc_definition_id
row.label(text=str(cost_item["TotalAppliedValue"]))
row.label(text=str(cost_item["TotalCostValue"]), icon="CON_TRANSLIKE")
if context.active_object:
oprops = context.active_object.BIMObjectProperties
row = layout.row(align=True)
if oprops.ifc_definition_id in cost_item["Controls"]:
op = row.operator("bim.unassign_control", text="", icon="KEYFRAME_HLT", emboss=False)
op.cost_item = item.ifc_definition_id
else:
op = row.operator("bim.assign_control", text="", icon="KEYFRAME", emboss=False)
op.cost_item = item.ifc_definition_id
if props.active_cost_item_id == item.ifc_definition_id:
row.operator("bim.edit_cost_item", text="", icon="CHECKMARK")
row.operator("bim.disable_editing_cost_item", text="", icon="CANCEL")
elif props.active_cost_item_id:
row.operator("bim.add_cost_item", text="", icon="ADD").cost_item = item.ifc_definition_id
row.operator("bim.remove_cost_item", text="", icon="X").cost_item = item.ifc_definition_id
else:
row.operator(
"bim.enable_editing_cost_item", text="", icon="GREASEPENCIL"
).cost_item = item.ifc_definition_id
row.operator("bim.add_cost_item", text="", icon="ADD").cost_item = item.ifc_definition_id
row.operator("bim.remove_cost_item", text="", icon="X").cost_item = item.ifc_definition_id
@@ -7,6 +7,7 @@ import json
import webbrowser
import tempfile
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.handler import purge_module_data
class AddCsvAttribute(bpy.types.Operator):
@@ -78,9 +79,19 @@ class ImportIfcCsv(bpy.types.Operator):
def execute(self, context):
import ifccsv
props = context.scene.CsvProperties
if props.should_load_from_memory:
ifc_file = IfcStore.get_file()
else:
ifc_file = ifcopenshell.open(props.csv_ifc_file)
ifc_csv = ifccsv.IfcCsv()
ifc_csv.output = self.filepath
ifc_csv.Import(context.scene.CsvProperties.csv_ifc_file)
if props.csv_delimiter == "CUSTOM":
ifc_csv.delimiter = props.csv_custom_delimiter
else:
ifc_csv.delimiter = props.csv_delimiter
ifc_csv.Import(ifc_file)
purge_module_data()
return {"FINISHED"}
@@ -53,6 +53,9 @@ class BIM_PT_debug(Panel):
row = layout.row(align=True)
row.prop(attribute, "name", text="")
row.prop(attribute, "string_value", text="")
if attribute.name == "GlobalId":
op = row.operator("bim.select_global_id", icon="RESTRICT_SELECT_OFF", text="")
op.global_id = attribute.string_value
if attribute.int_value:
row.operator(
"bim.inspect_from_step_id", icon="DISCLOSURE_TRI_RIGHT", text=""

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