Compare commits

...

773 Commits

Author SHA1 Message Date
Thomas Krijnen acddbfd18c Update .travis.yml 2021-07-10 16:08:23 +02:00
Thomas Krijnen 791f57072e Update .travis.yml 2021-07-10 16:05:32 +02:00
Thomas Krijnen 078f7b3dae Update .travis.yml 2021-07-10 15:58:54 +02:00
Rick Brice c02f270993 Adds support for 4x3 rc3 and rc4 for IfcParser library 2021-07-10 15:38:34 +02:00
Rick Brice a73178fe3e Adds ignore settings for vs2015 and vs2019 2021-07-10 15:38:34 +02:00
Thomas Krijnen 24808ced60 Merge pull request #1530 from atomczak/GSoC#45-IDS-checking
IDS - xmlschema, restrictions and requirements
2021-07-10 15:35:08 +02:00
Dion Moult 1d523c9636 Implement assigning material definition representation styles 2021-07-10 22:06:11 +10:00
Dion Moult 6ccb208345 Fix #1557. Surface style attributes can now be explicitly edited. 2021-07-10 12:34:25 +10:00
Dion Moult af0f0a4f6c Implement support for removing styles. See #1557. 2021-07-10 11:43:01 +10:00
Dion Moult edd737aa5d Minor fix 2021-07-10 10:32:34 +10:00
ArturTomczak 5d8f71128e add tests for parsing and authoring 2021-07-09 08:44:36 +02:00
ArturTomczak 1ea4f50d30 fix facet constructors 2021-07-09 08:35:04 +02:00
ArturTomczak 93fad2a5c3 add ids constructor, fix ids bug 2021-07-08 18:14:49 +02:00
ArturTomczak 30a03b0c51 add facet creation methods 2021-07-08 18:12:55 +02:00
ArturTomczak b0bb2cb58d add ids.to_xml() 2021-07-08 18:09:32 +02:00
ArturTomczak 3837c9d6ff add methods to specification class 2021-07-08 18:09:02 +02:00
ArturTomczak d189113b98 add asdict() to all ids classes 2021-07-08 18:06:07 +02:00
ArturTomczak 6dba96e780 add location_msg as separate param 2021-07-08 18:04:50 +02:00
ArturTomczak c2bdfa9394 fix imports and move ids_schema outside 2021-07-08 18:00:33 +02:00
Dion Moult 519a53dc61 Fix #1554. Changing Blender material name now auto updates both IFC material name and surface style name 2021-07-08 18:59:40 +10:00
Dion Moult 21c4433c75 Allow setting to toggle cascading of edited object placements 2021-07-08 18:00:41 +10:00
Dion Moult 0e24d4411f Fix #1556. Ensure that styles are always present when updating a representation. 2021-07-08 13:02:30 +10:00
Dion Moult cb6837b0ac Add Windows install docs for BlenderBIM for devs 2021-07-07 16:10:47 +10:00
Kai Zhu 5ec7f65df5 Update source installation for windows docs (#1555)
Added docs for Windows mklink syntax.
2021-07-07 16:03:11 +10:00
Dion Moult db04cc1bb5 Implement partial undo for BCF operators. Note that undo is not supported in BCF lib itself, nor BCF API. See #1475. 2021-07-07 11:57:07 +10:00
Dion Moult 1f128fd7e4 Minor BCF fix 2021-07-07 11:44:16 +10:00
bosonprojets 56633b8126 Implement undo for Costing, Sequencing, and Resources 2021-07-07 02:01:19 +01:00
Dion Moult 6706ad9bd5 Implement undo for augin, bimtester, clash, cobie, and covetool operators. See #1475. 2021-07-07 10:40:07 +10:00
Dion Moult fb6c670aa6 Fix colour by undo operators in search module. See #1475. 2021-07-07 10:25:19 +10:00
Dion Moult a87637163a Fix #1553. Bug where reloading an IFC file didn't refresh the ID map without needing to restart. 2021-07-07 09:55:42 +10:00
Jesusbill 4bc5a3ba46 small bug fix from 75d67cd719 2021-07-06 16:02:14 +02:00
Jesusbill 866df1f040 implement undo for "search" module, may need improvement for some operators #1475 2021-07-06 12:19:45 +02:00
Jesusbill 2fa2fddf46 implement undo for "owner" module, may need improvement for some operators #1475 2021-07-06 12:19:27 +02:00
Jesusbill 1aeae0bf4d use import_attributes from helper for #1549 2021-07-06 12:17:59 +02:00
Jesusbill 6a2c4aee46 implement undo for 'patch' module #1475 2021-07-06 12:15:45 +02:00
Dion Moult 5cfbcf211b Minor fix 2021-07-06 18:17:34 +10:00
Dion Moult 3dcd10b478 Implement undo in debug and diff operators. 2021-07-06 16:03:27 +10:00
Dion Moult c687e9af05 Implement undo for model operators. See #1475. 2021-07-06 15:47:53 +10:00
Dion Moult b10c228155 Implement undo for drawing operators. See #1475. 2021-07-06 15:17:55 +10:00
Dion Moult b1d731447a Implement undo for non-module operators. See #1475. 2021-07-06 15:06:21 +10:00
Dion Moult 32e7083e21 Implement undo for pset template operators. See #1475. 2021-07-06 14:21:02 +10:00
Dion Moult 597b5ffbd6 Minor fix 2021-07-06 12:33:05 +10:00
Dion Moult f7e550b926 Add documentation on georeferencing 2021-07-06 12:20:22 +10:00
Dion Moult 75d67cd719 Fix #1551. Support mixed geolocation workarounds of both object placement and cartesian point. 2021-07-06 12:20:04 +10:00
ArturTomczak 41a47f14ae add basic parsing tests 2021-07-05 13:21:36 +02:00
Dion Moult cca28231ee Implement undo for material operators. See #1475. 2021-07-05 18:59:10 +10:00
s-leger d7b3a1d95a Delete OCC.pth (#1550)
Remove no more needed OCC.pth (setup OCC as module)
2021-07-05 18:35:50 +10:00
Dion Moult 30ba45f6cb Implement undo for pset operators. See #1475. 2021-07-05 17:09:04 +10:00
Dion Moult d832c9780a Implement undo for qto operators. See #1475. 2021-07-05 16:51:26 +10:00
Dion Moult 7f3b7f4e21 Implement undo for georeference operators. See #1475. 2021-07-05 16:42:38 +10:00
Dion Moult dc7dea21ce Implement undo for spatial operators. See #1475. 2021-07-05 16:36:33 +10:00
Dion Moult d6a9b73657 Minor fix. 2021-07-05 16:35:13 +10:00
Dion Moult 97cd919a7b Improve undo redo object linking robustness. See #1475. 2021-07-05 15:59:26 +10:00
Dion Moult 41bc109556 Implement undo for type, unit, and void operators. See #1475. 2021-07-05 15:03:50 +10:00
Dion Moult 4f5d4def1e Implement batch transaction support. See #1475. 2021-07-05 14:16:19 +10:00
Jesusbill 3a24f03bed black formatting 2021-07-05 02:31:13 +02:00
Jesusbill f3ed96846e bug fix in constraint module, see #1549 2021-07-05 02:27:56 +02:00
Jesusbill cbe8165e08 add undo for "document" module #1475 2021-07-05 02:21:10 +02:00
Jesusbill 6a7a122de2 add undo for "csv" module #1475 2021-07-05 02:20:54 +02:00
Jesusbill e44db61fbd add undo for "classification" module #1475 2021-07-05 02:20:24 +02:00
Dion Moult 72b60e85d4 Minor fix 2021-07-05 09:01:10 +10:00
Dion Moult c48263ade9 Add installation instructions for IfcSverchok 2021-07-05 09:00:21 +10:00
Jesusbill 5ec081c6bc add undo for "contraint" module #1475 2021-07-04 18:33:35 +02:00
Jesusbill cc3f94097c add undo for "layer" module #1475 2021-07-04 18:32:29 +02:00
Dion Moult 253d478fdc Implement undo for style module operators. See #1475. 2021-07-04 20:28:21 +10:00
Dion Moult 549bc0e1a9 Minor fix. See #1475. 2021-07-04 20:22:20 +10:00
Dion Moult 9286de4813 Minor fix. See #1547. 2021-07-04 19:57:09 +10:00
Dion Moult b8015644e9 Implement undo for context and geometry operators. See #1475. 2021-07-04 19:45:34 +10:00
Dion Moult 8a917dc050 Fix #1529. Properly represent Blender materials as 1:1 mappings to surface styles, with support for material definition representation. 2021-07-04 12:38:41 +10:00
Dion Moult 68b6389794 Fix #1547. 2021-07-04 09:54:49 +10:00
Dion Moult 85bd616f6e Minor fix 2021-07-03 18:46:17 +10:00
Dion Moult 4ff60553cc Fix bug with inability to refresh token in bsdd api. Thanks TestPrab! 2021-07-03 17:45:29 +10:00
Dion Moult dcd6cace03 Fix bug in IfcSverchok nodes where refresh buttons didn't work 2021-07-03 14:34:15 +10:00
Dion Moult b7cfa53b4d IfcSverchok by_type node now updates to be schema dependent on the input file 2021-07-03 14:33:47 +10:00
Dion Moult e0048f7a3c Minor fix 2021-07-03 10:41:06 +10:00
Jesusbill a8dcb2f306 undo for structural - wip 2021-07-03 02:08:31 +02:00
Dion Moult a8f0a4807c Add undo support for group module operators 2021-07-03 10:03:27 +10:00
Dion Moult d5539399e8 New print IFC file debug operator 2021-07-03 10:02:26 +10:00
ArturTomczak ad8542f244 add unittest init 2021-07-02 17:27:09 +02:00
Dion Moult eb89308019 Minor fix 2021-07-02 19:16:55 +10:00
Dion Moult e7e7b22fd5 Implement undo support for all root operations. Clean up implementation, making passing transaction keys unnecessary. See #1475. 2021-07-02 19:10:51 +10:00
Dion Moult b4a422c2b8 New add application usecase, improved add_classification usecase and better defaults for owner settings. Thanks johltn 2021-07-02 16:30:57 +10:00
Carmen Fan 30f778e0a7 restore other schema versions 2021-07-01 20:42:45 +02:00
Carmen Fan 2032b361c8 fix spelling 2021-07-01 20:42:45 +02:00
Carmen Fan 529b3a9867 add necessary export definitions , add IFCParse as a dependency to IfcGeom build 2021-07-01 20:42:45 +02:00
Dion Moult cac8c64ac8 Document undo system 2021-07-01 21:05:26 +10:00
Dion Moult 3847c9bd7a Implement undo for aggregate operations, with automatic element-obj link undo/redo and improved undo syntax. See #1475. 2021-07-01 20:43:33 +10:00
Thomas Krijnen ffbaa6f0e0 #1537 implement schema enum cast 2021-07-01 11:13:50 +02:00
Thomas Krijnen b5c3812849 Missing change for 6ecedaa128 2021-07-01 11:09:15 +02:00
Dion Moult 30ad915649 Minor fix 2021-07-01 11:59:25 +10:00
Dion Moult c2cf3e8639 Minor fix 2021-07-01 11:53:56 +10:00
Dion Moult c1527b4199 Document module architecture 2021-07-01 11:53:00 +10:00
Dion Moult a1032a3bf0 Describe IfcOpenShell python architecture in the developer guide 2021-07-01 11:17:59 +10:00
Dion Moult 733b7c0e1e Add basic documentation for developer guide 2021-07-01 09:55:59 +10:00
Dion Moult 6b0a58db7d Experimental undo and redo Python prototype (#1539)
* Experimental undo and redo Python prototype

* Simplify history, use transaction jargon, use walk for more robust serialisation

* Black file, set history size, add file reference to entity_instance constructor

* Ensure that files are always passed when entities are wrapped

* Add support for undo/redo of all project module operations

* Minor fix

* Blender to IFC mappings are now managed by Blender RNA, so they don't break on undo operations. See #1475.

* Implement undo for all attribute operations. See #1475.

* Update documentation for installation of add-on

* Revert Blender RNA approach for Blender-IFC mappings, because it didn't scale, but still fix the undo/redo object memory corruption with new "reload_linked_elements" method.
2021-06-30 18:34:12 +10:00
Thomas Krijnen 10bf875e92 Remove wrong and unnecessary shape cast in derived profile 2021-06-27 20:29:47 +02:00
Thomas Krijnen 2fd2b49709 #1539 Option to specify id/instance_name in file::add() 2021-06-27 11:14:26 +02:00
Dion Moult 37a61c549f Accommmodate incorrect body representation contexts coming from Revit 2021-06-27 11:00:20 +10:00
Thomas Krijnen da10a1060e #1475 IfcFile::recalculate_id_counter() 2021-06-26 13:23:55 +02:00
Dion Moult ebfb29d6da Fix #1538. Bug when absolute coordinates are detected from a coordinate list. 2021-06-26 17:09:44 +10:00
Dion Moult 6e4e6a88a4 Add support for parametric updating of cardinal points 1-9 for profile set usages 2021-06-25 17:25:26 +10:00
Dion Moult 312de694b6 Task trees can now be sorted by any attribute independent of the WBS 2021-06-23 18:17:39 +10:00
Dion Moult 788a5a829e Tasks now auto generate identification numbers 2021-06-23 14:13:18 +10:00
Dion Moult a4391df9ca Wall tool now rebranded as BIM tool with a new icon thanks to s-leger! 2021-06-23 11:25:05 +10:00
Dion Moult 1071ac00d4 Fix validation errors in IFC2X3 with missing ownership and mandatory attributes 2021-06-23 10:35:25 +10:00
Thomas Krijnen 6ecedaa128 tree select(gp_Pnt) also with extend 2021-06-22 15:46:21 +02:00
blocovin 8f154c753c Fix xml double value is rounded #1533 2021-06-22 15:12:36 +02:00
Dion Moult e863015e85 You can now customise the data columns shown in the work task tree 2021-06-22 19:32:59 +10:00
Dion Moult 2c49b3bde8 MSProject XML to IFC conversion now supports blank IFCs or non-existent work plans 2021-06-22 13:22:48 +10:00
ArturTomczak eaa527f512 include location (type/inst/any) 2021-06-21 17:48:27 +02:00
ArturTomczak 99f0e6c93e add regex pattern validation 2021-06-21 15:23:03 +02:00
ArturTomczak f27fbecc9a Merge branch 'v0.6.0' into GSoC#45-IDS-checking 2021-06-21 08:48:46 +02:00
Dion Moult 650ebeddd6 Column profiles now update when switching type 2021-06-21 14:44:42 +10:00
Dion Moult 5d0d3aaa16 Minor fix 2021-06-21 14:13:25 +10:00
Dion Moult 54526960c8 Column profiles now auto update when you change the material profile 2021-06-21 12:21:55 +10:00
Jesusbill e0b93a70d3 black formatting 2021-06-20 20:58:36 +02:00
Jesusbill e569b97c56 Add general CRUD operations for boundary conditions (from scene properties) 2021-06-20 20:57:12 +02:00
Jesusbill a0b7fe2736 you can now filter structural loads based on name and inverse references 2021-06-20 20:53:45 +02:00
ArturTomczak 3b270a61df handle ifc2x3 and 4
handles .ItemReference or .Identification
2021-06-19 11:44:33 +02:00
ArturTomczak 5b94043cdc ids parse, restrictions
change __init__ to parse for separating those two
2021-06-18 16:50:26 +02:00
Dion Moult aadb28bb79 Fix #1456. Improve usability when a project library only has a single class type in it. 2021-06-18 16:05:58 +10:00
Dion Moult 2f1f332d31 Fix #1522. Appending from project libraries now supports material properties and styles. 2021-06-18 15:54:01 +10:00
Dion Moult 1ee2f420c8 New feature to see derived global and local elevations 2021-06-18 14:19:53 +10:00
Dion Moult 567bc7b667 Fix confusing transparency attribute in add style API usecase. See #1529. 2021-06-18 08:03:44 +10:00
Dion Moult 2a2f4d692a Fix bug where extrusion detection on voided profiles was not reliable 2021-06-17 19:24:16 +10:00
Dion Moult 1b49a10a15 Minor fix, bump readme 2021-06-17 18:30:25 +10:00
Dion Moult a08918fd22 Scheduled dates now cascade when dependencies, lag times, and calendars are modified 2021-06-17 14:59:28 +10:00
Dion Moult 50608d9459 Modifications to scheduled dates can now cascade through the task tree 2021-06-17 13:23:09 +10:00
Dion Moult 97d0286b57 Uncached schedule recalculation is now twice as fast 2021-06-17 11:05:58 +10:00
Cyril Waechter 2a7fbea9c3 Add recipe to extract only spaces (#1521)
`ExtractElements` was not allowing to extract only spaces.
It also helps to workaround revit-ifc failure in linking spaces.
See: https://github.com/Autodesk/revit-ifc/issues/15
2021-06-17 10:31:18 +10:00
Dion Moult 257997c2cb New feature to copy elements to multiple spatial containers 2021-06-16 09:21:44 +10:00
Dion Moult 2dfc35877c Generated construction animations now support animating input product consumptions, movement to/from, and multiple processes per product 2021-06-15 21:22:28 +10:00
Dion Moult 68233a8fb2 Add support for product inputs into tasks, such as for movements on site 2021-06-15 20:55:43 +10:00
Dion Moult 76e2f688e5 Level of Effort activities are now excluded from P6 import to IFC. 2021-06-15 18:28:43 +10:00
Dion Moult 4d4bedcf9a Fix #1526. Fix bug where custom pset template properties wouldn't show up. 2021-06-15 17:48:45 +10:00
Dion Moult 36a18ea0f9 You can now bulk copy task attributes to other tasks in the task tree 2021-06-15 17:29:04 +10:00
Dion Moult fa1984e89e You can now animate demolition, logistic, and renovation type processes during construction sequencing 2021-06-15 16:35:40 +10:00
Dion Moult 3be0a74316 Minor UI fix for unreadable "derived grid north" in geolocation panel 2021-06-15 15:15:49 +10:00
Dion Moult 90cb56220f Fix #1524. Fixed bug where unlinking copied Blender objects didn't work. 2021-06-15 13:05:24 +10:00
ArturTomczak 8fd4965106 add length restriction
both length and min/maxlength added
2021-06-12 22:35:37 +02:00
ArturTomczak ba26772e46 add bounds restriction 2021-06-12 21:10:50 +02:00
ArturTomczak 7e02eb923d add bounds restriction 2021-06-12 21:10:09 +02:00
ArturTomczak 459149e418 add enumeration restriction 2021-06-12 18:08:13 +02:00
ArturTomczak 768606571e fix multiple requirements at a time 2021-06-11 19:03:03 +02:00
ArturTomczak 65fa5768f2 implement material facet
added actual evaluation and extended current implementation with all possible ifc relations for materials.
2021-06-11 17:10:54 +02:00
ArturTomczak 9db01bc6cc implement classification facet 2021-06-10 15:21:51 +02:00
ArturTomczak 4e60ca01c3 improve feedback message
added counters and runtime info
2021-06-10 15:21:07 +02:00
ArturTomczak 8c9ab98697 switch from dom to xmlschema
as suggested by @Moult, xmlschema better fits the purpose than xml.dom.minidom
2021-06-10 15:19:23 +02:00
Dion Moult fc05bdf6d6 Support importing elements with no representations 2021-06-10 11:17:49 +10:00
Dion Moult 4c50b2d224 Support editing profile set usage attributes cardinal point and reference extent. Thanks Jesusbill! 2021-06-09 11:23:34 +10:00
ArturTomczak 4017097f19 Add xmlschema validation 2021-06-08 10:34:02 +02:00
Dion Moult b9472c9bae Implement free float calculation. Thanks SigmaDimensions! 2021-06-08 11:36:07 +10:00
Dion Moult 7d841c3f27 New dumb column tool. Creates a parametric solid column from profile sets. 2021-06-07 19:43:02 +10:00
Dion Moult ca0dac1353 The authoring UI now only shows available types so you don't waste your time checking what's available. 2021-06-07 16:09:56 +10:00
Dion Moult 1a962a7f60 Dumb slabs now auto calculate new quantities when edited 2021-06-07 15:58:03 +10:00
Dion Moult f3c1d1df31 Fix bug where you couldn't delete a quantity from a quantity set 2021-06-07 15:52:38 +10:00
Dion Moult 6a0562d00a Footprint and side areas are now generated for dumb wall quantities 2021-06-07 14:44:05 +10:00
Dion Moult 137a00225c Dumb slabs now auto generate footprint representations 2021-06-07 14:28:46 +10:00
Dion Moult 5a822d5e39 Slabs with voided profiles are now supported. 2021-06-07 11:20:05 +10:00
Dion Moult d844223367 Fix bug where P6 import module was not shipped 2021-06-07 10:32:26 +10:00
Dion Moult 6a29ab0d61 Fix bug where 2D or 1D objects generated an invalid box representation 2021-06-07 10:32:05 +10:00
Dion Moult e9c7b21bfc Fix bug where all meshes were cast to a solid by accident. Thanks s-leger! 2021-06-07 10:31:27 +10:00
Dion Moult ed316ba691 Fix #1382. You can now search property values agnostic of pset names. Thanks stefkeB! 2021-06-06 20:55:25 +10:00
Dion Moult cfd5742b5b Fix unnecessary error message when getting psets. 2021-06-06 20:54:40 +10:00
Dion Moult feac601340 Fix #1513. Materials now are included when importing types from a project library. 2021-06-06 10:05:24 +10:00
Dion Moult a8dc734351 Fix bugs where adding types without a material was broken. See #1513. 2021-06-06 09:57:50 +10:00
Dion Moult e0cbbf2eb7 Minor fixes to slab/wall syncing and stability in changing thicknesses. 2021-06-05 19:48:04 +10:00
Dion Moult 082ee3c95d Fix #1511. Prevent double registration of listener callbacks, and simplify listeners. 2021-06-05 19:48:04 +10:00
Thomas Krijnen ece6e6c8ca #1480 continued 2021-06-05 09:49:19 +02:00
Dion Moult 5c1d26ac91 Dumb walls and slabs now are always exported as parametric solids instead of meshes. 2021-06-04 17:30:37 +10:00
Dion Moult 153b6ee24d Total float calculation now takes into account calendar 2021-06-04 17:22:35 +10:00
Dion Moult 8b3b1eeb8e Fix #1510. Whoops :) 2021-06-04 15:29:14 +10:00
Dion Moult 8e279ea862 Improved date calculations during import and editing ensure that start / finish dates are always on working days. 2021-06-04 15:21:48 +10:00
Dion Moult 6f8861c030 ES, EF, LS, LF calculations in work schedules now are calendar based. Thanks SigmaDimensions! 2021-06-04 15:20:38 +10:00
Dion Moult 2ac6fe9eef Rewrite schedule calculation to be much faster and scale up to huge schedules. 2021-06-03 13:40:18 +10:00
Dion Moult 8338cc72db New styles on sequencing gantt chart to show dependency arrows, critical path, task groups, and milestones 2021-06-02 17:31:55 +10:00
Dion Moult 67b636f5fb New API usecase to recalculate schedule times, including:
* Critical path
* Total float
* Early start
* Early finish
* Late start
* Late finish
2021-06-02 17:30:05 +10:00
Jesusbill 3a754c8ecc fix of the fix n add_pset 2021-06-01 16:33:47 +02:00
Dion Moult d64d7cf27a Dumb slabs now roundtrip the Blender solidify modifier 2021-05-31 17:43:42 +10:00
Dion Moult 6ff4c95ac7 Dumb slabs now auto update thickness based on layer thickness and type changes. 2021-05-31 15:27:22 +10:00
Dion Moult 0b6af5f848 Experimental usecase serialisation function, in preparation for client-server systems and version control. 2021-05-31 14:18:24 +10:00
Dion Moult 3ded4dff5f Parametric engine is now registered on load, so you don't need to manually activate it 2021-05-31 14:17:33 +10:00
Dion Moult 452631b997 Updating either a duration, start, or finish date for a task now auto-updates the other attribute 2021-05-31 12:42:02 +10:00
Dion Moult c041dd13ad Fix bug where users saw a scary error message when exporting with no IFC data 2021-05-31 09:19:04 +10:00
Jesusbill 994ccb94f4 fix bug in adding psets for materials 2021-05-30 13:32:59 +02:00
Dion Moult c7ba643ef7 Fix material unassignment greedy purging bug, and auto assign usages of typed layer / profile sets. 2021-05-30 21:03:50 +10:00
Dion Moult 0b6f3113a7 Changing types of a dumb wall now auto updates their wall thicknesses 2021-05-30 21:03:50 +10:00
s-leger 730efcae49 Bugfix Set changed size during iteration (#1506)
Fix for remove  RuntimeError: Set changed size during iteration
Add docstring
2021-05-29 22:03:00 +10:00
Dion Moult 62b297757e Dumb slabs now are marked as parametric and inherit thickness from the slab type 2021-05-29 14:59:02 +10:00
s-leger 0533a00631 Allow global listeners (#1500)
* Allow global listeners

Add "global" listeners when ifc_file is None, listeners are run whatever the file.
This way allow to register listeners before a file exists, and "global" listeners.

Add listener returns the ifc_key
Remove listeners allow optional filtering by usecase_path / ifc_key.usecase_path.

* Update __init__.py

Fix was only removing the first listener found.
2021-05-29 09:06:38 +10:00
Dion Moult 7fe2e93dea Fix #1491. Bug where drawings couldn't have a slash in their names. 2021-05-28 17:26:00 +10:00
Dion Moult acc9bd736f Fix #1048. Switching drawings now works when in local view. 2021-05-28 16:09:42 +10:00
Dion Moult 478612b6ae Fix #976. Bug where project data dir was not very customisable without running into issues. 2021-05-28 15:58:19 +10:00
Dion Moult a24d683613 Fix #1396. New walls are selected when created. 2021-05-28 14:50:36 +10:00
Dion Moult 0b5d7cdbf3 Fix #1455. Bump IfcOpenShell. 2021-05-28 14:36:48 +10:00
Dion Moult b8e20ec1a5 Reimplement raster underlays when generating drawings. See #1153. 2021-05-28 14:33:46 +10:00
Dion Moult 1cd243f13c Fix #1462. Standardise IfcOpenShell API to use Pythonic argument names. 2021-05-28 10:59:39 +10:00
s-leger 0a2adbb632 Implement remove pre / pos / all listeners (#1498)
Remove listeners, passing callback method.
Will not handle file pre_load cleanup / post_load setup here to prevent any blender's dependancy.
2021-05-28 10:07:56 +10:00
Dion Moult 8882c99455 Implement editing IFC header metadata. See #747. 2021-05-28 09:50:55 +10:00
Dion Moult 7890ded698 Fix #1429. MVDs now reflect the full DesignTransferView by default. 2021-05-28 09:49:57 +10:00
bosonprojets 7d2d9b2bcf purging of cost and task inverse relationships in ifcopenshell.api, and amendement to cost item values editing UX in BlenderBIM 2021-05-28 00:24:01 +01:00
s-leger 75ae501ff9 Evaluate coplanar vertex in faces (#1489)
The idea is to only triangulate non co-planar faces when required.

Timings for evaluation with matrix based algo.
IfcRoof/Roof found 3927 of 116178 polygons: 0.366638 sec
IfcWall/Wall.012 found 0 of 6 polygons: 0.000046 sec

So should be acceptable for common use cases
But with single precision in blender we may still be over occ precision with a 1e-6 epsilon.
2021-05-28 08:57:22 +10:00
luz paz 880fbe9533 Fix misc. source comment typos
Found via `codespell`
2021-05-27 09:40:06 +02:00
Dion Moult c2684f4fbf New split wall tool 2021-05-27 16:33:34 +10:00
Dion Moult afdc1e6679 New feature to export point lights (IfcLightSourcePositional) into IFC 2021-05-27 13:55:44 +10:00
Dion Moult 8935a17844 Really barebones UI to list space boundaries. 2021-05-27 13:54:18 +10:00
Dion Moult 93c59e81c6 Minor code review for BCF XML 3.0 implementation with TestPrab 2021-05-26 18:00:21 +10:00
Dion Moult d423274e62 New wall flip tool with hotkey. Wall aligner now allows flipped walls. 2021-05-26 17:20:34 +10:00
Dion Moult 7958dd837b New wall tool with hotkeys for rapid wall editing 2021-05-26 15:45:45 +10:00
Long e42a638220 implement DatePicker, visualisation time demo (#1488) 2021-05-26 14:59:20 +10:00
Dion Moult f5f314a58f Dumb walls now auto update thicknesses if the wall layer thickness changes 2021-05-26 11:36:06 +10:00
Dion Moult 96465bed8f Code review for BCFXML v3 Markup.xsd implementation with TestPrab 2021-05-25 18:44:11 +10:00
Prabhat Singh 41bdeea981 Added Script for V3 bcfxml (#1486)
* Added Module Loader

* Added Project and Version script for v3

* Cleaning the code

* optional fix

* Added markup.xsd in v3 bcfxml

* Completed Vision.xsd and Markup.xsd

Co-authored-by: Dion Moult <dion@thinkmoult.com>
2021-05-25 17:22:01 +10:00
Dion Moult 14e0910e59 Purge old dumb wall. Bye bye! 2021-05-25 13:39:55 +10:00
Dion Moult e0ba1c2183 Total layer set thickness is now calculated and shown in the UI 2021-05-25 13:25:05 +10:00
Dion Moult a13b71abf0 Layer set usages now inherit layer sets from the type. Walls now support material layer set usages. Also minor fixes, thanks s-leger! 2021-05-25 13:10:26 +10:00
Dion Moult 32fb359ee2 Copying objects in Blender now auto copy them in IFC too. See #1360. Thanks s-leger! 2021-05-25 11:07:56 +10:00
s-leger 61661fe6f3 implement missing create curve 2d method (#1484) 2021-05-24 23:14:26 +10:00
Dion Moult 92054b1999 Fix bug where objects created with the same name fail to sync geometry on export 2021-05-24 21:22:50 +10:00
Dion Moult 22d9bce591 Dumb walls now automatically generate wall axis upon update 2021-05-24 20:56:47 +10:00
Dion Moult bfe9213f67 Dumb walls now auto calculate length, width, height, and simple volume quantities when being updated 2021-05-24 19:28:47 +10:00
Dion Moult 1abf854f01 New formwork area calculation feature 2021-05-24 18:56:20 +10:00
Dion Moult 686b2a187d New wall align tool (align exterior, interior, or centerline) 2021-05-24 17:29:32 +10:00
Dion Moult 7c2764c98a General bugfix and stability improvements to WIP wall join tool 2021-05-24 17:03:55 +10:00
Dion Moult f73a3a8f79 Fix bug where you were unable to delete certain objects. See #1483. 2021-05-24 13:51:51 +10:00
s-leger b9723c08e4 Ensure polygons are always coplanar (#1482)
* Ensure polygons are always coplanar

fix #1481
May adopt a bmesh approach instead ?

* Ensure polygons are coplanar

The bmesh version is lot faster than modifier one
2021-05-24 08:19:39 +10:00
Thomas Krijnen 1e80e67df5 #1472 various considerations for handling of non-manifold elements with openings 2021-05-23 16:26:34 +02:00
Thomas Krijnen 3e8c10b775 first rough partial draft of alignment handling in python 2021-05-23 09:48:47 +02:00
Thomas Krijnen fd9f209a19 Additional validity check with log level debug 2021-05-23 09:36:31 +02:00
Dion Moult 6b72f7322b Creating walls from cursor now auto-align perpendicular to the wall the cursor is at 2021-05-23 11:48:20 +10:00
Dion Moult 9b0d9c1f91 Stroke-generated walls now auto-join likely butt joints and t-junctions 2021-05-23 11:09:56 +10:00
Dion Moult 26b699be7c You can now join walls in bulk 2021-05-23 10:56:34 +10:00
Dion Moult 294eba24c4 You can now unjoin walls 2021-05-23 09:32:39 +10:00
Dion Moult 09094c090c New sketch wall tool, and join wall tool (T-junction, butt joint, and mitre joints) 2021-05-22 15:39:28 +10:00
ahladik a0dad0802e Fix to IfcHierarchyHelper.cpp (#1476) 2021-05-21 13:26:40 +02:00
Dion Moult 97244d1919 Revisiting dumb walls - typed dumb walls now auto derive wall thickness from the type material 2021-05-20 12:53:40 +10:00
Dion Moult 1541cbbbb8 Fix bug where sometimes active object edits would not get synced correctly. 2021-05-19 21:05:03 +10:00
Dion Moult 82692bb88d Adding a new type instance (like furniture, walls) now snaps it to the bottom of the storey you're working on. Nice. 2021-05-19 17:59:13 +10:00
Dion Moult 77a97233c5 New authoring panel for adding instances and fix bug where new cube instances were missing a face 2021-05-19 16:24:34 +10:00
Dion Moult 4214a449a8 Only show search UI if an IFC model is loaded. 2021-05-19 14:46:02 +10:00
Dion Moult b650c045f5 Simple support for API listeners for parametric IFC authoring - editing any parameter can now trigger events from a "parametric engine" which synchronises other related geometry and attributes in the IFC dataset. Implement auto box generation as a first demo. 2021-05-19 14:45:47 +10:00
Prabhat Singh 851ab07192 Added Project and Version script for v3 bcfxml.py (#1473)
* Added Project and Version script for v3

* Removed Redundant part

* Optional Element Fixes
2021-05-19 08:40:28 +10:00
civilx64 20a36c39fc finish implementing clothoid transition 2021-05-18 15:46:55 +02:00
civilx64 38544e2ba9 develop calcs for biquadratic parabola and clothoid curves 2021-05-18 15:46:55 +02:00
civilx64 41a88bcd4d create implementation of IfcTransitionCurveSegment2D 2021-05-18 15:46:55 +02:00
Dion Moult 966850c54a Minor fix 2021-05-18 20:02:56 +10:00
Dion Moult 319fe13f9e Minor refactor 2021-05-18 17:48:44 +10:00
Dion Moult 0d5110b7af Fix bug where you were unable to remove a representation 2021-05-18 16:42:41 +10:00
Dion Moult de873461e1 Fix bug where IFC2X3 files did not correctly generate 2D representations 2021-05-18 16:41:55 +10:00
Dion Moult cbce3cbec0 Assigning a representation to a product type now syncs mapped representations of all of its typed products. 2021-05-18 16:41:30 +10:00
Dion Moult 0822d1d377 Fix crash when switching representations for a type products. 2021-05-18 16:29:19 +10:00
Dion Moult e6bf32b832 Fix potential instability when assigning types and synchronising Blender geometry 2021-05-18 14:36:12 +10:00
Dion Moult 30c31d694a Purge OCC dependency and old drawing system. See #1153. 2021-05-18 10:44:33 +10:00
s-leger 65c2f9a038 Add support for rel_void / rel_fill / rel_project relative placement when editing object placements(#1471)
* Add support for rel_void / rel_fill

* Fix relationships, and add support for projection elements

Co-authored-by: Dion Moult <dion@thinkmoult.com>
2021-05-18 09:46:15 +10:00
Dion Moult 2d42846fa3 Basic reimplementation of the 2D representation support without PythonOCC. See #1153. 2021-05-17 21:52:23 +10:00
Dion Moult d3bb895c9d No new functionality. Just throwing all drawing code in the drawing module. See #1153. 2021-05-17 15:15:25 +10:00
Dion Moult aeead1c011 Purge old swept solid system. No longer relevant in the land of native IFC. 2021-05-17 14:24:10 +10:00
Dion Moult ba742d5976 Fix #1465. Bump BCF to new namespace. 2021-05-17 14:02:29 +10:00
s-leger 27750e0caf fix is_native_swept_disk_solid logick (#1469)
* Update import_ifc.py

Ensure that at least a raw representation exists before checking for type.

* Update import_ifc.py
2021-05-17 10:02:41 +10:00
s-leger 5b02d3214d Unlink only from relevant collections (#1467)
Objects may be linked to non ifc related collections, so keep those links whatever.
2021-05-17 09:35:14 +10:00
Prabhat Singh 0f55a98b2f Added Module Loader (#1468) 2021-05-17 09:34:31 +10:00
Laurens Oostwegel 13209b6d03 Create mapping to IFC for all CityJSON object types & semantic surfaces 2021-05-16 18:55:00 +02:00
Laurens Oostwegel 411855495e support for CityJSON MultiSurface, CompositeSurface & interior faces for cityjson2ifc convert 2021-05-16 18:16:59 +02:00
Laurens Oostwegel 79ebbb82ef Minor fix in previous commit regarding writing of ifc file 2021-05-16 15:46:31 +02:00
LaurensJN 647a96a655 Update README.md of ifccityjson 2021-05-16 15:36:58 +02:00
Laurens Oostwegel 66d4665498 Instead of using a template ifc file, ifccityjson now uses ifcopenshell.api to create a new file for conversion 2021-05-16 15:32:35 +02:00
Dion Moult 537efdcb53 Continue refactor, and provide user options to regenerate vector base layer / annotation layer of drawings. See #1153. 2021-05-16 23:13:27 +10:00
Dion Moult f2352e9a17 Old annotation systems now creates IFC elements again. Also text and misc annotations now work again with new system. See #1153. 2021-05-16 18:26:48 +10:00
Dion Moult e0b3a87fdb You can now select all products that are part of a group 2021-05-16 11:24:51 +10:00
s-leger ff06f452d5 disable boolean for mesh evaluation (#1466)
Disabling boolean modifier on screen instead of remove / add is enough to get proper mesh representation.
2021-05-16 10:20:56 +10:00
Dion Moult 5aec0718f4 IFC drawings are now roundtrip and are imported as Blender camera objects. See #1153. 2021-05-15 18:05:15 +10:00
Dion Moult fdcafa98b3 Continued grid bugfixing, especially related to export-time syncing. 2021-05-15 12:06:58 +10:00
bosonprojets 1776b19c07 Further fix to task_time durations editing 2021-05-15 02:04:43 +00:00
bosonprojets e1d1dca518 Minor fix to editing task durations 2021-05-14 23:25:54 +00:00
bosonprojets e8ab4dcc14 Improved UX for editing task durations 2021-05-14 23:04:51 +00:00
Dion Moult 41443d1a64 Fix bunch of bugs related to grid name syncing, axis deleting, copy axes and grid deletion 2021-05-14 20:19:30 +10:00
Dion Moult 13c642c6ab A little progress on refactoring the old drawing module into an IfcConvert based system. See #1153. 2021-05-14 17:49:24 +10:00
bosonprojets c40f07f9b5 Minor fix for editing Work Schedule Attributes 2021-05-14 07:40:54 +00:00
Dion Moult ae7d123aa2 Qto module now has various qto methods you can do to bulk populate quantities 2021-05-14 15:13:40 +10:00
Dion Moult 7c6e7996ff Improve the costing UI to format numbers properly and not take up so much space for cost components 2021-05-14 15:13:40 +10:00
ahladik 6ec23faf26 A few fixes of types and a typo (#1464) 2021-05-13 21:11:19 +02:00
Thomas Krijnen 81ad689478 #841 missing return value 2021-05-13 10:09:09 +02:00
Dion Moult b3c918f15f Implement load management and assigning activities with named loads. Thanks Jesusbill! 2021-05-13 18:04:50 +10:00
Dion Moult de7ddd9b97 You can now filter products assigned to a cost item or schedule 2021-05-13 15:54:26 +10:00
Dion Moult 66862a6cba You can now copy cost values from one cost item to another 2021-05-13 15:11:45 +10:00
Dion Moult 4e014d1a10 New utility functions for copying and deep copying IFC elements 2021-05-13 15:11:18 +10:00
Dion Moult 3b33977acc Linked cost item products now auto update cost quantities upon assignment and unassignment, with user friendly quantity type selection dropdown 2021-05-13 14:25:05 +10:00
Dion Moult 5334d2b754 Refactor pset module to follow the same design patterns as other modules, in preparation for complex property / quantity handling in the future 2021-05-13 11:55:25 +10:00
Jesusbill 75ed4dcdd8 you can now edit a structural connection coordinate system 2021-05-12 17:43:07 +02:00
Prabhat Singh 0276c8271e Prabhat gsoc (#1463)
* Added Pipfile and Pipfile.lock in gitignore

* Added File Structure for v3

* Add files via upload
2021-05-12 22:22:12 +10:00
Dion Moult beb813cb21 Fix bug where related openings are not deleted when you delete a product 2021-05-12 22:20:58 +10:00
Dion Moult 35ceba2e88 Fix #1462. 2021-05-12 19:12:46 +10:00
Thomas Krijnen 64b8eee077 #1461 only check for numbers on basename 2021-05-12 11:04:28 +02:00
Thomas Krijnen 229ba95d4b #1442 no longer create compsolids in extrusions 2021-05-12 09:58:11 +02:00
Thomas Krijnen 3f9e5ca806 respect --orient-shells for tesselated items 2021-05-11 23:09:41 +02:00
Thomas Krijnen cf68cf6f5c new_helper 2021-05-11 22:35:46 +02:00
Thomas Krijnen 34386ea27c Hacky solution to disable schemas to speedup compilation 2021-05-11 22:29:16 +02:00
ahladik ce76d27acc Update Boost download address (#1458) 2021-05-11 14:49:18 +02:00
Dion Moult 353281fa4d Updating a task duration now auto calculates the scheduled finish times based on the working calendar 2021-05-11 11:35:47 +10:00
Thomas Krijnen dd6f593e26 V7_5_2.patch 2021-05-10 21:36:38 +02:00
Dion Moult a9228c0800 Support importing lag times from P6 XML 2021-05-10 21:15:23 +10:00
Thomas Krijnen 5e52abb3cf Merge branch 'v0.6.0' of https://github.com/IfcOpenShell/IfcOpenShell into v0.6.0 2021-05-10 11:47:25 +02:00
Thomas Krijnen 8384235e10 Tree API extensions in wrapper 2021-05-10 11:47:16 +02:00
Dion Moult d98ac8ce89 Implement inherited calendars in the sequencing task tree 2021-05-10 18:40:51 +10:00
Dion Moult 1b4b2a526d Derived durations now calculate working days based on the calendar 2021-05-10 18:34:25 +10:00
Dion Moult b1ed85adda Fix incorrect P6 duration import. Now things finally make sense. 2021-05-10 18:31:49 +10:00
Dion Moult 02bfbf0144 You can now show calendars in the work schedule task tree 2021-05-10 11:34:50 +10:00
Dion Moult 3224c4fc77 New IfcPatch recipe to convert units of an IFC file. See #1247. 2021-05-10 10:39:17 +10:00
Dion Moult 19e67613db New recipe to clean up IFCs that store quantities in properties. 2021-05-10 09:27:31 +10:00
Thomas Krijnen e98771ae09 win build: boost dl dir 2021-05-08 21:51:28 +02:00
Thomas Krijnen 58663769c9 Update boost dl location
https://www.boost.org/users/news/boost_has_moved_downloads_to_jfr.html
2021-05-08 14:15:43 +02:00
Thomas Krijnen 28081f7df8 Fixes for spatial tree on iterator and positive extend param 2021-05-08 13:04:22 +02:00
Dion Moult a80fbdbc1d Implement arithmetic formulas for cost value components 2021-05-08 12:18:27 +10:00
Dion Moult 46bc688f26 Fix bug where empty material slots caused errors 2021-05-08 08:57:33 +10:00
Dion Moult f9b79b4beb Implement add / remove load groups, and half implement structural activities. Thanks Jesusbill and ihabelaghoury 2021-05-07 18:41:03 +10:00
Dion Moult 15927dd501 Refactor cost operator to use helpers 2021-05-07 15:10:45 +10:00
Dion Moult 7919c193b8 Minor improvement to deep purging for removing work schedules 2021-05-07 11:16:05 +10:00
Dion Moult 6492b93c69 Minor fix 2021-05-07 10:58:12 +10:00
Dion Moult 992474fbf2 Minor structural code review 2021-05-06 18:24:13 +10:00
Dion Moult 8a3e023408 Fix unfinished time/date/duration handling in edit task time, work plan, work schedule, and finally refactor attribute i/o like I promised 2021-05-06 17:14:28 +10:00
Jesusbill a13b257452 add point orientation - wip 2021-05-06 08:43:12 +02:00
Jesusbill 3f9842e358 extend structural api data for loads 2021-05-06 08:32:49 +02:00
bosonprojets 764b880ea8 Fix MSProject relationship_id issue at import 2021-05-06 06:19:40 +00:00
bosonprojets a10f4c070a Fix MSProject calendar day mapping at import, and add relationship type display in blenderBIM 2021-05-06 06:08:21 +00:00
Dion Moult 114214e2a1 Implement natural sorting for task trees, and support nested tasks in MS Project XML imports 2021-05-06 14:23:20 +10:00
bosonprojets d19d4fca3e FEature to import Microsoft Project .xml Schedules and convert to IFC schema 2021-05-06 02:40:04 +00:00
Jesusbill a031bffe6c you can now add, edit and remove structural load cases 2021-05-05 14:22:18 +02:00
Dion Moult 6ff792d75c Whoops 2021-05-05 18:43:12 +10:00
Dion Moult 6b3393c003 You can now generate construction sequence animations to visualise within a date range 2021-05-05 18:36:28 +10:00
Dion Moult da5a2625a9 New feature to visualise construction progress at a particular date 2021-05-05 17:07:17 +10:00
Dion Moult a1eb2a1ec3 Fix identification sorting issue of construction sequencing task trees 2021-05-05 12:49:22 +10:00
Dion Moult 403412e088 Support importing activity and calendar assignments from P6 2021-05-05 12:48:48 +10:00
Dion Moult ee46e76340 Derive start and finish dates and elapsed durations where possible in the task tree 2021-05-05 11:59:33 +10:00
Dion Moult 925c40c6ba Fix duration type when importing durations from P6 2021-05-05 11:58:36 +10:00
Dion Moult dea844dcf6 Fix purging when removing tasks 2021-05-05 11:57:47 +10:00
Dion Moult 018d754b2e Fix P6 WBS identification import 2021-05-05 10:16:25 +10:00
Dion Moult a05d017341 Implement adding, editing, and removing time lags to sequence relationships 2021-05-04 20:56:19 +10:00
Dion Moult f63479c823 Code review and use isodate library to handle durations for robustly 2021-05-04 12:52:14 +10:00
bosonprojets 8a8bdeeaa3 Update to ifcopenshell.util.date for handling of IfcDuration with python and import of Planned Durations from P6 .xml exports in blenderBIM 2021-05-04 00:27:53 +00:00
bosonprojets 6b46ff71a6 Implement feature to select all products related to a task in blenderBIM, and the ifcopenshell.api 2021-05-02 01:44:56 +00:00
bosonprojets d73a5c46ea Add support for editing sequence relationship attributes in blenderBIM 2021-05-01 21:45:27 +00:00
ArturTomczak 037261afb9 IDS Add PredefinedType support (#1447)
* fixed typos

changed all 'applicabiliy' to 'applicability'

* added boolean restriction

* Add PredefinedType support

* replace f-string with %

* skip PredefinedType if not IFC4

* code formatting

* check PredefinedType if exists not based on schema

* narrow try-except

* remove debug data
2021-05-01 09:09:35 +02:00
Dion Moult be87b17909 Support importing P6 sequence types and new view sequences UI 2021-04-30 21:34:18 +10:00
atom3 5419d72a6a remove debug data 2021-04-30 11:31:23 +02:00
atom3 9ca1e011e5 narrow try-except 2021-04-30 11:06:36 +02:00
Dion Moult 932a21309f Support assigning work calendars to tasks 2021-04-30 17:40:00 +10:00
atom3 3b806a5466 check PredefinedType if exists not based on schema 2021-04-30 08:41:33 +02:00
Dion Moult 7a1b35f7e2 Support different versions of P6 XML imports 2021-04-30 15:57:42 +10:00
Dion Moult 79dcf809ed Implement support of importing calendars from P6 2021-04-30 14:53:40 +10:00
bosonprojets 2f53e43b90 Added Usecases to assign, edit and unassign Lag Times to the ifcopenshell.api 2021-04-30 02:21:54 +00:00
Dion Moult cdc9180111 Support root-level activities when importing from P6 XML 2021-04-30 12:04:15 +10:00
atom3 a65a34cfe7 code formatting 2021-04-30 00:09:04 +02:00
atom3 2e0d5fa707 skip PredefinedType if not IFC4 2021-04-29 23:41:22 +02:00
atom3 de40049c9c Merge branch 'GSoC#45-IDS-checking' of https://github.com/atomczak/IfcOpenShell into GSoC#45-IDS-checking 2021-04-29 21:33:51 +02:00
atom3 29fa5bdffa replace f-string with % 2021-04-29 21:33:07 +02:00
ArturTomczak d55ad90319 Merge branch 'v0.6.0' into GSoC#45-IDS-checking 2021-04-29 21:05:26 +02:00
atom3 90ece8a058 Add PredefinedType support 2021-04-29 20:46:55 +02:00
Thomas Krijnen a936a5317d #1445 comment 2021-04-29 16:29:31 +02:00
Thomas Krijnen f5e5ede0fa Revert "Fixes IfcUtil::valid_binary_string so it returns false for an empty string"
This reverts commit 2514e93a8d.
2021-04-29 16:28:16 +02:00
Rick Brice 2d1ddea3d0 Fixes conditional logic 2021-04-29 16:27:57 +02:00
Rick Brice df932b90d6 Adds parsing for Ifc4x3_rc2 schema 2021-04-29 16:27:57 +02:00
Rick Brice 2514e93a8d Fixes IfcUtil::valid_binary_string so it returns false for an empty string 2021-04-29 16:27:57 +02:00
Rick Brice 64e6561828 Initalizes list_ with null points 2021-04-29 16:27:57 +02:00
Thomas Krijnen 76685d236c SVG Fixes to text positioning 2021-04-29 16:16:41 +02:00
Dion Moult fd83564af0 Sorry forgot to commit 2021-04-29 19:37:59 +10:00
Dion Moult 37e3a65ffb Decouple pset APIs from blenderbim namespace 2021-04-29 19:35:21 +10:00
Dion Moult e5cf0cde2d Improve time and date support (don't cast to datetime) and support all recurrence types and time periods in work schedules 2021-04-29 17:52:45 +10:00
Thomas Krijnen 78e4478242 Merge branch 'v0.6.0' of https://github.com/IfcOpenShell/IfcOpenShell into v0.6.0 2021-04-29 07:54:15 +02:00
Thomas Krijnen 3497022c78 SVG Also include annotations in non-floorplans 2021-04-29 07:53:05 +02:00
Dion Moult 810d36781c Add support for editing calendar work and exception times and recurring work times 2021-04-29 13:16:47 +10:00
bosonprojets f8eb1c178a Usecases to edit WorkCalendar Attributes in the ifcopenshell.api 2021-04-29 02:43:14 +00:00
Dion Moult b726ba7816 Simplify work calendar UI 2021-04-29 09:55:50 +10:00
bosonprojets 4ab23d231d Usecase to Unassign object aggregation in the ifcopenshell.api 2021-04-28 12:43:14 +00:00
Dion Moult 00694854af UI clean up for work plans and code review for assign schedules to plan 2021-04-28 17:59:57 +10:00
Dion Moult fa52ed6dbc Fix bug where you might have an activity that is not part of any WBS from P6 2021-04-28 16:54:54 +10:00
Dion Moult 319b59a266 Fix bug where sometimes migrating an IFC file schema version can explode in filesize 2021-04-28 16:53:38 +10:00
Dion Moult 357fcded71 Implement OAuth authorization in the bSDD library 2021-04-28 13:27:31 +10:00
bosonprojets c3cc5881df Aggregate WorkSchedules to WorkPlans 2021-04-28 02:17:08 +00:00
atom3 ca34c95057 added boolean restriction 2021-04-28 00:25:08 +02:00
atom3 5078db8b26 fixed typos
changed all 'applicabiliy' to 'applicability'
2021-04-28 00:10:13 +02:00
Dion Moult 56046dd6f9 Cost quantities that are counting that have controlled products now automatically count the number of products for convenience 2021-04-27 14:45:14 +10:00
Dion Moult 9164a7bcc3 Support importing predecessor and successor relationships from P6 2021-04-27 12:30:09 +10:00
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
ArturTomczak 3104743a02 Merge pull request #1 from IfcOpenShell/v0.6.0
Upstream update
2021-04-22 15:35:05 +02: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
Dion Moult f49de02ab7 Minor purge 2021-03-19 11:04:27 +11:00
Dion Moult 8b0a7821b1 You can now view / add / edit / remove structural boundary conditions 2021-03-19 10:55:52 +11:00
Dion Moult 0f365cadbd Experimental code for (currently slow) native faceted brep handling. See #841. 2021-03-18 19:02:54 +11:00
Dion Moult 0bc76b1af7 Reimplement lost feature from refactor - native handling of swept disks (mostly rebar) in Blender during import 2021-03-17 12:39:35 +11:00
Cyril Waechter efce28c053 Fix: get_applicable return empty generator (#1390)
`PsetQto.get_applicable` was returning an empty generator when called
multiple times. Combination on lru_cache and generator seems to lead to
this unwanted behavior.
It now returns a list to keep gains of lru_cache performance.
2021-03-17 09:23:06 +11:00
Dion Moult 071505038d Fix #1388. Extract elements IfcPatch recipe now retains aggregation relationships. 2021-03-16 21:48:17 +11:00
Dion Moult 98938acb26 Fix bug where nested aggregates were not nested in the Blender scene during import 2021-03-16 20:48:12 +11:00
Jesusbill 4817360ecd place structural elements in dedicated collections 2021-03-16 01:05:25 +01:00
Dion Moult b8e3fbba8c You can now import structural point connections. 2021-03-15 21:02:25 +11:00
Dion Moult 49ea774851 Export auto syncs edited meshes if authoring mode enabled. See #1360. 2021-03-15 20:32:47 +11:00
Dion Moult f4f2a3d24e Object placements are now updated on export in case you forgot to sync them in authoring mode. See #1360. 2021-03-15 18:58:32 +11:00
Dion Moult a25ca91ba6 Fix fundamental coordinate placement resolution utility bug. See #688. 2021-03-15 18:58:32 +11:00
Jesusbill fded9b3523 avoid warning for structural analysis entities
regarding spatial hierarchy
2021-03-15 01:41:29 +01:00
Jesusbill 9257f275af Import structural curve connections + small fixes 2021-03-15 01:39:35 +01:00
Priit Laes 40039e65df Ivccsv fixes (#1385)
* ifccsv: Fix reference to invalid variable

* ifccsv: Remove unused import
2021-03-15 08:20:51 +11:00
Priit Laes dbce9ec049 actions: ci: Skip installing recommended packages
Not much, but still small improvement:
6 upgraded, 138 newly installed, 0 to remove and 43 not upgraded.
6 upgraded, 135 newly installed, 0 to remove and 43 not upgraded.
2021-03-14 16:42:09 +01:00
Priit Laes b9c5c28c54 actions: ci: Use single apt install call
Reduces time for this step ~10%
2021-03-14 16:42:09 +01:00
Priit Laes 184a189f2a actions: ci: Enable ccache
Reduces build time with full cache from 20 to ~3 minutes.
2021-03-14 16:42:09 +01:00
Thomas Krijnen 37f549927d Update ci.yml 2021-03-14 12:29:05 +01:00
Thomas Krijnen a5fe3e77e7 Update ci.yml 2021-03-14 11:59:36 +01:00
Dion Moult 708da97bbd New IFC authoring mode - when enabled, exporting the IFC auto saves the Blender file too. See #1360. 2021-03-14 20:38:25 +11:00
Thomas Krijnen f80792091f #1290 Consider dimensionality in geometricset processing 2021-03-14 10:22:43 +01:00
Dion Moult a1e34b0acb Fix bug on Windows where sometimes, the body representation wouldn't be loaded by default. See #1290. 2021-03-14 19:34:18 +11:00
Thomas Krijnen e618232e70 clone --recursive in build-all.py 2021-03-14 09:32:38 +01:00
Dion Moult 922fc2beda Build fix 2021-03-14 18:04:57 +11:00
Dion Moult 4564ee3476 Fix bug where you couldn't add layers to a fresh project 2021-03-14 10:27:12 +11:00
Priit Laes 510dca1aca build: actions: Don't hardcode parallel builds to 10 in release workflow
And convert from deprecated `backtick` syntax to POSIX-compatible $(...)
2021-03-13 15:00:46 +01:00
Dion Moult 5604db8072 The IFC Debug inspector now also shows inverse references, great for debugging IFC2X3 or style relationships 2021-03-13 22:21:44 +11:00
Thomas Krijnen 5961253c29 #1376 remove_deep_batched() 2021-03-13 12:16:40 +01:00
Thomas Krijnen 624e296d24 #1368 fix layerset folding: check length thichkness ratio 2021-03-13 10:05:33 +01:00
Dion Moult 6ad605fbeb Minor fix 2021-03-13 10:11:17 +11:00
Dion Moult 797a5ec1a0 You can now delete objects in bulk 2021-03-13 09:58:22 +11:00
Dion Moult d56869d341 Create build on every commit to see what happens 2021-03-12 10:15:01 +11:00
Dion Moult 405e8bc5aa You can now automagically create arbitrary polygonal profiles with voids from meshes 2021-03-11 21:57:47 +11:00
Dion Moult b607242339 You can now add/edit/remove/assign/unassign structural analysis models 2021-03-11 10:46:55 +11:00
Dion Moult f57aa77ac2 You can now import structural curve members (again) 2021-03-10 21:18:55 +11:00
Dion Moult 01cc1887e0 Readd support for creating structural curves representations (vertex still WIP) 2021-03-10 21:10:00 +11:00
Dion Moult 003441a451 Add support for nano and micro meter units 2021-03-10 20:59:18 +11:00
Dion Moult 4b64f09aaa Fix #1370. Bug where loading pset templates makes it unable to install on Windows. 2021-03-10 20:28:08 +11:00
Dion Moult 4afcca8550 Adding drawings now creates groups and psets. Also very basic operator added to call IfcConvert from UI. See #1153. 2021-03-10 17:48:06 +11:00
Dion Moult 1a7ea1ea05 Provide upgrade / uninstall instructions to prevent users hitting that remove error message 2021-03-10 17:48:06 +11:00
Thomas Krijnen 5616a6ede5 SVG Ugh, fix transform syntax 2021-03-09 20:40:18 +01:00
Thomas Krijnen 28fa774a9a SVG --draw-storey-heights=left option 2021-03-09 16:11:42 +01:00
Thomas Krijnen 52d4fa4781 #1363 todo note 2021-03-09 09:57:18 +01:00
Thomas Krijnen 9258dc3104 IFC2X3 IfcAsymmetricIShapeProfileDef fillet radii 2021-03-09 09:55:40 +01:00
Thomas Krijnen 469fb9c204 SVG small fix 2021-03-09 09:52:47 +01:00
ahladik f7175a67c8 Shorten Boost pathnames and fix IfcParse::IfcSIPrefixToValue() (#1364) 2021-03-09 09:11:06 +01:00
Dion Moult ebfaeafeee You can now bulk assign / unassign construction types 2021-03-09 14:25:04 +11:00
Dion Moult 1f68c3f612 Fix bug where you could not assign a related window or door type. 2021-03-09 12:51:18 +11:00
Dion Moult aceb2140ad Fix #1361. You can now unlink objects and styles of elements that you've lost the IFC of to recover them. 2021-03-09 12:50:50 +11:00
Dion Moult 310d13ac75 Unbreak macos builds 2021-03-09 08:36:15 +11:00
Krande bc2ac6b31a Automated Blenderbim releases using github actions ci (#1354)
* Initial commit for suggested blenderbim github actions setup

* Changed the github repo address from fork to main repo for clarity

* Include python version info to appear on asset name. Consider using additional matrix loop on python versions 37 and 39. Currently hardcoded py37 for testing

* Cleanup of "python_fcl" files in dist/working before compiling BIMTester

* Loop over python 3.7 and 3.9 for all platforms and build 6 releases

* Set the correct repository name in the trigger. Also make the trigger only run when "[release]" is added in the commit message.
2021-03-09 08:11:47 +11:00
Thomas Krijnen 059c405652 Github actions nproc 2021-03-08 15:20:59 +01:00
Priit Laes 28d168b96c build: cmake: Improve OCC_LIBRARY_DIR detection 2021-03-08 14:53:07 +01:00
Priit Laes e4640df6f0 build: cmake: Rework OpenCascade header directory lookup 2021-03-08 14:53:07 +01:00
Thomas Krijnen 5c3642e1d1 Use BUILD_PACKAGE option 2021-03-08 14:52:57 +01:00
Thomas Krijnen 615f567ebf username 2021-03-08 14:52:57 +01:00
Thomas Krijnen a56ca661e8 BUILD_PACKAGE option 2021-03-08 14:52:57 +01:00
Thomas Krijnen 8ee9835e27 Change repo name 2021-03-08 14:52:57 +01:00
Christophe Prud'homme b26147c1c7 Release V 0.6.0 alpha 3 2021-03-08 14:52:57 +01:00
Christophe Prud'homme c3847ff38a add runtime depends 2021-03-08 14:52:57 +01:00
Christophe Prud'homme 8299505672 Release V 0.6.0 alpha.2 2021-03-08 14:52:57 +01:00
Christophe Prud'homme 0db743d3f7 Release V0.6.0 alpha 2
install in standard python dir
bump up version
2021-03-08 14:52:57 +01:00
Christophe Prud'homme e7558d0008 Release v0.6.0 alpha.1
add EXTRA_VERSION to set prerelease (to be set to null string for final release)

add release workflow
2021-03-08 14:52:57 +01:00
Christophe Prud'homme c9f40126c3 fix python package dir
@aothms the selection of the directory needs to fixed but the current choice is reasonnable
2021-03-08 14:52:57 +01:00
Christophe Prud'homme b0554e71d5 use GNUInstallDirs to define properly the installation directories
fix typo in name LIBARY -> LIBRARY
2021-03-08 14:52:57 +01:00
Christophe Prud'homme 1bed25408f up ci 2021-03-08 14:52:57 +01:00
Christophe Prud'homme 603a034ac6 use host ubuntu 20.04 2021-03-08 14:52:57 +01:00
Christophe Prud'homme 6f802ed770 prepare for docker image of ifcopenshell 2021-03-08 14:52:57 +01:00
Christophe Prud'homme c1b9062fa4 add package using cpack and install properly 2021-03-08 14:52:57 +01:00
Christophe Prud'homme e4f591babf add gh action 2021-03-08 14:52:57 +01:00
Thomas Krijnen e9c84d1c11 SVG Rotate dimension text 2021-03-08 14:19:07 +01:00
Dion Moult e62b7e94a4 Fix #1339. You can now search case insensitively for pset, prop and attribute names 2021-03-08 16:42:47 +11:00
Dion Moult e89d540236 Fix bug where assigning a new constituent or profile was borked. Also you can now reorder material lists and constituent set items. 2021-03-08 16:19:26 +11:00
Dion Moult fa23f64e9f Fix #1344. You can now reorder items in a material layer set 2021-03-08 14:20:43 +11:00
Dion Moult cfaa90b1fb Fix bug where material / style UI was showing without a loaded IFC 2021-03-08 12:11:29 +11:00
Dion Moult dc909f1fc6 Add support for adding material layer set usages to elements 2021-03-08 08:41:06 +11:00
Thomas Krijnen 30fc2ad580 SVG: Fix storey elevations 2021-03-07 12:07:40 +01:00
Thomas Krijnen c542fb2480 explicit cast 2021-03-07 10:22:22 +01:00
Dion Moult 25717d002f Fix bug where nested aggregates failed to import 2021-03-07 14:16:08 +11:00
Dion Moult 0181bd7e56 Add support for square and cubic decimetre units 2021-03-07 10:51:30 +11:00
Dion Moult 71c6bba36d Third time lucky. 2021-03-06 14:42:30 +11:00
Dion Moult eb539666d9 Second time lucky. 2021-03-06 14:02:22 +11:00
Dion Moult e43eec6203 Add support for Python 3.9 2021-03-06 09:36:42 +11:00
Thomas Krijnen 4ca64ac42e SVG: preliminary --draw-storey-heights 2021-03-05 21:50:32 +01:00
Priit Laes f51e0db7fe build: Check for libxml2
Fixes issue #1298

Signed-off-by: Priit Laes <plaes@plaes.org>
2021-03-05 21:36:18 +01:00
Thomas Krijnen b1e8e0a968 Update vs-cfg.cmd 2021-03-05 20:58:34 +01:00
Dion Moult be66a98cfa Implement support for showing IfcMaterialLayerSetUsage material types 2021-03-05 18:27:19 +11:00
Dion Moult 76487ea3bf Deprecate the "allow non-element aggregates" workaround for prostructures as the new aggregate system should make it obsolete. 2021-03-05 16:42:39 +11:00
Dion Moult 13fe011c75 Fix bug where sometimes cartesian point offsets wouldn't get detected correctly for poorly geolocated files 2021-03-05 16:38:51 +11:00
Dion Moult 266a341c6b You can now assign/unassign products to groups. Part of #1153 roadmap. 2021-03-05 15:37:40 +11:00
Dion Moult a5bcbe046f You can now view, add, edit, and remove IFC groups. Useful, but also needed for #1153. 2021-03-05 14:06:51 +11:00
Dion Moult ab057e3eff Fix bug where you couldn't do import profiling on Windows. Thanks erab! 2021-03-05 10:02:29 +11:00
Thomas Krijnen 6aa0651190 #1153 support IfcAnnotation with Box as drawing view 2021-03-04 14:39:32 +01:00
Thomas Krijnen 0f296c486f todo note 2021-03-04 14:38:36 +01:00
Dion Moult 29f64f7c11 Drawing cameras now save IfcBlock representations in IFC. See #1153. 2021-03-04 22:04:40 +11:00
Dion Moult d2d4a1b976 Bundle IfcConvert with the BlenderBIM Add-on. See #1153. 2021-03-04 20:54:31 +11:00
Dion Moult 249ff95920 Minor fix 2021-03-04 17:26:39 +11:00
Dion Moult 4c8d12d978 Fix bug where you could add psets of the same name multiple times 2021-03-04 17:02:05 +11:00
Dion Moult 04d02feb86 Null automatically toggles when you set a property in a pset now 2021-03-04 14:58:26 +11:00
Dion Moult e6700ea29b Fix bug where existing blender offsets weren't used when importing two offset models consecutively 2021-03-04 11:58:07 +11:00
Dion Moult 9c7c3d7078 Users can now toggle hiding of null / empty properties in case they are powerusers 2021-03-03 21:50:13 +11:00
Dion Moult 0d3175c6ef Improve pset UI to show if no properties are set 2021-03-03 21:42:00 +11:00
Dion Moult 04abadac22 You can now reassign classes in bulk 2021-03-03 21:16:51 +11:00
Jesusbill 0b39226869 update readme and changelog 2021-03-01 23:39:13 +01:00
Jesusbill 9e31991e75 add "bonded" case for mesh and run 2021-03-01 23:22:55 +01:00
QwiglyDee b40bd681b1 fixed disappearing extrusion gizmo 2021-03-01 23:46:52 +07:00
QwiglyDee 63384a730b WIP gizmo for parametreic extrusion, see #1324 2021-03-01 18:42:28 +07:00
Dion Moult b9f81b8014 Minor packaging fix 2021-03-01 10:57:48 +11:00
Dion Moult c34f357301 Minor fix 2021-03-01 10:52:21 +11:00
Dion Moult beb58b89fe Basic module for integration with Augin. Nice. 2021-03-01 10:52:09 +11:00
Thomas Krijnen 97df037c93 Reformat ids.py 2021-02-28 09:34:35 +01:00
Thomas Krijnen a7d8bffca5 Merge branch 'v0.6.0' of https://github.com/IfcOpenShell/IfcOpenShell into v0.6.0 2021-02-28 09:31:38 +01:00
Thomas Krijnen b5f58dc113 #1349 result formatting 2021-02-28 09:31:21 +01:00
Dion Moult 4e889b75d7 BIMTester can now accept both IDS .xml as well as Gherkin .feature files and generate HTML reports 2021-02-28 14:35:21 +11:00
Dion Moult 36cfb0d3f5 Meshes can now be automagically converted into circular and arbitrary profile extrusions (swept solids) 2021-02-28 12:04:30 +11:00
Dion Moult 5921bcb63d You can now automagically convert meshes into rectangular solid extrusions 2021-02-27 21:15:18 +11:00
Thomas Krijnen 23ad1a8005 Preliminary bSI IDS implementation 2021-02-27 11:04:45 +01:00
Thomas Krijnen f257aef8f4 #1348 special case profiledefwithvoids, needs more work 2021-02-27 10:58:22 +01:00
Thomas Krijnen 3eacd809b0 validate() docstring 2021-02-24 14:06:48 +01:00
Dion Moult e1b90ed3b2 Rebuild pset template UI to be less confusing and match the UX paradigm of other modules 2021-02-24 21:07:27 +11:00
Dion Moult 28c2d7248e Fix bug in where IfcCountMeasure and similar properties were not editable 2021-02-24 16:21:27 +11:00
Dion Moult 3bee746f49 Optimise aggregate creation on large file imports 2021-02-24 13:49:06 +11:00
Dion Moult eeb27bb1c7 Fix regression in last release where the file was loaded twice during import, which led to increased import times 2021-02-24 12:51:50 +11:00
Dion Moult d50c89abee Fix #1337. Changing the name of a Blender object now also auto changes its IFC name. 2021-02-23 21:15:27 +11:00
Dion Moult 15cf3f5afd Fix bug where undo operations may invalidate an ID map and break everything 2021-02-23 20:01:00 +11:00
Dion Moult 1e22dbca1e Meshes are auto updated in the IFC as well when edit mode is toggled now. 2021-02-23 19:19:06 +11:00
Dion Moult 28fbf0ea99 Minor fix 2021-02-23 18:55:42 +11:00
Dion Moult d874a0089d Add documentation for ifcpatch. 2021-02-22 22:04:23 +11:00
Dion Moult 441d842f67 You can now easily get and set the cursor location for local<->global coordinate conversions 2021-02-22 18:27:57 +11:00
Dion Moult 667ac307c3 Fix bug where cursor was not placed in the correct unit after a global->local coordinate conversion 2021-02-22 18:16:09 +11:00
Dion Moult c81031819c Fix bug where blender offsets were not considered in local<->global coordinate conversion 2021-02-22 18:15:22 +11:00
Dion Moult 7cbb87284c Fix bug where selecting high polygon meshes for debugging fails 2021-02-22 16:52:10 +11:00
RobinVanTol d0a9edd186 Update occ_utils.py 2021-02-21 16:04:42 +01:00
Dion Moult 425379aaad Bump IfcOpenShell build 2021-02-21 22:11:05 +11:00
Dion Moult 25d86a50c5 Minor fix for previous commit 2021-02-21 11:55:04 +11:00
Dion Moult 0286cd370d Improve UI to change spatial container to prevent accidentally selecting incorrect objects 2021-02-21 11:24:27 +11:00
Dion Moult 0178804feb Fix bug where removing mapped representations leaving you with nothing was a poor user experience 2021-02-20 18:03:03 +11:00
Dion Moult 1892e9cc86 Fix bug where copying a class did not enforce mapped representations 2021-02-20 17:23:58 +11:00
Dion Moult 1b46f5c83a Fix bug where you couldn't edit the geometry of a grid axis 2021-02-20 17:13:36 +11:00
Dion Moult 3a0edab8c1 Blender now helps prevent saving Blender without having an exported IFC. See #1323. 2021-02-20 15:15:44 +11:00
Dion Moult 7032ee9f11 STEP IDs are now shown for non-rooted elements, like grid axes. 2021-02-20 13:53:11 +11:00
Dion Moult fd19b2d730 Fix #1312. Allow null ownership histories in IFC4. 2021-02-20 10:26:58 +11:00
Dion Moult a65b9f2ba7 You can now add type instances, and mapped representations are enforced. 2021-02-20 10:11:27 +11:00
Dion Moult 2db4a6f89f Fix bug where you couldn't copy classes without representations. 2021-02-20 09:21:18 +11:00
Thomas Krijnen 517b819504 .bat script typo 2021-02-19 10:43:47 +01:00
Dion Moult a0f79cc0db Fix #1318 - unable to install add-on due to clash with Blender-OSM. 2021-02-18 17:45:06 +11:00
Thomas Krijnen 67666ed70e Attempt at https://github.com/IfcOpenShell/IfcOpenBot/issues/1 2021-02-17 16:06:50 +01:00
Dion Moult 6bcfa5946f New basic UI to view tasks in an IFC file 2021-02-17 22:54:11 +11:00
Dion Moult 52691b7ee5 You can now edit parameters of parametric profile definitions in geometry. 2021-02-17 22:03:41 +11:00
Dion Moult 780ea707b0 Highly experimental script to convert from IFC task trees into a Gantt chart JSON for jsgantt-improved 2021-02-17 21:17:02 +11:00
Dion Moult 7e7cfbdd5f Highly experimental script to convert from P6 XML into an IFC task tree. 2021-02-17 21:16:21 +11:00
Dion Moult ab102176cc IfcOpenShell date util now supports converting to IFC2X3 date entities 2021-02-17 21:12:04 +11:00
Thomas Krijnen d7f289bc8a Update stylesheet 2021-02-16 20:23:36 +01:00
Thomas Krijnen 95a34cb8d0 Update stylesheet 2021-02-16 12:13:59 +01:00
Dion Moult 8369818b88 Fix bug where geolocation was lost if you tried to edit an absolutely positioned file 2021-02-16 20:27:37 +11:00
Dion Moult 5251bdabc6 Fix bug #1315 - regression in the absolute coordinate offset feature where I forgot to convert non-meter units. 2021-02-16 18:50:04 +11:00
Dion Moult 83c33dd5b0 Fix bug where only a light removal occured when removing elements. Now, a full geometry purge is done as well. 2021-02-16 11:12:42 +11:00
Thomas Krijnen bfc695c756 #1311 better error handling during parse for schema and header 2021-02-15 11:11:14 +01:00
Thomas Krijnen 459faec01a #1311 normalize schema name case 2021-02-14 17:39:15 +01:00
Thomas Krijnen 4fb65199b8 Validate: better handling of boxed enum 2021-02-14 17:32:08 +01:00
Thomas Krijnen 08576e28d6 fixed boxed enum validation 2021-02-14 17:23:16 +01:00
Thomas Krijnen b3f581d78d Handle boxed enum creation in Python 2021-02-14 17:22:15 +01:00
Thomas Krijnen 89d6b3d21a Fixes for RC2 changes re IfcPoint and IfcSegment 2021-02-14 17:21:58 +01:00
Thomas Krijnen d2bf2cf46e Pick RC2 definitions where required 2021-02-14 17:21:34 +01:00
Thomas Krijnen 43b2982dbb fix char encoding issue in source 2021-02-14 17:20:55 +01:00
Thomas Krijnen e750a134d0 Add generated rc2 schemas 2021-02-14 17:20:34 +01:00
Thomas Krijnen 6bbbc528fe prep for 4x3_rc2 2021-02-14 12:43:27 +01:00
Thomas Krijnen 7f4a436708 regen schema 2021-02-14 12:43:09 +01:00
Thomas Krijnen e6592ca706 conversion rules for first citizin enum types #1276 2021-02-14 12:19:06 +01:00
Thomas Krijnen cc2138463e Merge branch 'v0.6.0' of https://github.com/IfcOpenShell/IfcOpenShell into v0.6.0 2021-02-14 09:49:03 +01:00
Thomas Krijnen 6de3e55d19 make wrapper import in schema_class optional 2021-02-14 09:43:10 +01:00
Dion Moult 595131e6e8 Fix #1313. Fix bug where you couldn't import two different IFC files in succession in the same Blender file. 2021-02-14 17:27:10 +11:00
Dion Moult 6966da3bdf Switching construction type now has an option to map representations 2021-02-14 16:50:27 +11:00
Thomas Krijnen 4e897a0c21 surface style ids on serialization 2021-02-13 16:03:57 +01:00
Long 73a9015801 ifcpatch make.py (#1319) 2021-02-13 20:23:59 +11:00
Dion Moult 1b2bebecb0 You can now assign types with a smart type filter, without needing to first import types into Blender 2021-02-13 18:29:52 +11:00
Dion Moult e4dee2258d Fix #1300. New IfcOpenShell.util.type module for checking applicable type classes. 2021-02-13 18:27:02 +11:00
Long 7de3eec1b1 make ifcpatch callable from command line (#1317) 2021-02-13 10:06:15 +11:00
rbertucat 20fe48bd6a BuildingSmart IFCRail changes to BIMTester (#1309)
* Add a way to validate the IFC file in BIMTester

* Fix an issue in Report where only the first error was shown

* Fix the usage of Schema in BIMTester

* Add Aggreation steps to BIMTester
2021-02-12 09:34:10 +11:00
Thomas Krijnen 1d6b2dcb37 SVG: IfcAnnotation objectype handling 2021-02-09 14:04:01 +01:00
Thomas Krijnen 45f3e677df Also process wires as part of geometric sets 2021-02-09 14:03:35 +01:00
Jesse Vander Does 65160f9bb4 Removed new line 2021-02-09 13:43:29 +01:00
Jesse Vander Does 333ba08841 Added '--no-wire-intersection-tolerance' option 2021-02-09 13:43:29 +01:00
Jesse Vander Does 2cb3b0817f Updated description 2021-02-09 13:43:29 +01:00
Jesse Vander Does 4ea95960a2 Added '--strict-tolerance' option 2021-02-09 13:43:29 +01:00
Jesse Vander Does 10f4bca5d2 Moved no wire flag inside of wire_intersections method 2021-02-09 13:43:29 +01:00
Jesse Vander Does d4a01618cd Fixed white space 2021-02-09 13:43:29 +01:00
Jesse Vander Does c9c6529978 Added '--no-wire-intersection-check' 2021-02-09 13:43:29 +01:00
Jesse Vander Does 2829a0764b Remove wire intersection check to preserve faces 2021-02-09 13:43:29 +01:00
Dion Moult 76d61cb4d4 Minor fix - map representation now only does that, and nothing more 2021-02-07 22:15:21 +11:00
Dion Moult e9d2a39166 Only IFC files are now shown in the export file dialog for a less confusing user experience 2021-02-07 20:00:26 +11:00
Dion Moult d08f4171e3 Type representations are now always imported (better for authoring), and also import super fast compared to before (before, enabling types being imported would significantly slow down import times) 2021-02-07 19:57:32 +11:00
Dion Moult 23483eafb3 Fix bug where removing a mapped representation did not properly considered all mapped usages 2021-02-07 19:13:36 +11:00
Dion Moult 686ba2536b Fix bug where you could create mapped representations without having a type product 2021-02-07 19:08:34 +11:00
Dion Moult 7d5ac99f34 Fix bug where representations wouldn't be purged correctly if they were part of a layer with many items 2021-02-07 19:05:56 +11:00
Dion Moult 9b8d224aca Minor fix 2021-02-07 13:27:23 +11:00
Dion Moult 8939d93333 Fix bug where product and type representations wouldn't be linked in Blender when importing 2021-02-07 12:53:41 +11:00
Dion Moult 04aad216ba Type representation maps are now shown in the IFC Representations UI panel 2021-02-07 11:44:14 +11:00
aothms 27eb9d340a sphinx theme 2021-02-06 15:24:17 +00:00
aothms fbb53f93d8 Merge branch 'v0.6.0' of https://github.com/IfcOpenshell/IfcOpenShell into v0.6.0 2021-02-06 15:11:00 +00:00
aothms af6aed80da Documentation changes 2021-02-06 15:10:41 +00:00
Dion Moult 92ba412c95 Fix bug where add grids didn't work. See #1284. 2021-02-06 15:24:53 +11:00
Dion Moult 110c382973 Fix ability to create grids which was totally broken 2021-02-06 15:14:38 +11:00
Dion Moult 977df60f05 Fix #1283 - dumb objects now have an assigned class 2021-02-06 08:01:29 +11:00
Thomas Krijnen 539d77b5ad #1247 Boost_NO_BOOST_CMAKE=On 2021-02-05 17:04:37 +01:00
aothms ea34c3de85 Changes to docs 2021-02-05 12:01:57 +00:00
Thomas Krijnen 9374594920 Merge pull request #1297 from IfcOpenShell/docs
Documentation generation by @johltn
2021-02-05 11:57:14 +01:00
736 changed files with 376720 additions and 19600 deletions
@@ -0,0 +1,66 @@
name: Publish-blenderbim-multiplatform
on:
push:
paths:
- 'src/blenderbim/**'
- '.github/workflows/ci-blenderbim-matrix.yml'
env:
major: 0
minor: 0
name: blenderbim
jobs:
activate:
runs-on: ubuntu-latest
if: |
github.repository == 'IfcOpenShell/IfcOpenShell'
steps:
- name: Set env
run: echo ok go
build:
name: ${{ matrix.config.name }}-${{ matrix.pyver }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
pyver: [py37, py39]
config:
- {
name: "Windows Build",
short_name: win,
}
- {
name: "Linux Build",
short_name: linux
}
- {
name: "MacOS Build",
short_name: macos
}
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2 # https://github.com/actions/setup-python
with:
python-version: '3.7.7' # Version range or exact version of a Python version to use, using SemVer's version range syntax
architecture: 'x64' # optional x64 or x86. Defaults to x64 if not specified
- run: echo ${{ env.DATE }}
- name: Get current date
id: date
run: echo "::set-output name=date::$(date +'%y%m%d')"
- name: Compile
run: |
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/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: "Daily developer testing build blenderbim-${{steps.date.outputs.date}}"
+107
View File
@@ -0,0 +1,107 @@
name: CI
on:
push:
jobs:
activate:
runs-on: ubuntu-latest
if: |
github.repository == 'IfcOpenShell/IfcOpenShell' &&
!startsWith(github.event.head_commit.message, 'Release ') &&
!contains(github.event.head_commit.message, 'ci skip')
steps:
- run: echo ok go
build:
runs-on: ubuntu-20.04
needs: activate
steps:
- uses: actions/checkout@v2
- name: Install dependencies
run: |
sudo apt update
sudo apt-get install --no-install-recommends \
git cmake gcc g++ libboost1.67-all-dev python3-all-dev swig libpcre3-dev libxml2-dev \
liboce-foundation-dev liboce-modeling-dev liboce-ocaf-dev liboce-visualization-dev liboce-ocaf-lite-dev
-
name: ccache
uses: hendrikmuhs/ccache-action@v1
-
name: Build ifcopenshell
run: |
mkdir build && cd build
cmake \
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DCMAKE_INSTALL_PREFIX=$PWD/install/ \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_PREFIX_PATH=/usr \
-DCMAKE_SYSTEM_PREFIX_PATH=/usr \
-DBUILD_PACKAGE=On \
-DOCC_INCLUDE_DIR=/usr/include/oce \
-DOCC_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu \
-DPYTHON_EXECUTABLE:FILEPATH=/usr/bin/python3 \
-DPYTHON_INCLUDE_DIR:PATH=/usr/include/python3.8 \
-DPYTHON_LIBRARY:FILEPATH=/usr/lib/x86_64-linux-gnu/libpython3.8.so \
-DCOLLADA_SUPPORT=Off \
-DLIBXML2_INCLUDE_DIR=/usr/include/libxml2 \
-DLIBXML2_LIBRARIES=/usr/lib/x86_64-linux-gnu/libxml2.so \
../cmake
make -j $(nproc)
make install
-
name: Package
run: |
make package
working-directory: build
- name: Upload
uses: actions/upload-artifact@v2
with:
# Artifact name
name: ifcos-artifacts
# Directory containing files to upload
path: build/assets/Ifc*
deliver:
runs-on: ubuntu-20.04
needs: build
name: Docker Build, Tag, Push
steps:
- uses: actions/checkout@v2
with:
lfs: true
- name: Download
uses: actions/download-artifact@v2
with:
# Artifact name
name: ifcos-artifacts
path: artifacts/
-
name: Set up QEMU
uses: docker/setup-qemu-action@v1
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
-
name: Login to Dockerhub
uses: docker/login-action@v1
with:
username: aecgeeks
password: ${{ secrets.DOCKER_HUB_TOKEN }}
-
name: Build container image
uses: docker/build-push-action@v2
with:
context: artifacts
repository: aecgeeks/ifcopenshell
tags: aecgeeks/ifcopenshell:latest
file: ./Dockerfile
push: true
+96
View File
@@ -0,0 +1,96 @@
name: Release
on:
push:
tags:
- 'v[0-9].[0-9].[0-9]*'
jobs:
activate:
if: github.repository == 'IfcOpenShell/IfcOpenShell'
runs-on: ubuntu-latest
steps:
- run: echo ok go
build:
needs: activate
runs-on: ubuntu-20.04
steps:
- name: Checkout
uses: actions/checkout@v2
with:
lfs: true
-
name: Update ubuntu
run: |
sudo apt update
sudo apt-get install git cmake gcc g++ libboost1.67-all-dev python3-all-dev swig libpcre3-dev libxml2-dev
sudo apt-get install liboce-foundation-dev liboce-modeling-dev liboce-ocaf-dev liboce-visualization-dev liboce-ocaf-lite-dev
- name: Build
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CHANGELOG_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
mkdir build && cd build
cmake \
-DCMAKE_INSTALL_PREFIX=$PWD/install/ \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_PREFIX_PATH=/usr \
-DCMAKE_SYSTEM_PREFIX_PATH=/usr \
-DBUILD_PACKAGE=On \
-DOCC_INCLUDE_DIR=/usr/include/oce \
-DOCC_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu \
-DPYTHON_EXECUTABLE:FILEPATH=/usr/bin/python3 \
-DPYTHON_INCLUDE_DIR:PATH=/usr/include/python3.8 \
-DPYTHON_LIBRARY:FILEPATH=/usr/lib/x86_64-linux-gnu/libpython3.8.so \
-DCOLLADA_SUPPORT=Off \
-DLIBXML2_INCLUDE_DIR=/usr/include/libxml2 \
-DLIBXML2_LIBRARIES=/usr/lib/x86_64-linux-gnu/libxml2.so \
../cmake
make -j $(nproc)
make install
- name: Package
id: package
run: |
make package
echo ::set-output name=tgz::$( ls assets/*.tar.gz | head -n 1 | xargs basename )
echo ::set-output name=deb::$( ls assets/*.deb | head -n 1 | xargs basename )
working-directory: build
env:
CHANGELOG_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Release
id: release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: Release ${{ github.ref }}
draft: false
prerelease: true
body: |
Changes to this release
- add github actions support #1306
- add boost depends to ifcopenshell deb packaging
- <add more changes here>
See more details in the documentation
# body_path: build/CHANGELOG.md
- name: Upload TGZ
id: upload-tgz
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps
asset_path: build/assets/${{ steps.package.outputs.tgz }}
asset_name: ${{ steps.package.outputs.tgz }}
asset_content_type: application/x-gzip
- name: Upload DEB
id: upload-deb
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps
asset_path: build/assets/${{ steps.package.outputs.deb }}
asset_name: ${{ steps.package.outputs.deb }}
asset_content_type: application/vnd.debian.binary-package
+13
View File
@@ -1,4 +1,11 @@
# Dependency and build folders created by the build scripts
/_build-vs2015-x64/
/_deps-vs2015-x64-installed/
/_build-vs2017-x64/
/_deps-vs2017-x64-installed/
/_build-vs2019-x64/
/_deps-vs2019-x64-installed/
/_deps/
/deps*/
/build*/
/install*/
@@ -13,6 +20,10 @@ __pycache__
# PyCharm files
.idea
#Virtual Env Files
Pipfile
Pipfile.lock
# Docs
/docs/output
/docs/rst_files
@@ -23,3 +34,5 @@ __pycache__
*.mo
# Vim
*.swp
+46 -39
View File
@@ -1,57 +1,61 @@
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
- cd ../../test
- /usr/bin/python2.7 tests.py
-DJSON_INCLUDE_DIR=/usr/include \
../cmake
# TODO: Drop sudo
- sudo make -j $(nproc) install
- cd ../test
- /usr/bin/python tests.py
- /usr/bin/python ../src/ifcopenshell-python/ifcopenshell/test_ids.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 \
echo $i | tee -a log; \
@@ -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
+37
View File
@@ -0,0 +1,37 @@
# -*- mode: Dockerfile -*-
FROM ubuntu:focal
ARG CHANNEL
ENV CHANNEL=${CHANNEL:-latest}
RUN apt-get update \
&& apt-get install -y \
vim \
ssh \
sudo \
wget \
software-properties-common ;\
rm -rf /var/lib/apt/lists/*
RUN useradd --user-group --create-home --shell /bin/bash IfcOpenShell ;\
echo "IfcOpenShell ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
ENV DEBIAN_FRONTEND=noninteractive
RUN echo "deb http://archive.ubuntu.com/ubuntu focal-proposed main restricted" | tee -a /etc/apt/sources.list; \
echo "deb http://archive.ubuntu.com/ubuntu focal-proposed universe" | tee -a /etc/apt/sources.list; \
echo "deb http://archive.ubuntu.com/ubuntu focal-proposed multiverse" | tee -a /etc/apt/sources.list; \
apt-get -qq update; \
apt-get -y install tzdata dos2unix rsync; \
apt-get -y install python3 libxml2 liboce-foundation11 liboce-modeling11 liboce-ocaf11 liboce-visualization11 \
liboce-ocaf-lite11 libpython3.8 libboost-system1.67.0 libboost-program-options1.67.0 \
libboost-regex1.67.0 libboost-thread1.67.0 libboost-date-time1.67.0; \
rm -rf /var/lib/apt/lists/* ;
COPY . /home/IfcOpenShell/
RUN ls -lrtR /home/IfcOpenShell
RUN dpkg -i /home/IfcOpenShell/*.deb
USER IfcOpenShell
+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:
+145 -53
View File
@@ -21,7 +21,10 @@ cmake_minimum_required(VERSION 3.1.3)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON) # not necessary, but encouraged
project (IfcOpenShell)
project (IfcOpenShell VERSION 0.6.0)
# use extra version to make pre-release using eg semver
set( EXTRA_VERSION "-alpha.3")
foreach(max_year RANGE 2014 2030)
set(max_sdk "$ENV{ADSK_3DSMAX_SDK_${max_year}}")
@@ -35,6 +38,7 @@ OPTION(COLLADA_SUPPORT "Build IfcConvert with COLLADA support (requires OpenCOLL
OPTION(IFCXML_SUPPORT "Build IfcParse with ifcXML support (requires libxml2)." ON)
OPTION(ENABLE_BUILD_OPTIMIZATIONS "Enable certain compiler and linker optimizations on RelWithDebInfo and Release builds." OFF)
OPTION(IFCCONVERT_DOUBLE_PRECISION "IfcConvert: Use double precision floating-point numbers." ON)
OPTION(BUILD_PACKAGE "" OFF)
OPTION(BUILD_IFCGEOM "Build IfcGeom." ON)
OPTION(BUILD_DOCUMENTATION "Build IfcOpenShell Documentation." OFF)
OPTION(BUILD_IFCPYTHON "Build IfcPython." ON)
@@ -54,7 +58,7 @@ OPTION(USERSPACE_PYTHON_PREFIX "Installs IfcPython for the current user only ins
# TODO QtViewer is deprecated ATM as it uses the 0.4 API
# OPTION(BUILD_QTVIEWER "Build IfcOpenShell Qt GUI Viewer (requires Qt 4 framework)." OFF)
include(GNUInstallDirs)
if((BUILD_CONVERT OR BUILD_GEOMSERVER OR BUILD_IFCPYTHON) AND (NOT BUILD_IFCGEOM))
message(STATUS "'IfcGeom' is required with current outputs")
set(BUILD_IFCGEOM ON)
@@ -65,7 +69,7 @@ IF(NOT BINDIR)
set(BINDIR bin)
ENDIF()
IF(NOT IS_ABSOLUTE ${BINDIR})
set(BINDIR ${CMAKE_INSTALL_PREFIX}/${BINDIR})
set(BINDIR ${CMAKE_INSTALL_BINDIR})
ENDIF()
MESSAGE(STATUS "BINDIR: ${BINDIR}")
@@ -73,7 +77,7 @@ IF(NOT INCLUDEDIR)
set(INCLUDEDIR include)
ENDIF()
IF(NOT IS_ABSOLUTE ${INCLUDEDIR})
set(INCLUDEDIR ${CMAKE_INSTALL_PREFIX}/${INCLUDEDIR})
set(INCLUDEDIR ${CMAKE_INSTALL_INCLUDEDIR})
ENDIF()
MESSAGE(STATUS "INCLUDEDIR: ${INCLUDEDIR}")
@@ -81,11 +85,11 @@ IF(NOT LIBDIR)
set(LIBDIR lib)
ENDIF()
IF(NOT IS_ABSOLUTE ${LIBDIR})
set(LIBDIR ${CMAKE_INSTALL_PREFIX}/${LIBDIR})
set(LIBDIR ${CMAKE_INSTALL_LIBDIR})
ENDIF()
MESSAGE(STATUS "LIBDIR: ${LIBDIR}")
set(IFCOPENSHELL_LIBARY_DIR "") # for *nix rpaths
set(IFCOPENSHELL_LIBRARY_DIR "") # for *nix rpaths
if (BUILD_SHARED_LIBS)
add_definitions(-DIFC_SHARED_BUILD)
@@ -95,7 +99,7 @@ if (BUILD_SHARED_LIBS)
# There will be couple hundreds of these so suppress them away, https://msdn.microsoft.com/en-us/library/esew7y1w.aspx
add_definitions(-wd4251)
endif()
set(IFCOPENSHELL_LIBARY_DIR "${LIBDIR}")
set(IFCOPENSHELL_LIBRARY_DIR "${LIBDIR}")
endif()
# Create cache entries if absent for environment variables
@@ -175,6 +179,7 @@ if(USE_MMAP)
endif()
if (IFCXML_SUPPORT)
find_package(LibXml2 REQUIRED)
add_definitions(-DWITH_IFCXML)
endif()
@@ -219,51 +224,63 @@ endfunction()
if(BUILD_IFCGEOM)
IF(MSVC)
add_debug_variants(LIBXML2_LIBRARIES "${LIBXML2_LIBRARIES}" d)
add_debug_variants(LIBXML2_LIBRARIES "${LIBXML2_LIBRARIES}" d)
ENDIF()
# Find Open CASCADE
# Open CASCADE
IF("${OCC_INCLUDE_DIR}" STREQUAL "")
SET(OCC_INCLUDE_DIR "/usr/include/oce/" CACHE FILEPATH "Open CASCADE header files")
MESSAGE(STATUS "Looking for Open CASCADE include files in: ${OCC_INCLUDE_DIR}")
MESSAGE(STATUS "Use OCC_INCLUDE_DIR to specify another directory")
FIND_PATH(OCC_INCLUDE_DIR Standard_Version.hxx
[PATHS
/usr/include/occt
/usr/include/oce
/usr/include/opencascade
]
REQUIRED
)
IF(OCC_INCLUDE_DIR)
MESSAGE(STATUS "Found Open CASCADE include files in: ${OCC_INCLUDE_DIR}")
ELSE()
MESSAGE(FATAL_ERROR "Unable to find Open CASCADE include directory, specify OCC_INCLUDE_DIR manually.")
ENDIF()
ELSE()
SET(OCC_INCLUDE_DIR ${OCC_INCLUDE_DIR} CACHE FILEPATH "Open CASCADE header files")
MESSAGE(STATUS "Looking for Open CASCADE include files in: ${OCC_INCLUDE_DIR}")
ENDIF()
FIND_FILE(gp_Pnt_hxx "gp_Pnt.hxx" ${OCC_INCLUDE_DIR})
IF(gp_Pnt_hxx)
MESSAGE(STATUS "Header files found")
ELSE()
MESSAGE(FATAL_ERROR "Unable to find header files, aborting")
SET(OCC_INCLUDE_DIR ${OCC_INCLUDE_DIR} CACHE FILEPATH "Open CASCADE header files")
MESSAGE(STATUS "Looking for Open CASCADE include files in: ${OCC_INCLUDE_DIR}")
ENDIF()
SET(OPENCASCADE_LIBRARY_NAMES
TKernel TKMath TKBRep TKGeomBase TKGeomAlgo TKG3d TKG2d TKShHealing TKTopAlgo TKMesh TKPrim TKBool TKBO
TKFillet TKSTEP TKSTEPBase TKSTEPAttr TKXSBase TKSTEP209 TKIGES TKOffset TKHLR
TKernel TKMath TKBRep TKGeomBase TKGeomAlgo TKG3d TKG2d TKShHealing TKTopAlgo TKMesh TKPrim TKBool TKBO
TKFillet TKSTEP TKSTEPBase TKSTEPAttr TKXSBase TKSTEP209 TKIGES TKOffset TKHLR
)
IF("${OCC_LIBRARY_DIR}" STREQUAL "")
SET(OCC_LIBRARY_DIR "/usr/lib/" CACHE FILEPATH "Open CASCADE library files")
MESSAGE(STATUS "Looking for Open CASCADE library files in: ${OCC_LIBRARY_DIR}")
MESSAGE(STATUS "Use OCC_LIBRARY_DIR to specify another directory")
find_library(OCC_LIBRARY TKernel
[PATHS
/usr/lib
]
REQUIRED
)
IF(OCC_LIBRARY)
GET_FILENAME_COMPONENT(OCC_LIBRARY_DIR ${OCC_LIBRARY} PATH)
MESSAGE(STATUS "Found Open CASCADE library files in: ${OCC_LIBRARY_DIR}")
ELSE()
MESSAGE(FATAL_ERROR "Unable find Open CASCADE library directory, specify OCC_LIBRARY_DIR manually.")
ENDIF()
ELSE()
SET(OCC_LIBRARY_DIR ${OCC_LIBRARY_DIR} CACHE FILEPATH "Open CASCADE library files")
MESSAGE(STATUS "Looking for Open CASCADE library files in: ${OCC_LIBRARY_DIR}")
SET(OCC_LIBRARY_DIR ${OCC_LIBRARY_DIR} CACHE FILEPATH "Open CASCADE library files")
MESSAGE(STATUS "Looking for Open CASCADE library files in: ${OCC_LIBRARY_DIR}")
ENDIF()
FIND_LIBRARY(libTKernel NAMES TKernel TKerneld PATHS ${OCC_LIBRARY_DIR} NO_DEFAULT_PATH)
IF(libTKernel)
MESSAGE(STATUS "Library files found")
MESSAGE(STATUS "Required Open Cascade Library files found")
ELSE()
MESSAGE(FATAL_ERROR "Unable to find library files, aborting")
MESSAGE(FATAL_ERROR "Unable to find Open Cascade library files, aborting")
ENDIF()
# Use the found libTKernel as a template for all other OCC libraries
# TODO Extract this into macro/function
foreach(lib ${OPENCASCADE_LIBRARY_NAMES})
# Make sure we'll handle the Windows/MSVC debug postfix convetion too.
# Make sure we'll handle the Windows/MSVC debug postfix convention too.
string(REPLACE TKerneld "${lib}" lib_path "${libTKernel}")
string(REPLACE TKernel "${lib}" lib_path "${lib_path}")
list(APPEND OPENCASCADE_LIBRARIES "${lib_path}")
@@ -338,7 +355,7 @@ IF(COLLADA_SUPPORT AND BUILD_CONVERT)
# Use the found OpenCOLLADAFramework as a template for all other OpenCOLLADA libraries
foreach(lib ${OPENCOLLADA_LIBRARY_NAMES})
# Make sure we'll handle the Windows/MSVC debug postfix convetion too.
# Make sure we'll handle the Windows/MSVC debug postfix convention too.
string(REPLACE OpenCOLLADAFrameworkd "${lib}" lib_path "${OpenCOLLADAFramework}")
string(REPLACE OpenCOLLADAFramework "${lib}" lib_path "${lib_path}")
list(APPEND OPENCOLLADA_LIBRARIES "${lib_path}")
@@ -424,7 +441,7 @@ IF(MSVC)
# Disable overeager and false positives causing C4458 ("declaration of 'indentifier' hides class member"), at least for now.
ADD_DEFINITIONS(-wd4458)
ENDIF()
# Enforce standards-conformance on VS > 2015, older Boost versions fail to compile with this
# Enforce standards-conformance on VS > 2015, older Boost versions fail to compile with this
if (MSVC_VERSION GREATER 1900 AND (Boost_MAJOR_VERSION GREATER 1 OR Boost_MINOR_VERSION GREATER 66))
add_definitions(-permissive-)
endif()
@@ -449,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)
@@ -465,27 +486,31 @@ INCLUDE_DIRECTORIES(${INCLUDE_DIRECTORIES} ${OCC_INCLUDE_DIR} ${OPENCOLLADA_INCL
function(files_for_ifc_version IFC_VERSION RESULT_NAME)
set(IFC_PARSE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../src/ifcparse)
set(${RESULT_NAME}
${IFC_PARSE_DIR}/Ifc${IFC_VERSION}.h
set(${RESULT_NAME}
${IFC_PARSE_DIR}/Ifc${IFC_VERSION}.h
${IFC_PARSE_DIR}/Ifc${IFC_VERSION}enum.h
${IFC_PARSE_DIR}/Ifc${IFC_VERSION}.cpp
PARENT_SCOPE
)
endfunction()
set(SCHEMA_VERSIONS "2x3" "4" "4x1" "4x2" "4x3_rc1")
set(SCHEMA_VERSIONS "2x3" "4" "4x1" "4x2" "4x3_rc1" "4x3_rc2" "4x3_rc3" "4x3_rc4")
foreach(s ${SCHEMA_VERSIONS})
add_definitions(-DHAS_SCHEMA_${s})
endforeach()
if(COMPILE_SCHEMA)
# @todo, this appears to be untested at the moment
find_package(PythonInterp)
IF(NOT PYTHONINTERP_FOUND)
MESSAGE(FATAL_ERROR "A Python interpreter is necessary when COMPILE_SCHEMA is enabled. Disable COMPILE_SCHEMA or fix Python paths to proceed.")
ENDIF()
set(IFC_RELEASE_NOT_USED ${SCHEMA_VERSIONS})
# Install pyparsing if necessary
execute_process(COMMAND ${PYTHON_EXECUTABLE} -m pip freeze OUTPUT_VARIABLE PYTHON_PACKAGE_LIST)
if ("${PYTHON_PACKAGE_LIST}" STREQUAL "")
@@ -507,23 +532,23 @@ if(COMPILE_SCHEMA)
else()
message(STATUS "Python interpreter with pyparsing found")
endif()
# Bootstrap the parser
message(STATUS "Compiling schema, this will take a while...")
execute_process(COMMAND ${PYTHON_EXECUTABLE} bootstrap.py express.bnf
WORKING_DIRECTORY ../src/ifcexpressparser
execute_process(COMMAND ${PYTHON_EXECUTABLE} bootstrap.py express.bnf
WORKING_DIRECTORY ../src/ifcexpressparser
OUTPUT_FILE express_parser.py
RESULT_VARIABLE SUCCESS)
if (NOT "${SUCCESS}" STREQUAL "0")
MESSAGE(FATAL_ERROR "Failed to bootstrap parser. Make sure pyparsing is installed")
endif()
# Generate code
execute_process(COMMAND ${PYTHON_EXECUTABLE} ../ifcexpressparser/express_parser.py ../../${COMPILE_SCHEMA}
WORKING_DIRECTORY ../src/ifcparse
OUTPUT_VARIABLE COMPILED_SCHEMA_NAME)
# Prevent the schema that had just been compiled from being excluded
foreach(s ${SCHEMA_VERSIONS})
if("${COMPILED_SCHEMA_NAME}" STREQUAL "${s}")
@@ -552,8 +577,35 @@ if (BUILD_CONVERT)
endif()
# IfcParse
file(GLOB IFCPARSE_H_FILES ../src/ifcparse/*.h)
file(GLOB IFCPARSE_CPP_FILES ../src/ifcparse/*.cpp)
file(GLOB IFCPARSE_H_FILES_ALL ../src/ifcparse/*.h)
file(GLOB IFCPARSE_CPP_FILES_ALL ../src/ifcparse/*.cpp)
foreach(s ${IFCPARSE_H_FILES_ALL})
get_filename_component(p "${s}" NAME)
if (NOT "${p}" MATCHES "[0-9]")
list(APPEND IFCPARSE_H_FILES "${s}")
endif()
endforeach()
foreach(s ${IFCPARSE_CPP_FILES_ALL})
get_filename_component(p "${s}" NAME)
if (NOT "${p}" MATCHES "[0-9]")
list(APPEND IFCPARSE_CPP_FILES "${s}")
endif()
endforeach()
foreach(s ${SCHEMA_VERSIONS})
list(APPEND IFCPARSE_H_FILES
../src/ifcparse/Ifc${s}.h
../src/ifcparse/Ifc${s}-definitions.h
)
list(APPEND IFCPARSE_CPP_FILES
../src/ifcparse/Ifc${s}.cpp
../src/ifcparse/Ifc${s}-schema.cpp
)
endforeach()
set(IFCPARSE_FILES ${IFCPARSE_CPP_FILES} ${IFCPARSE_H_FILES})
add_library(IfcParse ${IFCPARSE_FILES})
@@ -586,7 +638,7 @@ if (UNIX)
find_package(Threads)
endif()
TARGET_LINK_LIBRARIES(IfcGeom ${IFCGEOM_SCHEMA_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
TARGET_LINK_LIBRARIES(IfcGeom IfcParse ${IFCGEOM_SCHEMA_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
endif(BUILD_IFCGEOM)
@@ -624,7 +676,7 @@ if ((NOT WIN32) AND BUILD_SHARED_LIBS)
# Only set RPATHs when building shared libraries (i.e. IfcParse and
# IfcGeom are dynamically linked). Not necessarily a perfect solution
# but probably a good indication of whether RPATHs are necessary.
SET_INSTALL_RPATHS(IfcConvert "${IFCOPENSHELL_LIBARY_DIR};${OCC_LIBRARY_DIR};${Boost_LIBRARY_DIRS};${OPENCOLLADA_LIBRARY_DIR}")
SET_INSTALL_RPATHS(IfcConvert "${IFCOPENSHELL_LIBRARY_DIR};${OCC_LIBRARY_DIR};${Boost_LIBRARY_DIRS};${OPENCOLLADA_LIBRARY_DIR}")
endif()
INSTALL(TARGETS IfcConvert
@@ -645,7 +697,7 @@ ADD_EXECUTABLE(IfcGeomServer ${SOURCE_FILES})
TARGET_LINK_LIBRARIES(IfcGeomServer ${IFCOPENSHELL_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${Boost_LIBRARIES})
if ((NOT WIN32) AND BUILD_SHARED_LIBS)
SET_INSTALL_RPATHS(IfcGeomServer "${IFCOPENSHELL_LIBARY_DIR};${OCC_LIBRARY_DIR};${Boost_LIBRARY_DIRS}")
SET_INSTALL_RPATHS(IfcGeomServer "${IFCOPENSHELL_LIBRARY_DIR};${OCC_LIBRARY_DIR};${Boost_LIBRARY_DIRS}")
endif()
INSTALL(TARGETS IfcGeomServer
@@ -656,7 +708,7 @@ INSTALL(TARGETS IfcGeomServer
endif()
# Documentation
# Documentation
IF(BUILD_DOCUMENTATION)
set(CMAKE_MODULE_PATH "../docs/cmake")
ADD_SUBDIRECTORY(../docs docs)
@@ -675,7 +727,7 @@ IF(BUILD_IFCMAX)
ENDIF()
# CMake installation targets
INSTALL(FILES ${IFCPARSE_H_FILES}
INSTALL(FILES ${IFCPARSE_H_FILES}
DESTINATION ${INCLUDEDIR}/ifcparse
)
@@ -686,11 +738,11 @@ INSTALL(TARGETS IfcParse
)
if(BUILD_IFCGEOM)
INSTALL(FILES ${IFCGEOM_H_FILES}
INSTALL(FILES ${IFCGEOM_H_FILES}
DESTINATION ${INCLUDEDIR}/ifcgeom
)
INSTALL(FILES ${SCHEMA_AGNOSTIC_H_FILES}
INSTALL(FILES ${SCHEMA_AGNOSTIC_H_FILES}
DESTINATION ${INCLUDEDIR}/ifcgeom_schema_agnostic
)
@@ -716,3 +768,43 @@ INSTALL(FILES ${SERIALIZERS_S_FILES}
DESTINATION ${INCLUDEDIR}/serializers/schema_dependent
)
endif()
list(APPEND CPACK_SOURCE_IGNORE_FILES
.git
.gitignore
)
set(CPACK_PACKAGE_NAME "${PROJECT_NAME}-${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}${EXTRA_VERSION}")
set(CPACK_SOURCE_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}${EXTRA_VERSION}")
SET(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}${EXTRA_VERSION}-${CMAKE_SYSTEM_NAME}")
set(CPACK_PACKAGE_DIRECTORY "${PROJECT_BINARY_DIR}/assets")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "IfcOpenShell")
set(CPACK_PACKAGE_DESCRIPTION "IfcOpenShell.")
set(CPACK_PACKAGE_VENDOR "Cemosis")
set(CPACK_PACKAGE_CONTACT "Christophe Prud'homme <christophe.prudhomme@cemosis.fr>")
set(CPACK_PACKAGE_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}")
set(CPACK_PACKAGE_VERSION_MINOR "${PROJECT_VERSION_MINOR}")
set(CPACK_PACKAGE_VERSION_PATCH "${PROJECT_VERSION_PATCH}")
set(CPACK_GENERATOR "TGZ;DEB")
set(CPACK_SOURCE_GENERATOR "TGZ")
FOREACH(COMPONENT IN ITEMS ${BOOST_COMPONENTS})
string(REPLACE "_" "-" COMP ${COMPONENT})
set(BOOST_DEPS "${BOOST_DEPS}, libboost-${COMP}1.67.0")
endforeach(COMPONENT)
set(CPACK_DEBIAN_PACKAGE_NAME "${PROJECT_NAME}")
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "${CPACK_PACKAGE_CONTACT}")
set(CPACK_DEBIAN_PACKAGE_DEPENDS "python3, libxml2, liboce-foundation11, liboce-modeling11, liboce-ocaf11, liboce-visualization11, liboce-ocaf-lite11, libpython3.8 ${BOOST_DEPS}")
set(CPACK_DEBIAN_PACKAGE_DESCRIPTION_SUMMARY "${CPACK_PACKAGE_DESCRIPTION_SUMMARY}")
set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "${CPACK_PACKAGE_DESCRIPTION}")
set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional")
set(CPACK_DEBIAN_PACKAGE_SECTION "science")
set(CPACK_DEBIAN_PACKAGE_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}${EXTRA_VERSION}")
set(CPACK_DEBIAN_ARCHITECTURE "${CMAKE_SYSTEM_PROCESSOR}")
#set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CMAKE_SOURCE_DIR}/cmake/debian/postinst")
include(CPack)
+2 -2
View File
@@ -32,7 +32,7 @@ DOXYFILE_ENCODING = UTF-8
# title of most generated pages and in a few other places.
# The default value is: My Project.
PROJECT_NAME = "My Project"
PROJECT_NAME = "IfcOpenShell"
# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
# could be handy for archiving the generated documentation or if some version
@@ -829,7 +829,7 @@ WARN_LOGFILE =
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
# Note: If this tag is empty the current directory is searched.
INPUT = ../src/ifcgeom ../src/serializers
INPUT = ../src/ifcgeom ../src/serializers ../src/ifcgeom_schema_agnostic ../src/ifcparse
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+2 -13
View File
@@ -22,7 +22,7 @@ exhale_args = {
# These arguments are required
"containmentFolder": "./rst_files",
"rootFileName": "library_root.rst",
"rootFileTitle": "IfcOpenShell",
"rootFileTitle": "IfcOpenShell C++",
"doxygenStripFromPath": "..",
# Suggested optional arguments
"createTreeView": True,
@@ -34,15 +34,4 @@ exhale_args = {
}
# html_theme = 'sphinx_rtd_theme'
# # Tell sphinx what the primary language being documented is.
# primary_domain = 'cpp'
# # Tell sphinx what the pygments highlight language should be.
# highlight_language = 'cpp'
cpp_id_attributes = ['IFC_PARSE_API', 'IFC_GEOM_API']
+1 -1
View File
@@ -7,4 +7,4 @@ IfcOpenShell
rst_files/library_root
`ifcopenshell-python documentation <_build/html/index.html>`_
* `ifcopenshell-python documentation <python/html/index.html>`_
+26 -39
View File
@@ -1,52 +1,39 @@
# This program requires doxygen, sphinx, breathe and exhale.
import os
import os.path
import sys
import shutil
import subprocess
import multiprocessing
from sys import platform
def copy_content(content, container):
with open(content) as f:
with open(container, "w") as f1:
for line in f:
f1.write(line)
conf_path = "./conf.py"
index_path = "./index.rst"
# some extra check to see if we can find sphinx in pypy bin dir
sphinx_build = os.path.join(os.path.dirname(sys.executable), 'sphinx-build')
if not os.path.exists(sphinx_build):
sphinx_build = "sphinx-build"
# else:
# os.environ['PATH'] = os.pathsep + os.path.dirname(sys.executable)
# not used, -j caused issues in my sphinx install
j = str(multiprocessing.cpu_count())
if not os.path.isdir("./output/doxygen/xml"):
os.makedirs("output/doxygen/xml")
if os.path.isfile(conf_path) and not os.path.isfile(index_path):
os.remove(conf_path)
if os.path.isfile(index_path) and not os.path.isfile(conf_path):
os.remove(index_path)
subprocess.check_call([sphinx_build, "-b", "html", ".", "output"])
if not (os.path.isfile(conf_path) and os.path.isfile(index_path)):
subprocess.run(["sphinx-quickstart", "-q", "-p", "IfcOpenShell"])
conf_copy = ["./conf_to_copy.py", "./conf.py"]
index_copy = ["./index_to_copy.rst", "./index.rst"]
copy_content(conf_copy[0], conf_copy[1])
copy_content(index_copy[0], index_copy[1])
if platform == "linux" or platform == "linux2":
subprocess.run(["make", "html"])
if not os.path.isdir("/output/_build"):
subprocess.run(["cd","../src/ifcblenderexport/docs"])
subprocess.run(["make", "html"])
subprocess.run(["cd", "../../../docs"])
subprocess.run(["mv" "../src/ifcblenderexport/docs/_build", "./output"])
if sys.platform == "linux" or sys.platform == "linux2":
if os.path.isdir("output/python"):
shutil.rmtree("output/python")
if not os.path.exists(os.path.join("../src/ifcblenderexport/docs", "contents.rst")):
subprocess.check_call(["ln", "index.rst", "contents.rst"], cwd="../src/ifcblenderexport/docs")
subprocess.check_call(["sed", "-i", "s/html_theme/# html_theme/g", "conf.py"], cwd="../src/ifcblenderexport/docs")
subprocess.check_call(["make", "html"], cwd="../src/ifcblenderexport/docs")
subprocess.check_call(["git", "checkout", "conf.py"], cwd="../src/ifcblenderexport/docs")
shutil.move("../src/ifcblenderexport/docs/_build", "./output")
os.rename("./output/_build", "./output/python")
elif platform == "win32" or platform == "win64":
subprocess.run(["make.bat", "html"])
elif sys.platform == "win32" or sys.platform == "win64":
subprocess.check_call(["make.bat", "html"])
+1 -1
View File
@@ -7,4 +7,4 @@ IfcOpenShell
rst_files/library_root
`ifcopenshell-python documentation <_build/html/index.html>`_
* `ifcopenshell-python documentation <python/html/index.html>`_
+2 -2
View File
@@ -310,7 +310,7 @@ def run(cmds, cwd=None):
BOOST_VERSION_UNDERSCORE=BOOST_VERSION.replace(".", "_")
OCE_LOCATION="https://github.com/tpaviot/oce/archive/OCE-%s.tar.gz" % (OCE_VERSION,)
BOOST_LOCATION="https://dl.bintray.com/boostorg/release/%s/source/" % (BOOST_VERSION,)
BOOST_LOCATION="https://boostorg.jfrog.io/artifactory/main/release/%s/source/" % (BOOST_VERSION,)
# Helper functions
@@ -336,7 +336,7 @@ def git_clone_or_pull_repository(clone_url, target_dir, revision=None):
`None`."""
if not os.path.exists(target_dir):
logger.info("cloning '%s' into '%s'" % (clone_url, target_dir))
run([git, "clone", clone_url, target_dir])
run([git, "clone", "--recursive", clone_url, target_dir])
else:
logger.info("directory '%s' already cloned. Pulling latest changes." % (target_dir,))
+8 -5
View File
@@ -4,23 +4,26 @@ A simple Python implementation of BCF. The data model is described in `data.py`.
Manipulation of BCF-XML is available via `bcfxml.py` and manipulation of BCF-API
is available via `bcfapi.py`.
Currently supports BCF version 2.1.
- BCF-XML version 2.1: Fully supported
- BCF-API version 2.1: Not supported, will probably tackle this after BCF-API v3.0
- BCF-XML version 3.0: Almost fully supported, except for the documents module
- BCF-API version 3.0: Not supported, but work underway to support it
## bcfxml
The `bcfxml` module lets you interact with the BCF-XML standard.
```
from bcf.bcfxml import BcfXml
from bcf import bcfxml
bcfxml = BcfXml()
# Load a project
project = bcfxml.get_project("/path/to/file.bcf")
bcfxml = bcfxml.load("/path/to/file.bcf")
# The project is also stored in the module
# project == bcfxml.project
project=bcfxml.get_project()
print(project.name)
# To edit a project, just modify the object directly
+30 -750
View File
@@ -1,762 +1,42 @@
import os
import uuid
import shutil
import os.path
import zipfile
import logging
import tempfile
import bcf.data
from datetime import datetime
from xml.dom import minidom
from xmlschema import XMLSchema
from contextlib import contextmanager
from shutil import copyfile
cwd = os.path.dirname(os.path.realpath(__file__))
def load(filepath):
filepath = extract_project(filepath)
if os.path.isfile(os.path.join(filepath, "bcf.version")):
version_path = os.path.join(filepath, "bcf.version")
version_id = get_version(version_path)
# TODO: we actually coded it for 2.1, let's check the difference between 2.0 and 2.1
if version_id == "2.1" or version_id == "2.0":
from bcf.v2.bcfxml import BcfXml
bcfxml = BcfXml()
bcfxml.filepath = filepath
return bcfxml
elif version_id == "3.0":
from bcf.v3.bcfxml import BcfXml
@contextmanager
def cd(newdir):
prevdir = os.getcwd()
os.chdir(os.path.expanduser(newdir))
try:
yield
finally:
os.chdir(prevdir)
class BcfXml:
def __init__(self):
self.filepath = None
self.logger = logging.getLogger("bcfxml")
self.author = "john@doe.com"
self.project = bcf.data.Project()
self.version = "2.1"
self.topics = {}
def new_project(self):
self.project.project_id = str(uuid.uuid4())
self.project.name = "New Project"
self.topics = {}
if self.filepath:
self.close_project()
self.filepath = tempfile.mkdtemp()
self.edit_project()
self.edit_version()
def get_project(self, filepath=None):
if not filepath:
return self.project
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"]
return self.project
def edit_project(self):
self.document = minidom.Document()
root = self._create_element(self.document, "ProjectExtension")
project = self._create_element(root, "Project", {"ProjectId": self.project.project_id})
self._create_element(project, "Name", text=self.project.name)
self._create_element(root, "ExtensionSchema", text="extensions.xsd")
with open(os.path.join(self.filepath, "project.bcfp"), "wb") as f:
f.write(self.document.toprettyxml(encoding="utf-8"))
def save_project(self, filepath):
with cd(self.filepath):
zip_file = zipfile.ZipFile(filepath, "w", zipfile.ZIP_DEFLATED)
for root, dirs, files in os.walk("./"):
for file in files:
zip_file.write(os.path.join(root, file))
zip_file.close()
def get_version(self):
data = self._read_xml("bcf.version", "version.xsd")
self.version = data["@VersionId"]
return self.version
def edit_version(self):
self.document = minidom.Document()
root = self._create_element(self.document, "Version", {"VersionId": self.version})
version = self._create_element(root, "DetailedVersion", text=self.version)
with open(os.path.join(self.filepath, "bcf.version"), "wb") as f:
f.write(self.document.toprettyxml(encoding="utf-8"))
def get_topics(self):
self.topics = {}
topics = []
subdirs = []
for (dirpath, dirnames, filenames) in os.walk(self.filepath):
subdirs = dirnames
break
for subdir in subdirs:
self.topics[subdir] = self.get_topic(subdir)
return self.topics
def get_header(self, guid):
data = self._read_xml(os.path.join(guid, "markup.bcf"), "markup.xsd")
if "Header" not in data:
return
header = bcf.data.Header()
for item in data["Header"]["File"]:
header_file = bcf.data.HeaderFile()
optional_keys = {
"filename": "Filename",
"date": "Date",
"reference": "Reference",
"ifc_project": "@IfcProject",
"ifc_spatial_structure_element": "@IfcSpatialStructureElement",
"is_external": "@isExternal",
}
for key, value in optional_keys.items():
if value in item:
setattr(header_file, key, item[value])
header.files.append(header_file)
self.topics[guid].header = header
return header
def get_topic(self, guid):
if guid in self.topics:
return self.topics[guid]
data = self._read_xml(os.path.join(guid, "markup.bcf"), "markup.xsd")
topic = bcf.data.Topic()
self.topics[guid] = topic
mandatory_keys = {
"guid": "@Guid",
"title": "Title",
"creation_date": "CreationDate",
"creation_author": "CreationAuthor",
}
for key, value in mandatory_keys.items():
setattr(topic, key, data["Topic"][value])
optional_keys = {
"priority": "Priority",
"index": "Index",
"labels": "Labels",
"reference_links": "ReferenceLink",
"modified_date": "ModifiedDate",
"modified_author": "ModifiedAuthor",
"due_date": "DueDate",
"assigned_to": "AssignedTo",
"stage": "Stage",
"description": "Description",
"topic_status": "@TopicStatus",
"topic_type": "@TopicType",
}
for key, value in optional_keys.items():
if value in data["Topic"]:
setattr(topic, key, data["Topic"][value])
if "BimSnippet" in data["Topic"]:
bim_snippet = bcf.data.BimSnippet()
keys = {
"snippet_type": "@SnippetType",
"is_external": "@IsExternal",
"reference": "Reference",
"reference_schema": "ReferenceSchema",
}
for key, value in keys.items():
if value in data["Topic"]["BimSnippet"]:
setattr(bim_snippet, key, data["Topic"]["BimSnippet"][value])
topic.bim_snippet = bim_snippet
if "DocumentReference" in data["Topic"]:
for item in data["Topic"]["DocumentReference"]:
document_reference = bcf.data.DocumentReference()
keys = {
"referenced_document": "ReferencedDocument",
"is_external": "@IsExternal",
"guid": "@Guid",
"description": "Description",
}
for key, value in keys.items():
if value in item:
setattr(document_reference, key, item[value])
topic.document_references.append(document_reference)
if "RelatedTopic" in data["Topic"]:
for item in data["Topic"]["RelatedTopic"]:
related_topic = bcf.data.RelatedTopic()
related_topic.guid = item["@Guid"]
topic.related_topics.append(related_topic)
return topic
def add_topic(self, topic=None):
if topic is None:
topic = bcf.data.Topic()
if not topic.guid:
topic.guid = str(uuid.uuid4())
if not topic.title:
topic.title = "New Topic"
os.mkdir(os.path.join(self.filepath, topic.guid))
self.edit_topic(topic)
return topic
def edit_topic(self, topic):
if not topic.creation_date:
topic.creation_date = datetime.utcnow().isoformat()
topic.creation_author = self.author
bcfxml = BcfXml()
bcfxml.filepath = filepath
return bcfxml
else:
topic.modified_date = datetime.utcnow().isoformat()
topic.modified_author = self.author
raise Exception(f"Version {version_id} not supported.")
self.document = minidom.Document()
root = self._create_element(self.document, "Markup")
self.write_header(topic.header, root)
def get_version(version_path):
xmlparse = minidom.parse(version_path)
version_el = xmlparse.getElementsByTagName("Version")[0]
version = version_el.getAttribute("VersionId")
return version
topic_el = self._create_element(
root,
"Topic",
{
"Guid": topic.guid,
"TopicType": topic.topic_type,
"TopicStatus": topic.topic_status,
},
)
for reference_link in topic.reference_links:
self._create_element(topic_el, "ReferenceLink", text=reference_link)
text_map = {
"Title": topic.title,
"Priority": topic.priority,
"Index": topic.index,
}
for key, value in text_map.items():
if value:
self._create_element(topic_el, key, text=value)
for label in topic.labels:
self._create_element(topic_el, "Labels", text=label)
text_map = {
"CreationDate": topic.creation_date,
"CreationAuthor": topic.creation_author,
"ModifiedDate": topic.modified_date,
"ModifiedAuthor": topic.modified_author,
"DueDate": topic.due_date,
"AssignedTo": topic.assigned_to,
"Stage": topic.stage,
"Description": topic.description,
}
for key, value in text_map.items():
if value:
self._create_element(topic_el, key, text=value)
if topic.bim_snippet:
bim_snippet = self._create_element(
topic_el,
"BimSnippet",
{"SnippetType": topic.bim_snippet.snippet_type, "isExternal": topic.bim_snippet.is_external},
)
self._create_element(bim_snippet, "Reference", text=topic.bim_snippet.reference)
self._create_element(bim_snippet, "ReferenceSchema", text=topic.bim_snippet.reference_schema)
for reference in topic.document_references:
reference_el = self._create_element(
topic_el, "DocumentReference", {"Guid": reference.guid, "isExternal": reference.is_external}
)
self._create_element(reference_el, "ReferencedDocument", text=reference.referenced_document)
self._create_element(reference_el, "Description", text=reference.description)
for related_topic in topic.related_topics:
self._create_element(topic_el, "RelatedTopic", {"Guid": related_topic.guid})
self.write_comments(topic.comments, root)
self.write_viewpoints(topic.viewpoints, root, topic)
with open(os.path.join(self.filepath, topic.guid, "markup.bcf"), "wb") as f:
f.write(self.document.toprettyxml(encoding="utf-8"))
def write_header(self, header, root):
if not header or not header.files:
return
header_el = self._create_element(root, "Header")
for f in header.files:
file_el = self._create_element(
header_el,
"File",
{
"IfcProject": f.ifc_project,
"IfcSpatialStructureElement": f.ifc_spatial_structure_element,
"isExternal": f.is_external,
},
)
self._create_element(file_el, "Filename", text=f.filename)
self._create_element(file_el, "Date", text=f.date)
self._create_element(file_el, "Reference", text=f.reference)
def write_comments(self, comments, root):
for comment in comments.values():
comment_el = self._create_element(root, "Comment", {"Guid": comment.guid})
text_map = {
"Date": comment.date,
"Author": comment.author,
"Comment": comment.comment,
"ModifiedDate": comment.modified_date,
"ModifiedAuthor": comment.modified_author,
}
for key, value in text_map.items():
if value:
self._create_element(comment_el, key, text=value)
if comment.viewpoint:
self._create_element(comment_el, "Viewpoint", {"Guid": comment.viewpoint.guid})
def add_comment(self, topic, comment=None):
if comment is None:
comment = bcf.data.Comment()
if not comment.guid:
comment.guid = str(uuid.uuid4())
if not comment.comment:
comment.comment = "'Free software' is a matter of liberty, not price. To understand the concept, you should think of 'free' as in 'free speech,' not as in 'free beer'."
topic.comments[comment.guid] = comment
self.edit_comment(comment, topic)
def edit_comment(self, comment, topic):
if not comment.date:
comment.date = datetime.utcnow().isoformat()
comment.author = self.author
else:
comment.modified_date = datetime.utcnow().isoformat()
comment.modified_author = self.author
self.edit_topic(topic)
def delete_comment(self, guid, topic):
if guid in topic.comments:
del topic.comments[guid]
self.edit_topic(topic)
def delete_topic(self, guid):
if guid in self.topics:
del self.topics[guid]
shutil.rmtree(os.path.join(self.filepath, guid))
def write_viewpoints(self, viewpoints, root, topic):
for viewpoint in viewpoints.values():
viewpoint_el = self._create_element(root, "Viewpoints", {"Guid": viewpoint.guid})
text_map = {"Viewpoint": viewpoint.viewpoint, "Snapshot": viewpoint.snapshot, "Index": viewpoint.index}
for key, value in text_map.items():
if value:
self._create_element(viewpoint_el, key, text=value)
self.write_viewpoint(viewpoint, topic)
def write_viewpoint(self, viewpoint, topic):
document = minidom.Document()
root = self._create_element(document, "VisualizationInfo", {"Guid": viewpoint.guid})
self.write_viewpoint_components(viewpoint, root)
self.write_viewpoint_orthogonal_camera(viewpoint, root)
self.write_viewpoint_perspective_camera(viewpoint, root)
self.write_viewpoint_lines(viewpoint, root)
self.write_viewpoint_clipping_planes(viewpoint, root)
self.write_viewpoint_bitmaps(viewpoint, root)
with open(os.path.join(self.filepath, topic.guid, viewpoint.viewpoint), "wb") as f:
f.write(document.toprettyxml(encoding="utf-8"))
def write_viewpoint_components(self, viewpoint, parent):
if not viewpoint.components:
return
components_el = self._create_element(parent, "Components")
if viewpoint.components.view_setup_hints:
view_setup_hints = self._create_element(
components_el,
"ViewSetupHints",
{
"SpacesVisible": viewpoint.components.view_setup_hints.spaces_visible,
"SpaceBoundariesVisible": viewpoint.components.view_setup_hints.space_boundaries_visible,
"OpeningsVisible": viewpoint.components.view_setup_hints.openings_visible,
},
)
if viewpoint.components.selection:
selection_el = self._create_element(components_el, "Selection")
for selection in viewpoint.components.selection:
self.write_component(selection, selection_el)
visibility = self._create_element(
components_el, "Visibility", {"DefaultVisibility": viewpoint.components.visibility.default_visibility}
)
if viewpoint.components.visibility.exceptions:
exceptions_el = self._create_element(visibility, "Exceptions")
for exception in viewpoint.components.visibility.exceptions:
self.write_component(exception, exceptions_el)
if viewpoint.components.coloring:
coloring_el = self._create_element(components_el, "Coloring")
for color in viewpoint.components.coloring:
color_el = self._create_element(coloring_el, "Color", {"Color": color.color})
for component in color.components:
self.write_component(component, color_el)
def write_viewpoint_orthogonal_camera(self, viewpoint, parent):
if not viewpoint.orthogonal_camera:
return
camera = viewpoint.orthogonal_camera
camera_el = self._create_element(parent, "OrthogonalCamera")
camera_view_point = self._create_element(camera_el, "CameraViewPoint")
self.write_vector(camera_view_point, camera.camera_view_point)
camera_direction = self._create_element(camera_el, "CameraDirection")
self.write_vector(camera_direction, camera.camera_direction)
camera_up_vector = self._create_element(camera_el, "CameraUpVector")
self.write_vector(camera_up_vector, camera.camera_up_vector)
self._create_element(camera_el, "ViewToWorldScale", text=camera.view_to_world_scale)
def write_viewpoint_perspective_camera(self, viewpoint, parent):
if not viewpoint.perspective_camera:
return
camera = viewpoint.perspective_camera
camera_el = self._create_element(parent, "PerspectiveCamera")
camera_view_point = self._create_element(camera_el, "CameraViewPoint")
self.write_vector(camera_view_point, camera.camera_view_point)
camera_direction = self._create_element(camera_el, "CameraDirection")
self.write_vector(camera_direction, camera.camera_direction)
camera_up_vector = self._create_element(camera_el, "CameraUpVector")
self.write_vector(camera_up_vector, camera.camera_up_vector)
self._create_element(camera_el, "FieldOfView", text=camera.field_of_view)
def write_viewpoint_lines(self, viewpoint, parent):
if not viewpoint.lines:
return
lines_el = self._create_element(parent, "Lines")
for line in viewpoint.lines:
line_el = self._create_element(lines_el, "Line")
start_point_el = self._create_element(line_el, "StartPoint")
self.write_vector(start_point_el, line.start_point)
end_point_el = self._create_element(line_el, "EndPoint")
self.write_vector(end_point_el, line.end_point)
def write_viewpoint_clipping_planes(self, viewpoint, parent):
if not viewpoint.clipping_planes:
return
planes_el = self._create_element(parent, "ClippingPlanes")
for plane in viewpoint.clipping_planes:
plane_el = self._create_element(planes_el, "ClippingPlane")
location_el = self._create_element(plane_el, "Location")
self.write_vector(location_el, plane.location)
direction_el = self._create_element(plane_el, "Direction")
self.write_vector(direction_el, plane.direction)
def write_viewpoint_bitmaps(self, viewpoint, parent):
if not viewpoint.bitmaps:
return
for bitmap in viewpoint.bitmaps:
bitmap_el = self._create_element(parent, "Bitmap")
text_map = {"Bitmap": bitmap.bitmap_type, "Reference": bitmap.reference}
for key, value in text_map.items():
self._create_element(bitmap_el, key, text=value)
location_el = self._create_element(bitmap_el, "Location")
self.write_vector(location_el, bitmap.location)
normal_el = self._create_element(bitmap_el, "Normal")
self.write_vector(normal_el, bitmap.normal)
up_el = self._create_element(bitmap_el, "Up")
self.write_vector(up_el, bitmap.up)
self._create_element(bitmap_el, "Height", text=bitmap.height)
def write_vector(self, parent, from_obj):
self._create_element(parent, "X", text=from_obj.x)
self._create_element(parent, "Y", text=from_obj.y)
self._create_element(parent, "Z", text=from_obj.z)
def write_component(self, data, parent):
component_el = self._create_element(parent, "Component", {"IfcGuid": data.ifc_guid})
text_map = {"OriginatingSystem": data.originating_system, "AuthoringToolId": data.authoring_tool_id}
for key, value in text_map.items():
if value:
self._create_element(component_el, key, text=value)
def add_viewpoint(self, topic, viewpoint=None):
if not viewpoint:
viewpoint = bcf.data.Viewpoint()
if not viewpoint.guid:
viewpoint.guid = str(uuid.uuid4())
if not viewpoint.viewpoint:
viewpoint.viewpoint = f"{viewpoint.guid}.bcfv"
if viewpoint.snapshot:
topic_filepath = os.path.join(self.filepath, topic.guid)
filepath = os.path.join(topic_filepath, viewpoint.snapshot)
if not os.path.exists(filepath):
filename = viewpoint.guid + os.path.splitext(viewpoint.snapshot)[-1]
copyfile(viewpoint.snapshot, os.path.join(topic_filepath, filename))
viewpoint.snapshot = filename
topic.viewpoints[viewpoint.guid] = viewpoint
self.edit_topic(topic)
def delete_viewpoint(self, guid, topic):
if guid not in topic.viewpoints:
return
viewpoint = topic.viewpoints[guid]
if viewpoint.snapshot:
filepath = os.path.join(self.filepath, topic.guid, viewpoint.snapshot)
if os.path.exists(filepath):
os.remove(filepath)
if viewpoint.viewpoint:
filepath = os.path.join(self.filepath, topic.guid, viewpoint.viewpoint)
if os.path.exists(filepath):
os.remove(filepath)
for bitmap in viewpoint.bitmaps:
if not bitmap.reference:
continue
filepath = os.path.join(self.filepath, topic.guid, bitmap.reference)
if os.path.exists(filepath):
os.remove(filepath)
del topic.viewpoints[guid]
self.edit_topic(topic)
def delete_file(self, topic, index):
if not topic.header:
return
f = topic.header.files.pop(index)
filepath = os.path.join(self.filepath, topic.guid, f.reference)
if not f.is_external and os.path.exists(filepath):
os.remove(filepath)
self.edit_topic(topic)
def delete_bim_snippet(self, topic):
if not topic.bim_snippet:
return
if topic.bim_snippet.reference and not topic.bim_snippet.is_external:
filepath = os.path.join(self.filepath, topic.guid, topic.bim_snippet.reference)
if os.path.exists(filepath):
os.remove(filepath)
topic.bim_snippet = None
self.edit_topic(topic)
def delete_document_reference(self, topic, index):
document_reference = topic.document_references[index]
if document_reference.referenced_document and not document_reference.is_external:
filepath = os.path.join(self.filepath, topic.guid, document_reference.referenced_document)
if os.path.exists(filepath):
os.remove(filepath)
del topic.document_references[index]
self.edit_topic(topic)
def add_document_reference(self, topic, document_reference):
if os.path.exists(document_reference.referenced_document):
topic_filepath = os.path.join(self.filepath, topic.guid)
filename = os.path.basename(document_reference.referenced_document)
copyfile(document_reference.referenced_document, os.path.join(topic_filepath, filename))
document_reference.referenced_document = filename
document_reference.is_external = False
else:
document_reference.is_external = True
if not document_reference.guid:
document_reference.guid = str(uuid.uuid4())
topic.document_references.append(document_reference)
self.edit_topic(topic)
def add_bim_snippet(self, topic, bim_snippet):
if topic.bim_snippet:
self.delete_bim_snippet(topic)
if os.path.exists(bim_snippet.reference):
topic_filepath = os.path.join(self.filepath, topic.guid)
filename = os.path.basename(bim_snippet.reference)
copyfile(bim_snippet.reference, os.path.join(topic_filepath, filename))
bim_snippet.reference = filename
bim_snippet.is_external = False
else:
bim_snippet.is_external = True
topic.bim_snippet = bim_snippet
self.edit_topic(topic)
def add_file(self, topic, header_file):
if os.path.exists(header_file.reference):
topic_filepath = os.path.join(self.filepath, topic.guid)
header_file.filename = os.path.basename(header_file.reference)
copyfile(header_file.reference, os.path.join(topic_filepath, header_file.filename))
header_file.reference = header_file.filename
header_file.is_external = False
header_file.date = datetime.utcnow().isoformat()
if not topic.header:
topic.header = bcf.data.Header()
topic.header.files.append(header_file)
self.edit_topic(topic)
def get_comments(self, guid):
comments = {}
data = self._read_xml(os.path.join(guid, "markup.bcf"), "markup.xsd")
if "Comment" not in data:
return comments
for item in data["Comment"]:
comment = bcf.data.Comment()
mandatory_keys = {"guid": "@Guid", "date": "Date", "author": "Author", "comment": "Comment"}
for key, value in mandatory_keys.items():
setattr(comment, key, item[value])
optional_keys = {"modified_date": "ModifiedDate", "modified_author": "ModifiedAuthor"}
for key, value in optional_keys.items():
if value in item:
setattr(comment, key, item[value])
if "Viewpoint" in item:
viewpoint = bcf.data.Viewpoint()
viewpoint.guid = item["Viewpoint"]["@Guid"]
comment.viewpoint = viewpoint
comments[comment.guid] = comment
self.topics[guid].comments = comments
return comments
def get_viewpoints(self, guid):
viewpoints = {}
data = self._read_xml(os.path.join(guid, "markup.bcf"), "markup.xsd")
if "Viewpoints" not in data:
return viewpoints
for item in data["Viewpoints"]:
viewpoint = self.get_viewpoint(item, guid)
viewpoints[viewpoint.guid] = viewpoint
self.topics[guid].viewpoints = viewpoints
return viewpoints
def get_viewpoint(self, data, topic_guid):
viewpoint = bcf.data.Viewpoint()
viewpoint.guid = data["@Guid"]
optional_keys = {"viewpoint": "Viewpoint", "snapshot": "Snapshot", "index": "Index"}
for key, value in optional_keys.items():
if value in data:
setattr(viewpoint, key, data[value])
visinfo = self._read_xml(os.path.join(topic_guid, viewpoint.viewpoint), "visinfo.xsd")
viewpoint.components = self.get_viewpoint_components(visinfo)
viewpoint.orthogonal_camera = self.get_viewpoint_orthogonal_camera(visinfo)
viewpoint.perspective_camera = self.get_viewpoint_perspective_camera(visinfo)
viewpoint.lines = self.get_viewpoint_lines(visinfo)
viewpoint.clipping_planes = self.get_viewpoint_clipping_planes(visinfo)
viewpoint.bitmaps = self.get_viewpoint_bitmaps(visinfo)
return viewpoint
def get_viewpoint_components(self, visinfo):
if "Components" not in visinfo:
return None
components = bcf.data.Components()
data = visinfo["Components"]
if "ViewSetupHints" in data:
view_setup_hints = bcf.data.ViewSetupHints()
optional_keys = {
"spaces_visible": "@SpacesVisible",
"space_boundaries_visible": "@SpaceBoundariesVisible",
"openings_visible": "@OpeningsVisible",
}
for key, value in optional_keys.items():
if value in data["ViewSetupHints"]:
setattr(view_setup_hints, key, data["ViewSetupHints"][value])
components.view_setup_hints = view_setup_hints
if "Selection" in data and "Component" in data["Selection"]:
for item in data["Selection"]["Component"]:
components.selection.append(self.get_component(item))
if "Visibility" in data:
component_visibility = bcf.data.ComponentVisibility()
if "@DefaultVisibility" in data["Visibility"]:
component_visibility.default_visibility = data["Visibility"]["@DefaultVisibility"]
if "Exceptions" in data["Visibility"] and "Component" in data["Visibility"]["Exceptions"]:
for item in data["Visibility"]["Exceptions"]["Component"]:
component_visibility.exceptions.append(self.get_component(item))
components.visibility = component_visibility
if "Coloring" in data and "Color" in data["Coloring"]:
for item in data["Coloring"]["Color"]:
color = bcf.data.Color()
color.color = item["@Color"]
for item2 in item["Component"]:
color.components.append(self.get_component(item2))
components.coloring.append(color)
return components
def get_viewpoint_orthogonal_camera(self, visinfo):
if "OrthogonalCamera" not in visinfo:
return None
camera = bcf.data.OrthogonalCamera()
data = visinfo["OrthogonalCamera"]
self.set_vector(camera.camera_view_point, data["CameraViewPoint"])
self.set_vector(camera.camera_direction, data["CameraDirection"])
self.set_vector(camera.camera_up_vector, data["CameraUpVector"])
camera.view_to_world_scale = data["ViewToWorldScale"]
return camera
def get_viewpoint_perspective_camera(self, visinfo):
if "PerspectiveCamera" not in visinfo:
return None
camera = bcf.data.PerspectiveCamera()
data = visinfo["PerspectiveCamera"]
self.set_vector(camera.camera_view_point, data["CameraViewPoint"])
self.set_vector(camera.camera_direction, data["CameraDirection"])
self.set_vector(camera.camera_up_vector, data["CameraUpVector"])
camera.field_of_view = data["FieldOfView"]
return camera
def get_viewpoint_lines(self, visinfo):
if "Lines" not in visinfo:
return []
lines = []
for item in visinfo["Lines"]["Line"]:
line = bcf.data.Line()
self.set_vector(line.start_point, item["StartPoint"])
self.set_vector(line.end_point, item["EndPoint"])
lines.append(line)
return lines
def get_viewpoint_clipping_planes(self, visinfo):
if "ClippingPlanes" not in visinfo:
return []
planes = []
for item in visinfo["ClippingPlanes"]["ClippingPlane"]:
plane = bcf.data.ClippingPlane()
self.set_vector(plane.location, item["Location"])
self.set_vector(plane.direction, item["Direction"])
planes.append(plane)
return planes
def get_viewpoint_bitmaps(self, visinfo):
if "Bitmap" not in visinfo:
return []
bitmaps = []
for item in visinfo["Bitmap"]:
bitmap = bcf.data.Bitmap()
bitmap.reference = item["Reference"]
bitmap.bitmap_type = item["Bitmap"].upper()
self.set_vector(bitmap.location, item["Location"])
self.set_vector(bitmap.normal, item["Normal"])
self.set_vector(bitmap.up, item["Up"])
bitmap.height = item["Height"]
bitmaps.append(bitmap)
return bitmaps
def set_vector(self, to_obj, from_xml):
to_obj.x = from_xml["X"]
to_obj.y = from_xml["Y"]
to_obj.z = from_xml["Z"]
def get_component(self, data):
component = bcf.data.Component()
optional_keys = {
"originating_system": "OriginatingSystem",
"authoring_tool_id": "AuthoringToolId",
"ifc_guid": "@IfcGuid",
}
for key, value in optional_keys.items():
if value in data:
setattr(component, key, data[value])
return component
def close_project(self):
shutil.rmtree(self.filepath)
def _read_xml(self, filename, xsd):
schema = XMLSchema(os.path.join(cwd, "xsd", xsd))
filepath = os.path.join(self.filepath, filename)
(data, errors) = schema.to_dict(filepath, validation="lax")
for error in errors:
self.logger.error(error)
return data
def _create_element(self, parent, name, attributes={}, text=None):
element = self.document.createElement(name)
for key, value in attributes.items():
if isinstance(value, bool):
element.setAttribute(key, str(value).lower())
elif value:
element.setAttribute(key, value)
if text is not None:
text = self.document.createTextNode(str(text))
element.appendChild(text)
parent.appendChild(element)
return element
def __del__(self):
self.close_project()
def extract_project(filepath):
if not filepath:
return
zip_file = zipfile.ZipFile(filepath)
filepath = tempfile.mkdtemp()
zip_file.extractall(filepath)
return filepath
+771
View File
@@ -0,0 +1,771 @@
import os
import uuid
import shutil
import zipfile
import logging
import tempfile
import bcf.v2.data
from datetime import datetime
from xml.dom import minidom
from xmlschema import XMLSchema
from contextlib import contextmanager
from shutil import copyfile
cwd = os.path.dirname(os.path.realpath(__file__))
@contextmanager
def cd(newdir):
prevdir = os.getcwd()
os.chdir(os.path.expanduser(newdir))
try:
yield
finally:
os.chdir(prevdir)
class BcfXml:
def __init__(self):
self.filepath = None
self.logger = logging.getLogger("bcfxml")
self.author = "john@doe.com"
self.project = bcf.v2.data.Project()
self.version = "2.1"
self.topics = {}
def new_project(self):
self.project.project_id = str(uuid.uuid4())
self.project.name = "New Project"
self.topics = {}
if self.filepath:
self.close_project()
self.filepath = tempfile.mkdtemp()
self.edit_project()
self.edit_version()
def get_project(self, filepath=None):
if not filepath:
return self.project
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):
self.document = minidom.Document()
root = self._create_element(self.document, "ProjectExtension")
project = self._create_element(root, "Project", {"ProjectId": self.project.project_id})
self._create_element(project, "Name", text=self.project.name)
self._create_element(root, "ExtensionSchema", text="extensions.xsd")
with open(os.path.join(self.filepath, "project.bcfp"), "wb") as f:
f.write(self.document.toprettyxml(encoding="utf-8"))
def save_project(self, filepath):
with cd(self.filepath):
zip_file = zipfile.ZipFile(filepath, "w", zipfile.ZIP_DEFLATED)
for root, dirs, files in os.walk("./"):
for file in files:
zip_file.write(os.path.join(root, file))
zip_file.close()
def get_version(self):
data = self._read_xml("bcf.version", "version.xsd")
self.version = data["@VersionId"]
return self.version
def edit_version(self):
self.document = minidom.Document()
root = self._create_element(self.document, "Version", {"VersionId": self.version})
version = self._create_element(root, "DetailedVersion", text=self.version)
with open(os.path.join(self.filepath, "bcf.version"), "wb") as f:
f.write(self.document.toprettyxml(encoding="utf-8"))
def get_topics(self):
self.topics = {}
topics = []
subdirs = []
for (dirpath, dirnames, filenames) in os.walk(self.filepath):
subdirs = dirnames
break
for subdir in subdirs:
try:
uuid.UUID(subdir)
except ValueError:
continue
if not os.path.exists(os.path.join(self.filepath, subdir, "markup.bcf")):
continue
self.topics[subdir] = self.get_topic(subdir)
return self.topics
def get_header(self, guid):
data = self._read_xml(os.path.join(guid, "markup.bcf"), "markup.xsd")
if "Header" not in data:
return
header = bcf.v2.data.Header()
for item in data["Header"]["File"]:
header_file = bcf.v2.data.HeaderFile()
optional_keys = {
"filename": "Filename",
"date": "Date",
"reference": "Reference",
"ifc_project": "@IfcProject",
"ifc_spatial_structure_element": "@IfcSpatialStructureElement",
"is_external": "@isExternal",
}
for key, value in optional_keys.items():
if value in item:
setattr(header_file, key, item[value])
header.files.append(header_file)
self.topics[guid].header = header
return header
def get_topic(self, guid):
if guid in self.topics:
return self.topics[guid]
data = self._read_xml(os.path.join(guid, "markup.bcf"), "markup.xsd")
topic = bcf.v2.data.Topic()
self.topics[guid] = topic
mandatory_keys = {
"guid": "@Guid",
"title": "Title",
"creation_date": "CreationDate",
"creation_author": "CreationAuthor",
}
for key, value in mandatory_keys.items():
setattr(topic, key, data["Topic"][value])
optional_keys = {
"priority": "Priority",
"index": "Index",
"labels": "Labels",
"reference_links": "ReferenceLink",
"modified_date": "ModifiedDate",
"modified_author": "ModifiedAuthor",
"due_date": "DueDate",
"assigned_to": "AssignedTo",
"stage": "Stage",
"description": "Description",
"topic_status": "@TopicStatus",
"topic_type": "@TopicType",
}
for key, value in optional_keys.items():
if value in data["Topic"]:
setattr(topic, key, data["Topic"][value])
if "BimSnippet" in data["Topic"]:
bim_snippet = bcf.v2.data.BimSnippet()
keys = {
"snippet_type": "@SnippetType",
"is_external": "@IsExternal",
"reference": "Reference",
"reference_schema": "ReferenceSchema",
}
for key, value in keys.items():
if value in data["Topic"]["BimSnippet"]:
setattr(bim_snippet, key, data["Topic"]["BimSnippet"][value])
topic.bim_snippet = bim_snippet
if "DocumentReference" in data["Topic"]:
for item in data["Topic"]["DocumentReference"]:
document_reference = bcf.v2.data.DocumentReference()
keys = {
"referenced_document": "ReferencedDocument",
"is_external": "@IsExternal",
"guid": "@Guid",
"description": "Description",
}
for key, value in keys.items():
if value in item:
setattr(document_reference, key, item[value])
topic.document_references.append(document_reference)
if "RelatedTopic" in data["Topic"]:
for item in data["Topic"]["RelatedTopic"]:
related_topic = bcf.v2.data.RelatedTopic()
related_topic.guid = item["@Guid"]
topic.related_topics.append(related_topic)
return topic
def add_topic(self, topic=None):
if topic is None:
topic = bcf.v2.data.Topic()
if not topic.guid:
topic.guid = str(uuid.uuid4())
if not topic.title:
topic.title = "New Topic"
os.mkdir(os.path.join(self.filepath, topic.guid))
self.edit_topic(topic)
return topic
def edit_topic(self, topic):
if not topic.creation_date:
topic.creation_date = datetime.utcnow().isoformat()
topic.creation_author = self.author
else:
topic.modified_date = datetime.utcnow().isoformat()
topic.modified_author = self.author
self.document = minidom.Document()
root = self._create_element(self.document, "Markup")
self.write_header(topic.header, root)
topic_el = self._create_element(
root,
"Topic",
{
"Guid": topic.guid,
"TopicType": topic.topic_type,
"TopicStatus": topic.topic_status,
},
)
for reference_link in topic.reference_links:
self._create_element(topic_el, "ReferenceLink", text=reference_link)
text_map = {
"Title": topic.title,
"Priority": topic.priority,
"Index": topic.index,
}
for key, value in text_map.items():
if value:
self._create_element(topic_el, key, text=value)
for label in topic.labels:
self._create_element(topic_el, "Labels", text=label)
text_map = {
"CreationDate": topic.creation_date,
"CreationAuthor": topic.creation_author,
"ModifiedDate": topic.modified_date,
"ModifiedAuthor": topic.modified_author,
"DueDate": topic.due_date,
"AssignedTo": topic.assigned_to,
"Stage": topic.stage,
"Description": topic.description,
}
for key, value in text_map.items():
if value:
self._create_element(topic_el, key, text=value)
if topic.bim_snippet:
bim_snippet = self._create_element(
topic_el,
"BimSnippet",
{"SnippetType": topic.bim_snippet.snippet_type, "isExternal": topic.bim_snippet.is_external},
)
self._create_element(bim_snippet, "Reference", text=topic.bim_snippet.reference)
self._create_element(bim_snippet, "ReferenceSchema", text=topic.bim_snippet.reference_schema)
for reference in topic.document_references:
reference_el = self._create_element(
topic_el, "DocumentReference", {"Guid": reference.guid, "isExternal": reference.is_external}
)
self._create_element(reference_el, "ReferencedDocument", text=reference.referenced_document)
self._create_element(reference_el, "Description", text=reference.description)
for related_topic in topic.related_topics:
self._create_element(topic_el, "RelatedTopic", {"Guid": related_topic.guid})
self.write_comments(topic.comments, root)
self.write_viewpoints(topic.viewpoints, root, topic)
with open(os.path.join(self.filepath, topic.guid, "markup.bcf"), "wb") as f:
f.write(self.document.toprettyxml(encoding="utf-8"))
def write_header(self, header, root):
if not header or not header.files:
return
header_el = self._create_element(root, "Header")
for f in header.files:
file_el = self._create_element(
header_el,
"File",
{
"IfcProject": f.ifc_project,
"IfcSpatialStructureElement": f.ifc_spatial_structure_element,
"isExternal": f.is_external,
},
)
if f.filename:
self._create_element(file_el, "Filename", text=f.filename)
if f.date:
self._create_element(file_el, "Date", text=f.date)
if f.reference:
self._create_element(file_el, "Reference", text=f.reference)
def write_comments(self, comments, root):
for comment in comments.values():
comment_el = self._create_element(root, "Comment", {"Guid": comment.guid})
text_map = {
"Date": comment.date,
"Author": comment.author,
"Comment": comment.comment,
"ModifiedDate": comment.modified_date,
"ModifiedAuthor": comment.modified_author,
}
for key, value in text_map.items():
if value:
self._create_element(comment_el, key, text=value)
if comment.viewpoint:
self._create_element(comment_el, "Viewpoint", {"Guid": comment.viewpoint.guid})
def add_comment(self, topic, comment=None):
if comment is None:
comment = bcf.v2.data.Comment()
if not comment.guid:
comment.guid = str(uuid.uuid4())
if not comment.comment:
comment.comment = "'Free software' is a matter of liberty, not price. To understand the concept, you should think of 'free' as in 'free speech,' not as in 'free beer'."
topic.comments[comment.guid] = comment
self.edit_comment(comment, topic)
def edit_comment(self, comment, topic):
if not comment.date:
comment.date = datetime.utcnow().isoformat()
comment.author = self.author
else:
comment.modified_date = datetime.utcnow().isoformat()
comment.modified_author = self.author
self.edit_topic(topic)
def delete_comment(self, guid, topic):
if guid in topic.comments:
del topic.comments[guid]
self.edit_topic(topic)
def delete_topic(self, guid):
if guid in self.topics:
del self.topics[guid]
shutil.rmtree(os.path.join(self.filepath, guid))
def write_viewpoints(self, viewpoints, root, topic):
for viewpoint in viewpoints.values():
viewpoint_el = self._create_element(root, "Viewpoints", {"Guid": viewpoint.guid})
text_map = {"Viewpoint": viewpoint.viewpoint, "Snapshot": viewpoint.snapshot, "Index": viewpoint.index}
for key, value in text_map.items():
if value:
self._create_element(viewpoint_el, key, text=value)
self.write_viewpoint(viewpoint, topic)
def write_viewpoint(self, viewpoint, topic):
document = minidom.Document()
root = self._create_element(document, "VisualizationInfo", {"Guid": viewpoint.guid})
self.write_viewpoint_components(viewpoint, root)
self.write_viewpoint_orthogonal_camera(viewpoint, root)
self.write_viewpoint_perspective_camera(viewpoint, root)
self.write_viewpoint_lines(viewpoint, root)
self.write_viewpoint_clipping_planes(viewpoint, root)
self.write_viewpoint_bitmaps(viewpoint, root)
with open(os.path.join(self.filepath, topic.guid, viewpoint.viewpoint), "wb") as f:
f.write(document.toprettyxml(encoding="utf-8"))
def write_viewpoint_components(self, viewpoint, parent):
if not viewpoint.components:
return
components_el = self._create_element(parent, "Components")
if viewpoint.components.view_setup_hints:
view_setup_hints = self._create_element(
components_el,
"ViewSetupHints",
{
"SpacesVisible": viewpoint.components.view_setup_hints.spaces_visible,
"SpaceBoundariesVisible": viewpoint.components.view_setup_hints.space_boundaries_visible,
"OpeningsVisible": viewpoint.components.view_setup_hints.openings_visible,
},
)
if viewpoint.components.selection:
selection_el = self._create_element(components_el, "Selection")
for selection in viewpoint.components.selection:
self.write_component(selection, selection_el)
visibility = self._create_element(
components_el, "Visibility", {"DefaultVisibility": viewpoint.components.visibility.default_visibility}
)
if viewpoint.components.visibility.exceptions:
exceptions_el = self._create_element(visibility, "Exceptions")
for exception in viewpoint.components.visibility.exceptions:
self.write_component(exception, exceptions_el)
if viewpoint.components.coloring:
coloring_el = self._create_element(components_el, "Coloring")
for color in viewpoint.components.coloring:
color_el = self._create_element(coloring_el, "Color", {"Color": color.color})
for component in color.components:
self.write_component(component, color_el)
def write_viewpoint_orthogonal_camera(self, viewpoint, parent):
if not viewpoint.orthogonal_camera:
return
camera = viewpoint.orthogonal_camera
camera_el = self._create_element(parent, "OrthogonalCamera")
camera_view_point = self._create_element(camera_el, "CameraViewPoint")
self.write_vector(camera_view_point, camera.camera_view_point)
camera_direction = self._create_element(camera_el, "CameraDirection")
self.write_vector(camera_direction, camera.camera_direction)
camera_up_vector = self._create_element(camera_el, "CameraUpVector")
self.write_vector(camera_up_vector, camera.camera_up_vector)
self._create_element(camera_el, "ViewToWorldScale", text=camera.view_to_world_scale)
def write_viewpoint_perspective_camera(self, viewpoint, parent):
if not viewpoint.perspective_camera:
return
camera = viewpoint.perspective_camera
camera_el = self._create_element(parent, "PerspectiveCamera")
camera_view_point = self._create_element(camera_el, "CameraViewPoint")
self.write_vector(camera_view_point, camera.camera_view_point)
camera_direction = self._create_element(camera_el, "CameraDirection")
self.write_vector(camera_direction, camera.camera_direction)
camera_up_vector = self._create_element(camera_el, "CameraUpVector")
self.write_vector(camera_up_vector, camera.camera_up_vector)
self._create_element(camera_el, "FieldOfView", text=camera.field_of_view)
def write_viewpoint_lines(self, viewpoint, parent):
if not viewpoint.lines:
return
lines_el = self._create_element(parent, "Lines")
for line in viewpoint.lines:
line_el = self._create_element(lines_el, "Line")
start_point_el = self._create_element(line_el, "StartPoint")
self.write_vector(start_point_el, line.start_point)
end_point_el = self._create_element(line_el, "EndPoint")
self.write_vector(end_point_el, line.end_point)
def write_viewpoint_clipping_planes(self, viewpoint, parent):
if not viewpoint.clipping_planes:
return
planes_el = self._create_element(parent, "ClippingPlanes")
for plane in viewpoint.clipping_planes:
plane_el = self._create_element(planes_el, "ClippingPlane")
location_el = self._create_element(plane_el, "Location")
self.write_vector(location_el, plane.location)
direction_el = self._create_element(plane_el, "Direction")
self.write_vector(direction_el, plane.direction)
def write_viewpoint_bitmaps(self, viewpoint, parent):
if not viewpoint.bitmaps:
return
for bitmap in viewpoint.bitmaps:
bitmap_el = self._create_element(parent, "Bitmap")
text_map = {"Bitmap": bitmap.bitmap_format, "Reference": bitmap.reference}
for key, value in text_map.items():
self._create_element(bitmap_el, key, text=value)
location_el = self._create_element(bitmap_el, "Location")
self.write_vector(location_el, bitmap.location)
normal_el = self._create_element(bitmap_el, "Normal")
self.write_vector(normal_el, bitmap.normal)
up_el = self._create_element(bitmap_el, "Up")
self.write_vector(up_el, bitmap.up)
self._create_element(bitmap_el, "Height", text=bitmap.height)
def write_vector(self, parent, from_obj):
self._create_element(parent, "X", text=from_obj.x)
self._create_element(parent, "Y", text=from_obj.y)
self._create_element(parent, "Z", text=from_obj.z)
def write_component(self, data, parent):
component_el = self._create_element(parent, "Component", {"IfcGuid": data.ifc_guid})
text_map = {"OriginatingSystem": data.originating_system, "AuthoringToolId": data.authoring_tool_id}
for key, value in text_map.items():
if value:
self._create_element(component_el, key, text=value)
def add_viewpoint(self, topic, viewpoint=None):
if not viewpoint:
viewpoint = bcf.v2.data.Viewpoint()
if not viewpoint.guid:
viewpoint.guid = str(uuid.uuid4())
if not viewpoint.viewpoint:
viewpoint.viewpoint = f"{viewpoint.guid}.bcfv"
if viewpoint.snapshot:
topic_filepath = os.path.join(self.filepath, topic.guid)
filepath = os.path.join(topic_filepath, viewpoint.snapshot)
if not os.path.exists(filepath):
filename = viewpoint.guid + os.path.splitext(viewpoint.snapshot)[-1]
copyfile(viewpoint.snapshot, os.path.join(topic_filepath, filename))
viewpoint.snapshot = filename
topic.viewpoints[viewpoint.guid] = viewpoint
self.edit_topic(topic)
def delete_viewpoint(self, guid, topic):
if guid not in topic.viewpoints:
return
viewpoint = topic.viewpoints[guid]
if viewpoint.snapshot:
filepath = os.path.join(self.filepath, topic.guid, viewpoint.snapshot)
if os.path.exists(filepath):
os.remove(filepath)
if viewpoint.viewpoint:
filepath = os.path.join(self.filepath, topic.guid, viewpoint.viewpoint)
if os.path.exists(filepath):
os.remove(filepath)
for bitmap in viewpoint.bitmaps:
if not bitmap.reference:
continue
filepath = os.path.join(self.filepath, topic.guid, bitmap.reference)
if os.path.exists(filepath):
os.remove(filepath)
del topic.viewpoints[guid]
self.edit_topic(topic)
def delete_file(self, topic, index):
if not topic.header:
return
f = topic.header.files.pop(index)
filepath = os.path.join(self.filepath, topic.guid, f.reference)
if not f.is_external and os.path.exists(filepath):
os.remove(filepath)
self.edit_topic(topic)
def delete_bim_snippet(self, topic):
if not topic.bim_snippet:
return
if topic.bim_snippet.reference and not topic.bim_snippet.is_external:
filepath = os.path.join(self.filepath, topic.guid, topic.bim_snippet.reference)
if os.path.exists(filepath):
os.remove(filepath)
topic.bim_snippet = None
self.edit_topic(topic)
def delete_document_reference(self, topic, index):
document_reference = topic.document_references[index]
if document_reference.referenced_document and not document_reference.is_external:
filepath = os.path.join(self.filepath, topic.guid, document_reference.referenced_document)
if os.path.exists(filepath):
os.remove(filepath)
del topic.document_references[index]
self.edit_topic(topic)
def add_document_reference(self, topic, document_reference):
if os.path.exists(document_reference.referenced_document):
topic_filepath = os.path.join(self.filepath, topic.guid)
filename = os.path.basename(document_reference.referenced_document)
copyfile(document_reference.referenced_document, os.path.join(topic_filepath, filename))
document_reference.referenced_document = filename
document_reference.is_external = False
else:
document_reference.is_external = True
if not document_reference.guid:
document_reference.guid = str(uuid.uuid4())
topic.document_references.append(document_reference)
self.edit_topic(topic)
def add_bim_snippet(self, topic, bim_snippet):
if topic.bim_snippet:
self.delete_bim_snippet(topic)
if os.path.exists(bim_snippet.reference):
topic_filepath = os.path.join(self.filepath, topic.guid)
filename = os.path.basename(bim_snippet.reference)
copyfile(bim_snippet.reference, os.path.join(topic_filepath, filename))
bim_snippet.reference = filename
bim_snippet.is_external = False
else:
bim_snippet.is_external = True
topic.bim_snippet = bim_snippet
self.edit_topic(topic)
def add_file(self, topic, header_file):
if os.path.exists(header_file.reference):
topic_filepath = os.path.join(self.filepath, topic.guid)
header_file.filename = os.path.basename(header_file.reference)
copyfile(header_file.reference, os.path.join(topic_filepath, header_file.filename))
header_file.reference = header_file.filename
header_file.is_external = False
header_file.date = datetime.utcnow().isoformat()
if not topic.header:
topic.header = bcf.v2.data.Header()
topic.header.files.append(header_file)
self.edit_topic(topic)
def get_comments(self, guid):
comments = {}
data = self._read_xml(os.path.join(guid, "markup.bcf"), "markup.xsd")
if "Comment" not in data:
return comments
for item in data["Comment"]:
comment = bcf.v2.data.Comment()
mandatory_keys = {"guid": "@Guid", "date": "Date", "author": "Author", "comment": "Comment"}
for key, value in mandatory_keys.items():
setattr(comment, key, item[value])
optional_keys = {"modified_date": "ModifiedDate", "modified_author": "ModifiedAuthor"}
for key, value in optional_keys.items():
if value in item:
setattr(comment, key, item[value])
if "Viewpoint" in item:
viewpoint = bcf.v2.data.Viewpoint()
viewpoint.guid = item["Viewpoint"]["@Guid"]
comment.viewpoint = viewpoint
comments[comment.guid] = comment
self.topics[guid].comments = comments
return comments
def get_viewpoints(self, guid):
viewpoints = {}
data = self._read_xml(os.path.join(guid, "markup.bcf"), "markup.xsd")
if "Viewpoints" not in data:
return viewpoints
for item in data["Viewpoints"]:
viewpoint = self.get_viewpoint(item, guid)
viewpoints[viewpoint.guid] = viewpoint
self.topics[guid].viewpoints = viewpoints
return viewpoints
def get_viewpoint(self, data, topic_guid):
viewpoint = bcf.v2.data.Viewpoint()
viewpoint.guid = data["@Guid"]
optional_keys = {"viewpoint": "Viewpoint", "snapshot": "Snapshot", "index": "Index"}
for key, value in optional_keys.items():
if value in data:
setattr(viewpoint, key, data[value])
visinfo = self._read_xml(os.path.join(topic_guid, viewpoint.viewpoint), "visinfo.xsd")
viewpoint.components = self.get_viewpoint_components(visinfo)
viewpoint.orthogonal_camera = self.get_viewpoint_orthogonal_camera(visinfo)
viewpoint.perspective_camera = self.get_viewpoint_perspective_camera(visinfo)
viewpoint.lines = self.get_viewpoint_lines(visinfo)
viewpoint.clipping_planes = self.get_viewpoint_clipping_planes(visinfo)
viewpoint.bitmaps = self.get_viewpoint_bitmaps(visinfo)
return viewpoint
def get_viewpoint_components(self, visinfo):
if "Components" not in visinfo:
return None
components = bcf.v2.data.Components()
data = visinfo["Components"]
if "ViewSetupHints" in data:
view_setup_hints = bcf.v2.data.ViewSetupHints()
optional_keys = {
"spaces_visible": "@SpacesVisible",
"space_boundaries_visible": "@SpaceBoundariesVisible",
"openings_visible": "@OpeningsVisible",
}
for key, value in optional_keys.items():
if value in data["ViewSetupHints"]:
setattr(view_setup_hints, key, data["ViewSetupHints"][value])
components.view_setup_hints = view_setup_hints
if "Selection" in data and "Component" in data["Selection"]:
for item in data["Selection"]["Component"]:
components.selection.append(self.get_component(item))
if "Visibility" in data:
component_visibility = bcf.v2.data.ComponentVisibility()
if "@DefaultVisibility" in data["Visibility"]:
component_visibility.default_visibility = data["Visibility"]["@DefaultVisibility"]
if "Exceptions" in data["Visibility"] and "Component" in data["Visibility"]["Exceptions"]:
for item in data["Visibility"]["Exceptions"]["Component"]:
component_visibility.exceptions.append(self.get_component(item))
components.visibility = component_visibility
if "Coloring" in data and "Color" in data["Coloring"]:
for item in data["Coloring"]["Color"]:
color = bcf.v2.data.Color()
color.color = item["@Color"]
for item2 in item["Component"]:
color.components.append(self.get_component(item2))
components.coloring.append(color)
return components
def get_viewpoint_orthogonal_camera(self, visinfo):
if "OrthogonalCamera" not in visinfo:
return None
camera = bcf.v2.data.OrthogonalCamera()
data = visinfo["OrthogonalCamera"]
self.set_vector(camera.camera_view_point, data["CameraViewPoint"])
self.set_vector(camera.camera_direction, data["CameraDirection"])
self.set_vector(camera.camera_up_vector, data["CameraUpVector"])
camera.view_to_world_scale = data["ViewToWorldScale"]
return camera
def get_viewpoint_perspective_camera(self, visinfo):
if "PerspectiveCamera" not in visinfo:
return None
camera = bcf.v2.data.PerspectiveCamera()
data = visinfo["PerspectiveCamera"]
self.set_vector(camera.camera_view_point, data["CameraViewPoint"])
self.set_vector(camera.camera_direction, data["CameraDirection"])
self.set_vector(camera.camera_up_vector, data["CameraUpVector"])
camera.field_of_view = data["FieldOfView"]
return camera
def get_viewpoint_lines(self, visinfo):
if "Lines" not in visinfo:
return []
lines = []
for item in visinfo["Lines"]["Line"]:
line = bcf.v2.data.Line()
self.set_vector(line.start_point, item["StartPoint"])
self.set_vector(line.end_point, item["EndPoint"])
lines.append(line)
return lines
def get_viewpoint_clipping_planes(self, visinfo):
if "ClippingPlanes" not in visinfo:
return []
planes = []
for item in visinfo["ClippingPlanes"]["ClippingPlane"]:
plane = bcf.v2.data.ClippingPlane()
self.set_vector(plane.location, item["Location"])
self.set_vector(plane.direction, item["Direction"])
planes.append(plane)
return planes
def get_viewpoint_bitmaps(self, visinfo):
if "Bitmap" not in visinfo:
return []
bitmaps = []
for item in visinfo["Bitmap"]:
bitmap = bcf.v2.data.Bitmap()
bitmap.reference = item["Reference"]
bitmap.bitmap_format = item["Bitmap"].upper()
self.set_vector(bitmap.location, item["Location"])
self.set_vector(bitmap.normal, item["Normal"])
self.set_vector(bitmap.up, item["Up"])
bitmap.height = item["Height"]
bitmaps.append(bitmap)
return bitmaps
def set_vector(self, to_obj, from_xml):
to_obj.x = from_xml["X"]
to_obj.y = from_xml["Y"]
to_obj.z = from_xml["Z"]
def get_component(self, data):
component = bcf.v2.data.Component()
optional_keys = {
"originating_system": "OriginatingSystem",
"authoring_tool_id": "AuthoringToolId",
"ifc_guid": "@IfcGuid",
}
for key, value in optional_keys.items():
if value in data:
setattr(component, key, data[value])
return component
def close_project(self):
shutil.rmtree(self.filepath)
def _read_xml(self, filename, xsd):
schema = XMLSchema(os.path.join(cwd, "xsd", xsd))
filepath = os.path.join(self.filepath, filename)
(data, errors) = schema.to_dict(filepath, validation="lax")
for error in errors:
self.logger.error(error)
return data
def _create_element(self, parent, name, attributes={}, text=None):
element = self.document.createElement(name)
for key, value in attributes.items():
if isinstance(value, bool):
element.setAttribute(key, str(value).lower())
elif value:
element.setAttribute(key, value)
if text is not None:
text = self.document.createTextNode(str(text))
element.appendChild(text)
parent.appendChild(element)
return element
def __del__(self):
self.close_project()
@@ -2,6 +2,7 @@ class Project:
def __init__(self):
self.project_id = ""
self.name = ""
self.extension_schema = ""
class BimSnippet:
@@ -156,7 +157,7 @@ class Bitmap:
def __init__(self):
self.reference = "" # Only in BCF-XML
self.bitmap_data = None # Only in BCF-API
self.bitmap_type = "PNG" # Enum of png or jpg
self.bitmap_format = "PNG" # Enum of png or jpg
self.location = Point()
self.normal = Direction()
self.up = Direction()
+827
View File
@@ -0,0 +1,827 @@
import os
import uuid
import shutil
import zipfile
import logging
import tempfile
import bcf.v3.data
from datetime import datetime
from xml.dom import minidom
from xmlschema import XMLSchema
from contextlib import contextmanager
from shutil import copyfile
cwd = os.path.dirname(os.path.realpath(__file__))
@contextmanager
def cd(newdir):
prevdir = os.getcwd()
os.chdir(os.path.expanduser(newdir))
try:
yield
finally:
os.chdir(prevdir)
class BcfXml:
def __init__(self):
self.filepath = None
self.logger = logging.getLogger("bcfxml")
self.author = "john@doe.com"
self.project = bcf.v3.data.Project()
self.version = "3.0"
self.topics = {}
def new_project(self):
self.project.project_id = str(uuid.uuid4())
self.project.name = "New Project"
self.topics = {}
if self.filepath:
self.close_project()
self.filepath = tempfile.mkdtemp()
self.edit_project()
self.edit_version()
def get_project(self, filepath=None):
if os.path.isfile(os.path.join(self.filepath, "project.bcfp")):
data = self._read_xml("project.bcfp", "project.xsd")
self.project.project_id = data["Project"]["@ProjectId"]
self.project.name = data["Project"].get("Name")
return self.project
def edit_project(self):
self.document = minidom.Document()
root = self._create_element(self.document, "ProjectInfo")
project = self._create_element(root, "Project", {"ProjectId": self.project.project_id})
if self.project.name:
self._create_element(project, "Name", text=self.project.name)
with open(os.path.join(self.filepath, "project.bcfp"), "wb") as f:
f.write(self.document.toprettyxml(encoding="utf-8"))
def save_project(self, filepath):
with cd(self.filepath):
zip_file = zipfile.ZipFile(filepath, "w", zipfile.ZIP_DEFLATED)
for root, dirs, files in os.walk("./"):
for file in files:
zip_file.write(os.path.join(root, file))
zip_file.close()
def get_version(self):
data = self._read_xml("bcf.version", "version.xsd")
self.version = data["@VersionId"]
return self.version
def edit_version(self):
self.document = minidom.Document()
root = self._create_element(self.document, "Version", {"VersionId": self.version})
with open(os.path.join(self.filepath, "bcf.version"), "wb") as f:
f.write(self.document.toprettyxml(encoding="utf-8"))
def get_topics(self):
self.topics = {}
topics = []
subdirs = []
for (dirpath, dirnames, filenames) in os.walk(self.filepath):
subdirs = dirnames
break
for subdir in subdirs:
try:
uuid.UUID(subdir)
except ValueError:
continue
if not os.path.exists(os.path.join(self.filepath, subdir, "markup.bcf")):
continue
self.topics[subdir] = self.get_topic(subdir)
return self.topics
def get_header(self, guid):
data = self._read_xml(os.path.join(guid, "markup.bcf"), "markup.xsd")
if "Header" not in data:
return
header = bcf.v3.data.Header()
if data["Header"].get("Files"):
for item in data["Header"]["Files"].get("File", []):
header_file = bcf.v3.data.HeaderFile()
optional_keys = {
"filename": "Filename",
"date": "Date",
"reference": "Reference",
"ifc_project": "@IfcProject",
"ifc_spatial_structure_element": "@IfcSpatialStructureElement",
"is_external": "@IsExternal",
}
for key, value in optional_keys.items():
if value in item:
setattr(header_file, key, item[value])
header.files.append(header_file)
self.topics[guid].header = header
return header
def get_topic(self, guid):
if guid in self.topics:
return self.topics[guid]
data = self._read_xml(os.path.join(guid, "markup.bcf"), "markup.xsd")
topic = bcf.v3.data.Topic()
self.topics[guid] = topic
mandatory_keys = {
"guid": "@Guid",
"title": "Title",
"creation_date": "CreationDate",
"creation_author": "CreationAuthor",
"topic_status": "@TopicStatus",
"topic_type": "@TopicType",
}
for key, value in mandatory_keys.items():
setattr(topic, key, data["Topic"][value])
optional_keys = {
"priority": "Priority",
"index": "Index",
"modified_date": "ModifiedDate",
"modified_author": "ModifiedAuthor",
"due_date": "DueDate",
"assigned_to": "AssignedTo",
"stage": "Stage",
"description": "Description",
"server_assigned_id": "@ServerAssignedId",
}
for key, value in optional_keys.items():
if value in data["Topic"]:
setattr(topic, key, data["Topic"][value])
if data["Topic"].get("ReferenceLinks"):
topic.reference_links.extend(data["Topic"]["ReferenceLinks"].get("ReferenceLink", []))
if data["Topic"].get("Labels"):
topic.labels.extend(data["Topic"]["Labels"].get("Label", []))
if "BimSnippet" in data["Topic"]:
bim_snippet = bcf.v3.data.BimSnippet()
keys = {
"snippet_type": "@SnippetType",
"is_external": "@IsExternal",
"reference": "Reference",
"reference_schema": "ReferenceSchema",
}
for key, value in keys.items():
if value in data["Topic"]["BimSnippet"]:
setattr(bim_snippet, key, data["Topic"]["BimSnippet"][value])
topic.bim_snippet = bim_snippet
if data["Topic"].get("DocumentReferences"):
for item in data["Topic"]["DocumentReferences"].get("DocumentReference", []):
document_reference = bcf.v3.data.DocumentReference()
keys = {
"document_guid": "DocumentGuid",
"url": "Url",
"guid": "@Guid",
"description": "Description",
}
for key, value in keys.items():
if value in item:
setattr(document_reference, key, item[value])
topic.document_references.append(document_reference)
if data["Topic"].get("RelatedTopics"):
for item in data["Topic"]["RelatedTopics"].get("RelatedTopic", []):
related_topic = bcf.v3.data.RelatedTopic()
related_topic.guid = item["@Guid"]
topic.related_topics.append(related_topic)
return topic
def add_topic(self, topic=None):
if topic is None:
topic = bcf.v3.data.Topic()
if not topic.guid:
topic.guid = str(uuid.uuid4())
if not topic.title:
topic.title = "New Topic"
os.mkdir(os.path.join(self.filepath, topic.guid))
self.edit_topic(topic)
return topic
def edit_topic(self, topic):
if not topic.creation_date:
topic.creation_date = datetime.utcnow().isoformat()
topic.creation_author = self.author
else:
topic.modified_date = datetime.utcnow().isoformat()
topic.modified_author = self.author
self.document = minidom.Document()
root = self._create_element(self.document, "Markup")
if topic.header:
self.write_header(topic.header, root)
topic_el = self._create_element(
root,
"Topic",
{
"Guid": topic.guid,
"ServerAssignedId": topic.server_assigned_id,
"TopicType": topic.topic_type,
"TopicStatus": topic.topic_status,
},
)
if topic.reference_links:
reference_Links_el = self._create_element(topic_el, "ReferenceLinks")
for reference_link in topic.reference_links:
self._create_element(reference_Links_el, "ReferenceLink", text=reference_link)
text_map = {
"Title": topic.title,
"Priority": topic.priority,
"Index": topic.index,
}
for key, value in text_map.items():
if value:
self._create_element(topic_el, key, text=value)
if topic.labels:
label_el = self._create_element(topic_el, "Labels")
for label in topic.labels:
self._create_element(label_el, "Label", text=label)
text_map = {
"CreationDate": topic.creation_date,
"CreationAuthor": topic.creation_author,
"ModifiedDate": topic.modified_date,
"ModifiedAuthor": topic.modified_author,
"DueDate": topic.due_date,
"AssignedTo": topic.assigned_to,
"Stage": topic.stage,
"Description": topic.description,
}
for key, value in text_map.items():
if value:
self._create_element(topic_el, key, text=value)
if topic.bim_snippet:
bim_snippet = self._create_element(
topic_el,
"BimSnippet",
{
"SnippetType": topic.bim_snippet.snippet_type,
"IsExternal": topic.bim_snippet.is_external,
},
)
self._create_element(bim_snippet, "Reference", text=topic.bim_snippet.reference)
self._create_element(
bim_snippet,
"ReferenceSchema",
text=topic.bim_snippet.reference_schema,
)
if topic.document_references:
reference_el = self._create_element(topic_el, "DocumentReferences")
self.write_document_references(topic.document_references, reference_el)
if topic.related_topics:
related_topic_el = self._create_element(topic_el, "RelatedTopics")
for related_topic in topic.related_topics:
self._create_element(related_topic_el, "RelatedTopic", {"Guid": related_topic.guid})
if topic.comments:
comment_el = self._create_element(topic_el, "Comments")
self.write_comments(topic.comments, comment_el)
if topic.viewpoints:
viewpoint_el = self._create_element(topic_el, "Viewpoints")
self.write_viewpoints(topic.viewpoints, viewpoint_el, topic)
with open(os.path.join(self.filepath, topic.guid, "markup.bcf"), "wb") as f:
f.write(self.document.toprettyxml(encoding="utf-8"))
def write_document_references(self, references, root):
for reference in references:
document_reference_el = self._create_element(root, "DocumentReference", {"Guid": reference.guid})
if reference.document_guid:
self._create_element(document_reference_el, "DocumentGuid", text=reference.document_guid)
elif reference.url:
self._create_element(document_reference_el, "Url", text=reference.url)
if reference.description:
self._create_element(document_reference_el, "Description", text=reference.description)
def write_header(self, header, root):
if not header or not header.files:
return
header_el = self._create_element(root, "Header")
files_el = self._create_element(header_el, "Files")
for f in header.files:
file_el = self._create_element(
files_el,
"File",
{
"IfcProject": f.ifc_project,
"IfcSpatialStructureElement": f.ifc_spatial_structure_element,
"IsExternal": f.is_external,
},
)
if f.filename:
self._create_element(file_el, "Filename", text=f.filename)
if f.date:
self._create_element(file_el, "Date", text=f.date)
if f.reference:
self._create_element(file_el, "Reference", text=f.reference)
def write_comments(self, comments, root):
for comment in comments.values():
comment_el = self._create_element(root, "Comment", {"Guid": comment.guid})
text_map = {
"Date": comment.date,
"Author": comment.author,
"Comment": comment.comment,
"ModifiedDate": comment.modified_date,
"ModifiedAuthor": comment.modified_author,
}
for key, value in text_map.items():
if value:
self._create_element(comment_el, key, text=value)
if comment.viewpoint:
self._create_element(comment_el, "Viewpoint", {"Guid": comment.viewpoint.guid})
def add_comment(self, topic, comment=None):
if comment is None:
comment = bcf.v3.data.Comment()
if not comment.guid:
comment.guid = str(uuid.uuid4())
if not comment.comment:
comment.comment = "'Free software' is a matter of liberty, not price. To understand the concept, you should think of 'free' as in 'free speech,' not as in 'free beer'."
topic.comments[comment.guid] = comment
self.edit_comment(comment, topic)
def edit_comment(self, comment, topic):
if not comment.date:
comment.date = datetime.utcnow().isoformat()
comment.author = self.author
else:
comment.modified_date = datetime.utcnow().isoformat()
comment.modified_author = self.author
self.edit_topic(topic)
def delete_comment(self, guid, topic):
if guid in topic.comments:
del topic.comments[guid]
self.edit_topic(topic)
def delete_topic(self, guid):
if guid in self.topics:
del self.topics[guid]
shutil.rmtree(os.path.join(self.filepath, guid))
def write_viewpoints(self, viewpoints, root, topic):
for viewpoint in viewpoints.values():
viewpoint_el = self._create_element(root, "ViewPoint", {"Guid": viewpoint.guid})
text_map = {
"Viewpoint": viewpoint.viewpoint,
"Snapshot": viewpoint.snapshot,
"Index": viewpoint.index,
}
for key, value in text_map.items():
if value:
self._create_element(viewpoint_el, key, text=value)
self.write_viewpoint(viewpoint, topic)
def write_viewpoint(self, viewpoint, topic):
document = minidom.Document()
root = self._create_element(document, "VisualizationInfo", {"Guid": viewpoint.guid})
self.write_viewpoint_components(viewpoint, root)
self.write_viewpoint_orthogonal_camera(viewpoint, root)
self.write_viewpoint_perspective_camera(viewpoint, root)
self.write_viewpoint_lines(viewpoint, root)
self.write_viewpoint_clipping_planes(viewpoint, root)
self.write_viewpoint_bitmaps(viewpoint, root)
with open(os.path.join(self.filepath, topic.guid, viewpoint.viewpoint), "wb") as f:
f.write(document.toprettyxml(encoding="utf-8"))
def write_viewpoint_components(self, viewpoint, parent):
if not viewpoint.components:
return
components_el = self._create_element(parent, "Components")
if viewpoint.components.selection:
selection_el = self._create_element(components_el, "Selection")
for selection in viewpoint.components.selection:
self.write_component(selection, selection_el)
if viewpoint.components.visibility:
visibility = self._create_element(
components_el,
"Visibility",
{"DefaultVisibility": viewpoint.components.visibility.default_visibility},
)
if viewpoint.components.visibility.view_setup_hints:
view_setup_hints = self._create_element(
visibility,
"ViewSetupHints",
{
"SpacesVisible": viewpoint.components.visibility.view_setup_hints.spaces_visible,
"SpaceBoundariesVisible": viewpoint.components.visibility.view_setup_hints.space_boundaries_visible,
"OpeningsVisible": viewpoint.components.visibility.view_setup_hints.openings_visible,
},
)
if viewpoint.components.visibility.exceptions:
exceptions_el = self._create_element(visibility, "Exceptions")
for exception in viewpoint.components.visibility.exceptions:
self.write_component(exception, exceptions_el)
if viewpoint.components.coloring:
coloring_el = self._create_element(components_el, "Coloring")
for color in viewpoint.components.coloring:
color_el = self._create_element(coloring_el, "Color", {"Color": color.color})
component_el = self._create_element(color_el, "Components")
for component in color.components:
self.write_component(component, component_el)
def write_viewpoint_orthogonal_camera(self, viewpoint, parent):
if not viewpoint.orthogonal_camera:
return
camera = viewpoint.orthogonal_camera
camera_el = self._create_element(parent, "OrthogonalCamera")
camera_view_point = self._create_element(camera_el, "CameraViewPoint")
self.write_vector(camera_view_point, camera.camera_view_point)
camera_direction = self._create_element(camera_el, "CameraDirection")
self.write_vector(camera_direction, camera.camera_direction)
camera_up_vector = self._create_element(camera_el, "CameraUpVector")
self.write_vector(camera_up_vector, camera.camera_up_vector)
self._create_element(camera_el, "ViewToWorldScale", text=camera.view_to_world_scale)
self._create_element(camera_el, "AspectRatio", text=camera.aspect_ratio)
def write_viewpoint_perspective_camera(self, viewpoint, parent):
if not viewpoint.perspective_camera:
return
camera = viewpoint.perspective_camera
camera_el = self._create_element(parent, "PerspectiveCamera")
camera_view_point = self._create_element(camera_el, "CameraViewPoint")
self.write_vector(camera_view_point, camera.camera_view_point)
camera_direction = self._create_element(camera_el, "CameraDirection")
self.write_vector(camera_direction, camera.camera_direction)
camera_up_vector = self._create_element(camera_el, "CameraUpVector")
self.write_vector(camera_up_vector, camera.camera_up_vector)
self._create_element(camera_el, "FieldOfView", text=camera.field_of_view)
self._create_element(camera_el, "AspectRatio", text=camera.aspect_ratio)
def write_viewpoint_lines(self, viewpoint, parent):
if not viewpoint.lines:
return
lines_el = self._create_element(parent, "Lines")
for line in viewpoint.lines:
line_el = self._create_element(lines_el, "Line")
start_point_el = self._create_element(line_el, "StartPoint")
self.write_vector(start_point_el, line.start_point)
end_point_el = self._create_element(line_el, "EndPoint")
self.write_vector(end_point_el, line.end_point)
def write_viewpoint_clipping_planes(self, viewpoint, parent):
if not viewpoint.clipping_planes:
return
planes_el = self._create_element(parent, "ClippingPlanes")
for plane in viewpoint.clipping_planes:
plane_el = self._create_element(planes_el, "ClippingPlane")
location_el = self._create_element(plane_el, "Location")
self.write_vector(location_el, plane.location)
direction_el = self._create_element(plane_el, "Direction")
self.write_vector(direction_el, plane.direction)
def write_viewpoint_bitmaps(self, viewpoint, parent):
if not viewpoint.bitmaps:
return
bitmaps_el = self._create_element(parent, "Bitmaps")
for bitmap in viewpoint.bitmaps:
bitmap_el = self._create_element(bitmaps_el, "Bitmap")
text_map = {"Format": bitmap.bitmap_format, "Reference": bitmap.reference}
for key, value in text_map.items():
self._create_element(bitmap_el, key, text=value)
location_el = self._create_element(bitmap_el, "Location")
self.write_vector(location_el, bitmap.location)
normal_el = self._create_element(bitmap_el, "Normal")
self.write_vector(normal_el, bitmap.normal)
up_el = self._create_element(bitmap_el, "Up")
self.write_vector(up_el, bitmap.up)
self._create_element(bitmap_el, "Height", text=bitmap.height)
def write_vector(self, parent, from_obj):
self._create_element(parent, "X", text=from_obj.x)
self._create_element(parent, "Y", text=from_obj.y)
self._create_element(parent, "Z", text=from_obj.z)
def write_component(self, data, parent):
component_el = self._create_element(parent, "Component", {"IfcGuid": data.ifc_guid})
text_map = {
"OriginatingSystem": data.originating_system,
"AuthoringToolId": data.authoring_tool_id,
}
for key, value in text_map.items():
if value:
self._create_element(component_el, key, text=value)
def add_viewpoint(self, topic, viewpoint=None):
if not viewpoint:
viewpoint = bcf.v3.data.Viewpoint()
if not viewpoint.guid:
viewpoint.guid = str(uuid.uuid4())
if not viewpoint.viewpoint:
viewpoint.viewpoint = f"{viewpoint.guid}.bcfv"
if viewpoint.snapshot:
topic_filepath = os.path.join(self.filepath, topic.guid)
filepath = os.path.join(topic_filepath, viewpoint.snapshot)
if not os.path.exists(filepath):
filename = viewpoint.guid + os.path.splitext(viewpoint.snapshot)[-1]
copyfile(viewpoint.snapshot, os.path.join(topic_filepath, filename))
viewpoint.snapshot = filename
topic.viewpoints[viewpoint.guid] = viewpoint
self.edit_topic(topic)
def delete_viewpoint(self, guid, topic):
if guid not in topic.viewpoints:
return
viewpoint = topic.viewpoints[guid]
if viewpoint.snapshot:
filepath = os.path.join(self.filepath, topic.guid, viewpoint.snapshot)
if os.path.exists(filepath):
os.remove(filepath)
if viewpoint.viewpoint:
filepath = os.path.join(self.filepath, topic.guid, viewpoint.viewpoint)
if os.path.exists(filepath):
os.remove(filepath)
for bitmap in viewpoint.bitmaps:
if not bitmap.reference:
continue
filepath = os.path.join(self.filepath, topic.guid, bitmap.reference)
if os.path.exists(filepath):
os.remove(filepath)
del topic.viewpoints[guid]
self.edit_topic(topic)
def delete_file(self, topic, index):
if not topic.header:
return
f = topic.header.files.pop(index)
filepath = os.path.join(self.filepath, topic.guid, f.reference)
if not f.is_external and os.path.exists(filepath):
os.remove(filepath)
self.edit_topic(topic)
def delete_bim_snippet(self, topic):
if not topic.bim_snippet:
return
if topic.bim_snippet.reference and not topic.bim_snippet.is_external:
filepath = os.path.join(self.filepath, topic.guid, topic.bim_snippet.reference)
if os.path.exists(filepath):
os.remove(filepath)
topic.bim_snippet = None
self.edit_topic(topic)
def delete_document_reference(self, topic, index):
document_reference = topic.document_references[index]
if document_reference.referenced_document and not document_reference.is_external:
filepath = os.path.join(self.filepath, topic.guid, document_reference.referenced_document)
if os.path.exists(filepath):
os.remove(filepath)
del topic.document_references[index]
self.edit_topic(topic)
def add_document_reference(self, topic, document_reference):
if os.path.exists(document_reference.referenced_document):
topic_filepath = os.path.join(self.filepath, topic.guid)
filename = os.path.basename(document_reference.referenced_document)
copyfile(
document_reference.referenced_document,
os.path.join(topic_filepath, filename),
)
document_reference.referenced_document = filename
document_reference.is_external = False
else:
document_reference.is_external = True
if not document_reference.guid:
document_reference.guid = str(uuid.uuid4())
topic.document_references.append(document_reference)
self.edit_topic(topic)
def add_bim_snippet(self, topic, bim_snippet):
if topic.bim_snippet:
self.delete_bim_snippet(topic)
if os.path.exists(bim_snippet.reference):
topic_filepath = os.path.join(self.filepath, topic.guid)
filename = os.path.basename(bim_snippet.reference)
copyfile(bim_snippet.reference, os.path.join(topic_filepath, filename))
bim_snippet.reference = filename
bim_snippet.is_external = False
else:
bim_snippet.is_external = True
topic.bim_snippet = bim_snippet
self.edit_topic(topic)
def add_file(self, topic, header_file):
if os.path.exists(header_file.reference):
topic_filepath = os.path.join(self.filepath, topic.guid)
header_file.filename = os.path.basename(header_file.reference)
copyfile(
header_file.reference,
os.path.join(topic_filepath, header_file.filename),
)
header_file.reference = header_file.filename
header_file.is_external = False
header_file.date = datetime.utcnow().isoformat()
if not topic.header:
topic.header = bcf.v3.data.Header()
topic.header.files.append(header_file)
self.edit_topic(topic)
def get_comments(self, guid):
comments = {}
if "Comments" not in data["Topics"]:
return comments
data = self._read_xml(os.path.join(guid, "markup.bcf"), "markup.xsd")
for item in data["Topic"]["Comments"].get("Comment", []):
comment = bcf.v3.data.Comment()
mandatory_keys = {
"guid": "@Guid",
"date": "Date",
"author": "Author",
}
for key, value in mandatory_keys.items():
setattr(comment, key, item[value])
optional_keys = {
"comment": "Comment",
"modified_date": "ModifiedDate",
"modified_author": "ModifiedAuthor",
}
for key, value in optional_keys.items():
if value in item:
setattr(comment, key, item[value])
if "Viewpoint" in item:
viewpoint = bcf.v3.data.Viewpoint()
viewpoint.guid = item["Viewpoint"]["@Guid"]
comment.viewpoint = viewpoint
comments[comment.guid] = comment
self.topics[guid].comments = comments
return comments
def get_viewpoints(self, guid):
viewpoints = {}
if "Viewpoints" not in data["Topic"]:
return viewpoints
data = self._read_xml(os.path.join(guid, "markup.bcf"), "markup.xsd")
for item in data["Topic"]["Viewpoints"]:
viewpoint = self.get_viewpoint(item, guid)
viewpoints[viewpoint.guid] = viewpoint
self.topics[guid].viewpoints = viewpoints
return viewpoints
def get_viewpoint(self, data, topic_guid):
viewpoint = bcf.v3.data.Viewpoint()
viewpoint.guid = data["@Guid"]
optional_keys = {
"viewpoint": "Viewpoint",
"snapshot": "Snapshot",
"index": "Index",
}
for key, value in optional_keys.items():
if value in data:
setattr(viewpoint, key, data[value])
visinfo = self._read_xml(os.path.join(topic_guid, viewpoint.viewpoint), "visinfo.xsd")
viewpoint.components = self.get_viewpoint_components(visinfo)
viewpoint.orthogonal_camera = self.get_viewpoint_orthogonal_camera(visinfo)
viewpoint.perspective_camera = self.get_viewpoint_perspective_camera(visinfo)
viewpoint.lines = self.get_viewpoint_lines(visinfo)
viewpoint.clipping_planes = self.get_viewpoint_clipping_planes(visinfo)
viewpoint.bitmaps = self.get_viewpoint_bitmaps(visinfo)
return viewpoint
def get_viewpoint_components(self, visinfo):
if "Components" not in visinfo:
return None
components = bcf.v3.data.Components()
data = visinfo["Components"]
if "Selection" in data and "Component" in data["Selection"]:
for item in data["Selection"].get("Component", []):
components.selection.append(self.get_component(item))
if "Visibility" in data:
component_visibility = bcf.v3.data.ComponentVisibility()
if "@DefaultVisibility" in data["Visibility"]:
component_visibility.default_visibility = data["Visibility"]["@DefaultVisibility"]
if "Exceptions" in data["Visibility"] and "Component" in data["Visibility"]["Exceptions"]:
for item in data["Visibility"]["Exceptions"]["Component"]:
component_visibility.exceptions.append(self.get_component(item))
if "ViewSetupHints" in data["Visibility"]:
view_setup_hints = bcf.v3.data.ViewSetupHints()
optional_keys = {
"spaces_visible": "@SpacesVisible",
"space_boundaries_visible": "@SpaceBoundariesVisible",
"openings_visible": "@OpeningsVisible",
}
for key, value in optional_keys.items():
if value in data["Visibility"]["ViewSetupHints"]:
setattr(view_setup_hints, key, data["Visibility"]["ViewSetupHints"][value])
component_visibility.view_setup_hints = view_setup_hints
components.visibility = component_visibility
if "Coloring" in data and "Color" in data["Coloring"]:
for item in data["Coloring"]["Color"]:
color = bcf.v3.data.Color()
color.color = item["@Color"]
for item2 in item["Components"]["Component"]:
color.components.append(self.get_component(item2))
components.coloring.append(color)
return components
def get_viewpoint_orthogonal_camera(self, visinfo):
if "OrthogonalCamera" not in visinfo:
return None
camera = bcf.v3.data.OrthogonalCamera()
data = visinfo["OrthogonalCamera"]
self.set_vector(camera.camera_view_point, data["CameraViewPoint"])
self.set_vector(camera.camera_direction, data["CameraDirection"])
self.set_vector(camera.camera_up_vector, data["CameraUpVector"])
camera.view_to_world_scale = data["ViewToWorldScale"]
camera.aspect_ratio = data["AspectRatio"]
return camera
def get_viewpoint_perspective_camera(self, visinfo):
if "PerspectiveCamera" not in visinfo:
return None
camera = bcf.v3.data.PerspectiveCamera()
data = visinfo["PerspectiveCamera"]
self.set_vector(camera.camera_view_point, data["CameraViewPoint"])
self.set_vector(camera.camera_direction, data["CameraDirection"])
self.set_vector(camera.camera_up_vector, data["CameraUpVector"])
camera.field_of_view = data["FieldOfView"]
camera.aspect_ratio = data["AspectRatio"]
return camera
def get_viewpoint_lines(self, visinfo):
if "Lines" not in visinfo:
return []
lines = []
for item in visinfo["Lines"].get("Line", []):
line = bcf.v3.data.Line()
self.set_vector(line.start_point, item["StartPoint"])
self.set_vector(line.end_point, item["EndPoint"])
lines.append(line)
return lines
def get_viewpoint_clipping_planes(self, visinfo):
if "ClippingPlanes" not in visinfo:
return []
planes = []
for item in visinfo["ClippingPlanes"]["ClippingPlane"]:
plane = bcf.v3.data.ClippingPlane()
self.set_vector(plane.location, item["Location"])
self.set_vector(plane.direction, item["Direction"])
planes.append(plane)
return planes
def get_viewpoint_bitmaps(self, visinfo):
if "Bitmaps" not in visinfo:
return []
bitmaps = []
for item in visinfo["Bitmaps"].get("Bitmap"):
bitmap = bcf.v3.data.Bitmap()
bitmap.reference = item["Reference"]
bitmap.bitmap_format = item["Format"].upper()
self.set_vector(bitmap.location, item["Location"])
self.set_vector(bitmap.normal, item["Normal"])
self.set_vector(bitmap.up, item["Up"])
bitmap.height = item["Height"]
bitmaps.append(bitmap)
return bitmaps
def set_vector(self, to_obj, from_xml):
to_obj.x = from_xml["X"]
to_obj.y = from_xml["Y"]
to_obj.z = from_xml["Z"]
def get_component(self, data):
component = bcf.v3.data.Component()
optional_keys = {
"originating_system": "OriginatingSystem",
"authoring_tool_id": "AuthoringToolId",
"ifc_guid": "@IfcGuid",
}
for key, value in optional_keys.items():
if value in data:
setattr(component, key, data[value])
return component
def close_project(self):
shutil.rmtree(self.filepath)
def _read_xml(self, filename, xsd):
schema = XMLSchema(os.path.join(cwd, "xsd", xsd))
filepath = os.path.join(self.filepath, filename)
(data, errors) = schema.to_dict(filepath, validation="lax")
for error in errors:
self.logger.error(error)
return data
def _create_element(self, parent, name, attributes={}, text=None):
element = self.document.createElement(name)
for key, value in attributes.items():
if isinstance(value, bool):
element.setAttribute(key, str(value).lower())
elif value:
element.setAttribute(key, value)
if text is not None:
text = self.document.createTextNode(str(text))
element.appendChild(text)
parent.appendChild(element)
return element
def __del__(self):
self.close_project()
+181
View File
@@ -0,0 +1,181 @@
class Project:
def __init__(self):
self.project_id = ""
self.name = ""
class BimSnippet:
def __init__(self):
self.snippet_type = None
self.is_external = False
self.reference = None
self.reference_schema = None
class DocumentReference:
def __init__(self):
self.description = None
self.document_guid = None
self.url = None
self.guid = None
class RelatedTopic:
def __init__(self):
self.guid = None
class HeaderFile:
def __init__(self):
self.file_name = ""
self.date = None
self.reference = ""
self.ifc_project = None
self.ifc_spatial_structure_element = None
self.is_external = True
class Header:
def __init__(self):
self.files = []
class Topic:
def __init__(self):
self.reference_links = []
self.title = ""
self.priority = None
self.index = None # Deprecated, stored, but ignored
self.labels = []
self.creation_date = None
self.creation_author = None
self.modified_date = None
self.modified_author = None
self.due_date = None
self.assigned_to = None
self.stage = None
self.description = None
self.bim_snippet = None
self.document_references = []
self.related_topics = []
self.topic_status = None
self.topic_type = None
self.guid = None
self.header = None
self.comments = {}
self.viewpoints = {}
self.server_assigned_id = ""
class Comment:
def __init__(self):
self.guid = None
self.date = None
self.author = ""
self.comment = ""
self.viewpoint = None
self.modified_date = None
self.modified_author = ""
class ViewSetupHints:
def __init__(self):
self.spaces_visible = False
self.space_boundaries_visible = False
self.openings_visible = False
class Component:
def __init__(self):
self.originating_system = None
self.authoring_tool_id = None
self.ifc_guid = None
class ComponentVisibility:
def __init__(self):
self.exceptions = []
self.default_visibility = False
self.view_setup_hints = None
class Color:
def __init__(self):
self.color = None
self.components = []
class Components:
def __init__(self):
self.selection = []
self.visibility = None
self.coloring = []
class Point:
def __init__(self):
self.x = 0
self.y = 0
self.z = 0
class Direction(Point):
pass
class OrthogonalCamera:
def __init__(self):
self.camera_view_point = Point()
self.camera_direction = Direction()
self.camera_up_vector = Direction()
self.view_to_world_scale = 1.0
self.aspect_ratio = 1.0
class PerspectiveCamera:
def __init__(self):
self.camera_view_point = Point()
self.camera_direction = Direction()
self.camera_up_vector = Direction()
self.field_of_view = 60.0
self.aspect_ratio = 1.0
class Line:
def __init__(self):
self.start_point = Point()
self.end_point = Point()
class ClippingPlane:
def __init__(self):
self.location = Point()
self.direction = Direction()
class Bitmap:
def __init__(self):
self.reference = "" # Only in BCF-XML
self.bitmap_data = None # Only in BCF-API
self.bitmap_format = "PNG" # Enum of png or jpg
self.location = Point()
self.normal = Direction()
self.up = Direction()
self.height = 1.0
class Viewpoint:
def __init__(self):
self.guid = None
self.viewpoint = None
self.snapshot = None
self.index = None
self.components = None # It's not a list, despite the plural name
self.orthogonal_camera = None
self.perspective_camera = None
self.lines = []
self.clipping_planes = []
self.bitmaps = []
+31
View File
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:include schemaLocation="shared-types.xsd"/>
<xs:element name="DocumentInfo">
<xs:complexType>
<xs:sequence>
<xs:element name="Documents" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="Document" type="Document" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name="Document">
<xs:sequence>
<!-- Filename of the document with the file extension. Not used to store the file in the BCF -->
<xs:element name="Filename" type="NonEmptyOrBlankString"/>
<!-- Human readable description of the document -->
<xs:element name="Description" type="NonEmptyOrBlankString" minOccurs="0"/>
</xs:sequence>
<xs:attributeGroup ref="DocumentAttributes"/>
</xs:complexType>
<xs:attributeGroup name="DocumentAttributes">
<!-- Guid of the document. Must match the filename in the BCF -->
<xs:attribute name="Guid" type="Guid" use="required"/>
</xs:attributeGroup>
</xs:schema>
+60
View File
@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:include schemaLocation="shared-types.xsd"/>
<xs:element name="Extensions">
<xs:complexType>
<xs:sequence>
<xs:element name="TopicTypes" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="TopicType" type="NonEmptyOrBlankString" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="TopicStatuses" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="TopicStatus" type="NonEmptyOrBlankString" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Priorities" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="Priority" type="NonEmptyOrBlankString" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="TopicLabels" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="TopicLabel" type="NonEmptyOrBlankString" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Users" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="User" type="NonEmptyOrBlankString" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="SnippetTypes" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="SnippetType" type="NonEmptyOrBlankString" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Stages" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="Stage" type="NonEmptyOrBlankString" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
+174
View File
@@ -0,0 +1,174 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:include schemaLocation="shared-types.xsd"/>
<xs:element name="Markup">
<xs:complexType>
<xs:sequence>
<xs:element name="Header" type="Header" minOccurs="0"/>
<xs:element name="Topic" type="Topic"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name="Header">
<xs:sequence>
<xs:element name="Files" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="File" type="File" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
<!-- ISG Jira issue BCF-9. Add support for several viewpoints and snapshots per issue -->
<xs:complexType name="ViewPoint">
<xs:sequence>
<!-- viewpoint file (xml) -->
<xs:element name="Viewpoint" type="NonEmptyOrBlankString" minOccurs="0"/>
<!-- the snapshot png -->
<xs:element name="Snapshot" type="NonEmptyOrBlankString" minOccurs="0"/>
<!-- the viewpoint index (sort order) -->
<xs:element name="Index" type="xs:int" minOccurs="0"/>
</xs:sequence>
<xs:attribute name="Guid" type="Guid" use="required"/>
<!-- Guid of the viewpoint -->
</xs:complexType>
<!-- BimSnippet -->
<xs:complexType name="BimSnippet">
<xs:sequence>
<!--
Name of the file in the topic folder containing the snippet or a URL.
E.G.- Expresscode containing p.e Issue, Request
// Maybe some header infos ?? // IfcEntites // Geometry
-->
<!-- Reference (name) to the snippet file -->
<xs:element name="Reference" type="NonEmptyOrBlankString"/>
<xs:element name="ReferenceSchema" type="NonEmptyOrBlankString"/>
</xs:sequence>
<xs:attribute name="SnippetType" type="NonEmptyOrBlankString" use="required"/>
<xs:attribute name="IsExternal" type="xs:boolean" default="false"/>
<!-- This flag is true when the reference is a URL pointing outside of the BCF file-->
</xs:complexType>
<xs:complexType name="Topic">
<xs:sequence>
<xs:element name="ReferenceLinks" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="ReferenceLink" type="NonEmptyOrBlankString" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Title" type="NonEmptyOrBlankString"/>
<xs:element name="Priority" type="NonEmptyOrBlankString" minOccurs="0"/>
<!-- ISG Jira issue BCF-8 Add a way save order the topics -->
<!-- This property is deprecated and will be removed in a future release -->
<xs:element name="Index" type="xs:int" minOccurs="0"/>
<xs:element name="Labels" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="Label" type="NonEmptyOrBlankString" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="CreationDate" type="xs:dateTime"/>
<xs:element name="CreationAuthor" type="NonEmptyOrBlankString"/>
<xs:element name="ModifiedDate" type="xs:dateTime" minOccurs="0"/>
<xs:element name="ModifiedAuthor" type="NonEmptyOrBlankString" minOccurs="0"/>
<xs:element name="DueDate" type="xs:dateTime" minOccurs="0"/>
<xs:element name="AssignedTo" type="NonEmptyOrBlankString" minOccurs="0"/>
<xs:element name="Stage" type="NonEmptyOrBlankString" minOccurs="0"/>
<xs:element name="Description" type="NonEmptyOrBlankString" minOccurs="0"/>
<xs:element name="BimSnippet" type="BimSnippet" minOccurs="0"/>
<xs:element name="DocumentReferences" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="DocumentReference" type="DocumentReference" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="RelatedTopics" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="RelatedTopic" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="Guid" type="Guid" use="required"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Comments" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="Comment" type="Comment" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<!-- ISG Jira issue BCF-9. Add support for several viewpoints and snapshots per issue -->
<xs:element name="Viewpoints" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="ViewPoint" type="ViewPoint" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="Guid" type="Guid" use="required"/>
<xs:attribute name="ServerAssignedId" type="NonEmptyOrBlankString" use="optional"/>
<xs:attribute name="TopicType" type="NonEmptyOrBlankString" use="required"/>
<xs:attribute name="TopicStatus" type="NonEmptyOrBlankString" use="required"/>
</xs:complexType>
<xs:complexType name="File">
<xs:sequence>
<xs:element name="Filename" type="NonEmptyOrBlankString" minOccurs="0"/>
<xs:element name="Date" type="xs:dateTime" minOccurs="0"/>
<!-- Reference (URL) of the file -->
<xs:element name="Reference" type="NonEmptyOrBlankString" minOccurs="0"/>
</xs:sequence>
<xs:attributeGroup ref="FileAttributes"/>
</xs:complexType>
<xs:attributeGroup name="FileAttributes">
<xs:attribute name="IfcProject" type="IfcGuid" use="optional"/>
<xs:attribute name="IfcSpatialStructureElement" type="IfcGuid" use="optional"/>
<xs:attribute name="IsExternal" type="xs:boolean" default="true"/>
</xs:attributeGroup>
<!-- Reference to a document inside of the topic folder or a url pointing to the web -->
<xs:complexType name="DocumentReference">
<xs:sequence>
<xs:choice>
<!-- Guid of the document: If pointing to an internal document -->
<xs:element name="DocumentGuid" type="Guid" minOccurs="0"/>
<!-- Url of the reference. If pointing to an external document -->
<xs:element name="Url" type="NonEmptyOrBlankString" minOccurs="0"/>
</xs:choice>
<!-- Human readable description of the document reference -->
<xs:element name="Description" type="NonEmptyOrBlankString" minOccurs="0"/>
</xs:sequence>
<xs:attributeGroup ref="DocumentReferenceAttributes"/>
</xs:complexType>
<xs:attributeGroup name="DocumentReferenceAttributes">
<!-- Guid of the document reference -->
<xs:attribute name="Guid" type="Guid" use="required"/>
</xs:attributeGroup>
<xs:complexType name="Comment">
<xs:sequence>
<xs:element name="Date" type="xs:dateTime"/>
<xs:element name="Author" type="NonEmptyOrBlankString"/>
<xs:element name="Comment" minOccurs="0" type="NonEmptyOrBlankString"/>
<xs:element name="Viewpoint" minOccurs="0">
<xs:complexType>
<xs:attribute name="Guid" type="Guid" use="required"/>
</xs:complexType>
</xs:element>
<xs:element name="ModifiedDate" type="xs:dateTime" minOccurs="0"/>
<xs:element name="ModifiedAuthor" type="NonEmptyOrBlankString" minOccurs="0"/>
</xs:sequence>
<xs:attribute name="Guid" type="Guid" use="required"/>
</xs:complexType>
<xs:simpleType name="IfcGuid">
<xs:restriction base="xs:string">
<xs:length value="22"/>
<xs:pattern value="[0-9A-Za-z_$]*"/>
</xs:restriction>
</xs:simpleType>
</xs:schema>
+17
View File
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:include schemaLocation="shared-types.xsd"/>
<xs:element name="ProjectInfo">
<xs:complexType>
<xs:sequence>
<xs:element name="Project" type="Project"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name="Project">
<xs:sequence>
<xs:element name="Name" type="NonEmptyOrBlankString" minOccurs="0"/>
</xs:sequence>
<xs:attribute name="ProjectId" type="NonEmptyOrBlankString" use="required"/>
</xs:complexType>
</xs:schema>
+14
View File
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:simpleType name="Guid">
<xs:restriction base="xs:string">
<xs:pattern value="[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="NonEmptyOrBlankString">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:whiteSpace value="collapse"/>
</xs:restriction>
</xs:simpleType>
</xs:schema>
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="Version">
<xs:complexType>
<xs:attribute name="VersionId" type="xs:string" use="required"/>
</xs:complexType>
</xs:element>
</xs:schema>
+220
View File
@@ -0,0 +1,220 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
<xs:include schemaLocation="shared-types.xsd"/>
<xs:element name="VisualizationInfo">
<xs:annotation>
<xs:documentation>VisualizationInfo documentation</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:sequence>
<!--
Although plural, 'Components' is not a collection
-->
<xs:element name="Components" type="Components" minOccurs="0"/>
<xs:choice>
<xs:element name="OrthogonalCamera" type="OrthogonalCamera"/>
<xs:element name="PerspectiveCamera" type="PerspectiveCamera"/>
</xs:choice>
<xs:element name="Lines" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="Line" type="Line" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="ClippingPlanes" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="ClippingPlane" type="ClippingPlane" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Bitmaps" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="Bitmap" type="Bitmap" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
<!-- Guid of the viewpoint -->
<xs:attribute name="Guid" type="Guid" use="required"/>
</xs:complexType>
</xs:element>
<xs:complexType name="OrthogonalCamera">
<xs:sequence>
<xs:element name="CameraViewPoint" type="Point"/>
<xs:element name="CameraDirection" type="Direction"/>
<xs:element name="CameraUpVector" type="Direction"/>
<xs:element name="ViewToWorldScale" type="xs:double">
<xs:annotation>
<xs:documentation>view's visible vertical size in meters</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="AspectRatio" type="PositiveDouble">
<xs:annotation>
<xs:documentation>
Proportional relationship between the width and the height of the view (w/h).
</xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="PerspectiveCamera">
<xs:sequence>
<xs:element name="CameraViewPoint" type="Point"/>
<xs:element name="CameraDirection" type="Direction"/>
<xs:element name="CameraUpVector" type="Direction"/>
<xs:element name="FieldOfView" type="FieldOfView">
<xs:annotation>
<xs:documentation>
Vertical field of view, in degrees.
It is currently limited to a value between 45 and 60 degrees.
This limitation will be dropped in the next release and viewers
should be expect values outside this range in current implementations.
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="AspectRatio" type="PositiveDouble">
<xs:annotation>
<xs:documentation>
Proportional relationship between the width and the height of the view (w/h).
</xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="Point">
<xs:sequence>
<xs:element name="X" type="xs:double"/>
<xs:element name="Y" type="xs:double"/>
<xs:element name="Z" type="xs:double"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="Direction">
<xs:sequence>
<xs:element name="X" type="xs:double"/>
<xs:element name="Y" type="xs:double"/>
<xs:element name="Z" type="xs:double"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="PositiveDouble">
<xs:restriction base="xs:double">
<xs:minExclusive value="0"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="FieldOfView">
<xs:restriction base="xs:double">
<xs:minExclusive value="0"/>
<xs:maxExclusive value="180"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="Components">
<xs:sequence>
<!-- Components with relevance to the viewpoint. They should be displayed highlighted or selected in a viewer -->
<xs:element name="Selection" type="ComponentSelection" minOccurs="0"/>
<xs:element name="Visibility" type="ComponentVisibility" minOccurs="0"/>
<xs:element name="Coloring" type="ComponentColoring" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="ComponentSelection">
<xs:sequence>
<xs:element name="Component" type="Component" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="ComponentVisibility">
<xs:sequence>
<xs:element name="ViewSetupHints" type="ViewSetupHints" minOccurs="0"/>
<xs:element name="Exceptions" minOccurs="0">
<!-- List Components that are different than the DefaultVisibility. E.g. if DefaultVisibility = false then list
Components that should be visible -->
<xs:complexType>
<xs:sequence>
<xs:element name="Component" type="Component" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="DefaultVisibility" type="xs:boolean" default="false"/>
</xs:complexType>
<xs:complexType name="ViewSetupHints">
<xs:attribute name="SpacesVisible" type="xs:boolean" default="false"/>
<xs:attribute name="SpaceBoundariesVisible" type="xs:boolean" default="false"/>
<xs:attribute name="OpeningsVisible" type="xs:boolean" default="false"/>
</xs:complexType>
<xs:complexType name="ComponentColoring">
<xs:sequence>
<xs:element name="Color" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<!-- At least one component is required for a Color. -->
<xs:element name="Components">
<xs:complexType>
<xs:sequence>
<xs:element name="Component" type="Component" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute ref="Color" use="required"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="Component">
<xs:sequence>
<xs:element name="OriginatingSystem" type="NonEmptyOrBlankString" minOccurs="0"/>
<xs:element name="AuthoringToolId" type="NonEmptyOrBlankString" minOccurs="0"/>
</xs:sequence>
<xs:attribute ref="IfcGuid"/>
</xs:complexType>
<xs:attribute name="Color">
<xs:simpleType>
<xs:restriction base="xs:normalizedString">
<!-- Should either match 3 or 4 hex bytes , e.g. "FF00FF" or "FF00FF99" -->
<xs:pattern value="[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="IfcGuid">
<xs:simpleType>
<xs:restriction base="xs:normalizedString">
<xs:length value="22"/>
<xs:pattern value="[0-9A-Za-z_$]*"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:complexType name="Line">
<xs:sequence>
<xs:element name="StartPoint" type="Point"/>
<xs:element name="EndPoint" type="Point"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="ClippingPlane">
<xs:sequence>
<xs:element name="Location" type="Point"/>
<xs:element name="Direction" type="Direction"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="Bitmap">
<xs:sequence>
<xs:element name="Format" type="BitmapFormat"/>
<!-- Name of the bitmap file in the topic folder -->
<xs:element name="Reference" type="NonEmptyOrBlankString"/>
<!-- Location of the center of the bitmap -->
<xs:element name="Location" type="Point"/>
<!-- Normal of the bitmap -->
<xs:element name="Normal" type="Direction"/>
<!-- Upvector of the bitmap -->
<xs:element name="Up" type="Direction"/>
<!-- Height of the bitmap -->
<xs:element name="Height" type="xs:double"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="BitmapFormat">
<xs:restriction base="xs:string">
<xs:enumeration value="png"/>
<xs:enumeration value="jpg"/>
</xs:restriction>
</xs:simpleType>
</xs:schema>
+1
View File
@@ -0,0 +1 @@
xmlschema
@@ -1,19 +1,5 @@
VERSION:=`date '+%y%m%d'`
ifeq ($(PLATFORM), win)
PYTHONOCC_URL:=https://anaconda.org/DLR-SC/pythonocc-core/0.17.3/download/win-64/pythonocc-core-0.17.3-py37he980bc4_10.tar.bz2
OCE_URL:=https://anaconda.org/DLR-SC/oce/0.17.2/download/win-64/oce-0.17.2-he980bc4_14.tar.bz2
TBB_URL:=https://anaconda.org/DLR-SC/tbb/2019.5/download/win-64/tbb-2019.5-he980bc4_0.tar.bz2
endif
ifeq ($(PLATFORM), macos)
PYTHONOCC_URL:=https://anaconda.org/DLR-SC/pythonocc-core/0.17.3/download/osx-64/pythonocc-core-0.17.3-py37h04f5b5a_10.tar.bz2
OCE_URL:=https://anaconda.org/DLR-SC/oce/0.17.2/download/osx-64/oce-0.17.2-h04f5b5a_12.tar.bz2
TBB_URL:=https://anaconda.org/DLR-SC/tbb/4.3.6/download/osx-64/tbb-4.3.6-0.tar.bz2
endif
ifeq ($(PLATFORM), linux)
PYTHONOCC_URL:=https://anaconda.org/DLR-SC/pythonocc-core/0.17.3/download/linux-64/pythonocc-core-0.17.3-py37h6bb024c_10.tar.bz2
OCE_URL:=https://anaconda.org/DLR-SC/oce/0.17.2/download/linux-64/oce-0.17.2-h6bb024c_14.tar.bz2
TBB_URL:=https://anaconda.org/DLR-SC/tbb/4.3.6/download/linux-64/tbb-4.3.6-0.tar.bz2
endif
PYVERSION:=py37
.PHONY: dist
dist:
@@ -26,70 +12,47 @@ endif
cp -r blenderbim/* dist/blenderbim/
# Provides IfcOpenShell Python functionality
cd dist/working && wget https://s3.amazonaws.com/ifcopenshell-builds/ifcblender-python-37-v0.6.0-3bbdc3f-$(PLATFORM)64.zip
ifeq ($(PYVERSION), py37)
cd dist/working && wget https://s3.amazonaws.com/ifcopenshell-builds/ifcblender-python-37-v0.6.0-2fd2b49-$(PLATFORM)64.zip
endif
ifeq ($(PYVERSION), py39)
cd dist/working && wget https://s3.amazonaws.com/ifcopenshell-builds/ifcblender-python-39-v0.6.0-2fd2b49-$(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
# Provides Python OCC functionality for cutting IFC geometry for construction documentation
# Provides IfcConvert for construction documentation
mkdir dist/working
cd dist/working && wget $(PYTHONOCC_URL)
cd dist/working && tar -xjvf pythonocc-core*
cd dist/working && wget https://s3.amazonaws.com/ifcopenshell-builds/IfcConvert-v0.6.0-81ad689-$(PLATFORM)64.zip
cd dist/working && unzip IfcConvert*
ifeq ($(PLATFORM), win)
cd dist/working && cp -r Lib/site-packages/OCC ../blenderbim/libs/site/packages/
cp -r dist/working/IfcConvert.exe dist/blenderbim/libs/
else
cd dist/working && cp -r lib/python3.7/site-packages/OCC ../blenderbim/libs/site/packages/
cp -r dist/working/IfcConvert dist/blenderbim/libs/
endif
rm -rf dist/working
# Required by Python OCC
mkdir dist/working
cd dist/working && wget $(OCE_URL)
cd dist/working && tar -xjvf oce*
ifeq ($(PLATFORM), win)
cd dist/working && cp -r Library/bin/* ../blenderbim/libs/site/packages/OCC/
else
# Unix Conda builds of PythonOCC expect OCE libs to have a RPATH of ../../../
cd dist/working && cp -r lib/* ../blenderbim/libs/
endif
ifeq ($(PLATFORM), linux)
rm -rf dist/blenderbim/libs/oce-0.17
endif
rm -rf dist/working
# Required by OpenCascade
mkdir dist/working
cd dist/working && wget $(TBB_URL)
cd dist/working && tar -xjvf tbb*
ifeq ($(PLATFORM), win)
cd dist/working && cp -r Library/bin/* ../blenderbim/libs/site/packages/OCC/
else
cd dist/working && cp -r lib/* ../blenderbim/libs/
endif
rm -rf dist/working
ifeq ($(PLATFORM), macos)
mkdir dist/working
cd dist/working && wget https://blenderbim.org/builds/patch-blender28-bim-macos.zip
cd dist/working && unzip patch*
cd dist/working && cp -r *.dylib ../blenderbim/libs/
rm -rf dist/working
endif
# Provides dependencies that are part of IfcOpenShell
mkdir dist/working
cd dist/working && wget https://github.com/IfcOpenShell/IfcOpenShell/archive/v0.6.0.zip
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/
@@ -98,7 +61,9 @@ endif
# Provides IFCCSV functionality
cp -r dist/working/IfcOpenShell-0.6.0/src/ifccsv/* dist/blenderbim/libs/site/packages/
# Provides IFCPatch functionality
cp -r dist/working/IfcOpenShell-0.6.0/src/ifcpatch dist/blenderbim/libs/site/packages/
cp -r dist/working/IfcOpenShell-0.6.0/src/ifcpatch/ifcpatch dist/blenderbim/libs/site/packages/
# Provides IFCP6 functionality
cp -r dist/working/IfcOpenShell-0.6.0/src/ifcp6/ifcp6 dist/blenderbim/libs/site/packages/
rm -rf dist/working
# Provides Mustache templating in construction documentation
@@ -115,6 +80,41 @@ 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 duration parsing for construction sequencing
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/b1/80/fb8c13a4cd38eb5021dc3741a9e588e4d1de88d895c1910c6fc8a08b7a70/isodate-0.6.0.tar.gz
cd dist/working && tar -xzvf isodate*
cp -r dist/working/isodate-0.6.0/src/isodate dist/blenderbim/libs/site/packages/
rm -rf dist/working
# Provides networkx graph analysis for project dependency calculations
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/b0/21/adfbf6168631e28577e4af9eb9f26d75fe72b2bb1d33762a5f2c425e6c2a/networkx-2.5.1.tar.gz
cd dist/working && tar -xzvf networkx*
cp -r dist/working/networkx-2.5.1/networkx dist/blenderbim/libs/site/packages/
rm -rf dist/working
# Required by networkx
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/4f/51/15a4f6b8154d292e130e5e566c730d8ec6c9802563d58760666f1818ba58/decorator-5.0.9.tar.gz
cd dist/working && tar -xzvf decorator*
cp -r dist/working/decorator-5.0.9/src/decorator.py 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
@@ -179,8 +179,6 @@ ifeq ($(PLATFORM), linux)
cp -r dist/working/fcl dist/blenderbim/libs/site/packages/
rm -rf dist/working
endif
# Required by IFCClash
ifeq ($(PLATFORM), win)
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/19/e6/6e9f33fb59e8f27c0e1592bd26e644bc92b85c942b072b2d3854105d5887/python_fcl_win32-0.0.12.post3-py3-none-win_amd64.whl
@@ -194,6 +192,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
@@ -231,6 +234,34 @@ endif
cd dist/working/defusedxml-0.6.0/ && cp -r defusedxml ../../blenderbim/libs/site/packages/
rm -rf dist/working
# Required by augin
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/76/b4/b7baffbda025efd5dc8fcd8d2e953e3aa939c236a484084fa8f4c3588ee9/boto3-1.17.17.tar.gz
cd dist/working && tar -xzvf boto3*
cd dist/working/boto3-1.17.17/ && cp -r boto3 ../../blenderbim/libs/site/packages/
rm -rf dist/working
# Required by boto3
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/7f/2e/e02fdfd0f0377c4e44e61de27b05d5cfe93575770661ef9ded80ed90fa88/botocore-1.20.17.tar.gz
cd dist/working && tar -xzvf botocore*
cd dist/working/botocore-1.20.17/ && cp -r botocore ../../blenderbim/libs/site/packages/
rm -rf dist/working
# Required by boto3
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/3c/56/3f325b1eef9791759784aa5046a8f6a1aff8f7c898a2e34506771d3b99d8/jmespath-0.10.0.tar.gz
cd dist/working && tar -xzvf jmespath*
cd dist/working/jmespath-0.10.0/ && cp -r jmespath ../../blenderbim/libs/site/packages/
rm -rf dist/working
# Required by boto3
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/08/e1/3ee2096ebaeeb8c186d20ed16c8faf4a503913e5c9a0e14cd6b8ffc405a3/s3transfer-0.3.4.tar.gz
cd dist/working && tar -xzvf s3transfer*
cd dist/working/s3transfer-0.3.4/ && cp -r s3transfer ../../blenderbim/libs/site/packages/
rm -rf dist/working
# Provides IFCJSON functionality
mkdir dist/working
cd dist/working && wget https://github.com/IFCJSON-Team/IFC2JSON_python/archive/master.zip
@@ -239,7 +270,7 @@ endif
rm -rf dist/working
cd dist/blenderbim && sed -i "s/999999/$(VERSION)/" __init__.py
cd dist && zip -r blender28-bim-$(VERSION)-$(PLATFORM).zip ./*
cd dist && zip -r blender28-bim-$(VERSION)-$(PYVERSION)-$(PLATFORM).zip ./*
rm -rf dist/blenderbim
.PHONY: clean

Before

Width:  |  Height:  |  Size: 399 KiB

After

Width:  |  Height:  |  Size: 399 KiB

@@ -15,6 +15,7 @@ import site
# process *.pth in /libs/site/packages to setup globally importable modules
# 3 levels deep required by occ static ../../ path
# TODO: 3 levels deep is no longer required as we no longer bundle OCC
cwd = os.path.dirname(os.path.realpath(__file__))
site.addsitedir(os.path.join(cwd, "libs", "site", "packages"))
@@ -14,8 +14,10 @@ if bpy is not None:
"bcf": None,
"root": None,
"unit": None,
"model": None,
"georeference": None,
"context": None,
"drawing": None,
"attribute": None,
"type": None,
"spatial": None,
@@ -23,10 +25,15 @@ if bpy is not None:
"aggregate": None,
"geometry": None,
"cobie": None,
"resource": None,
"cost": None,
"sequence": None,
"group": None,
"structural": None,
"boundary": None,
"material": None,
"style": None,
"layer": None,
"model": None,
"owner": None,
"pset": None,
"qto": None,
@@ -40,6 +47,7 @@ if bpy is not None:
"diff": None,
"patch": None,
"covetool": None,
"augin": None,
"debug": None,
}
@@ -53,94 +61,27 @@ if bpy is not None:
operator.SelectIfcFile,
operator.ExportIFC,
operator.ImportIFC,
operator.SelectExternalMaterialDir,
operator.AddSweptSolid,
operator.RemoveSweptSolid,
operator.AssignSweptSolidOuterCurve,
operator.SelectSweptSolidOuterCurve,
operator.AddSweptSolidInnerCurve,
operator.SelectSweptSolidInnerCurves,
operator.AssignSweptSolidExtrusion,
operator.SelectSweptSolidExtrusion,
operator.FetchExternalMaterial,
operator.FetchObjectPassport,
operator.CutSection,
operator.AddSheet,
operator.OpenSheet,
operator.AddDrawingToSheet,
operator.CreateSheets,
operator.OpenView,
operator.OpenViewCamera,
operator.ActivateView,
operator.OpenUpstream,
operator.AddSectionPlane,
operator.RemoveSectionPlane,
operator.ReloadIfcFile,
operator.AddIfcFile,
operator.RemoveIfcFile,
operator.SelectDocIfcFile,
operator.AddAnnotation,
operator.GenerateReferences,
operator.ResizeText,
operator.AddVariable,
operator.RemoveVariable,
operator.PropagateTextData,
operator.SetOverrideColour,
operator.AddDrawing,
operator.RemoveDrawing,
operator.AddDrawingStyle,
operator.RemoveDrawingStyle,
operator.SaveDrawingStyle,
operator.ActivateDrawingStyle,
operator.EditVectorStyle,
operator.RemoveSheet,
operator.AddSchedule,
operator.RemoveSchedule,
operator.SelectScheduleFile,
operator.BuildSchedule,
operator.AddScheduleToSheet,
operator.SetViewportShadowFromSun,
operator.AddDrawingStyleAttribute,
operator.RemoveDrawingStyleAttribute,
operator.CopyPropertyToSelection,
operator.CopyAttributeToSelection,
operator.RefreshDrawingList,
operator.CleanWireframes,
operator.LinkIfc,
operator.SnapSpacesTogether,
operator.CopyGrid,
operator.AddSectionsAnnotations,
prop.StrProperty,
prop.Attribute,
prop.Variable,
prop.Drawing,
prop.Schedule,
prop.DrawingStyle,
prop.Sheet,
prop.BIMProperties,
prop.DocProperties,
prop.IfcParameter,
prop.BoundaryCondition,
prop.PsetQto,
prop.GlobalId,
prop.RepresentationItem,
prop.BIMObjectProperties,
prop.BIMMaterialProperties,
prop.SweptSolid,
prop.ItemSlotMap,
prop.BIMMeshProperties,
prop.BIMCameraProperties,
prop.BIMTextProperties,
ui.BIM_PT_section_plane,
ui.BIM_PT_drawings,
ui.BIM_PT_schedules,
ui.BIM_PT_sheets,
ui.BIM_PT_camera,
ui.BIM_PT_text,
ui.BIM_PT_annotation_utilities,
ui.BIM_PT_misc_utilities,
ui.BIM_UL_generic,
ui.BIM_UL_drawinglist,
ui.BIM_UL_topics,
ui.BIM_ADDON_preferences,
]
@@ -162,49 +103,49 @@ if bpy is not None:
for cls in classes:
bpy.utils.register_class(cls)
bpy.app.handlers.depsgraph_update_post.append(on_register)
bpy.app.handlers.undo_pre.append(handler.undo_pre)
bpy.app.handlers.undo_post.append(handler.undo_post)
bpy.app.handlers.redo_pre.append(handler.redo_pre)
bpy.app.handlers.redo_post.append(handler.redo_post)
bpy.app.handlers.load_post.append(handler.setDefaultProperties)
bpy.app.handlers.load_post.append(handler.loadIfcStore)
bpy.app.handlers.save_pre.append(handler.storeIdMap)
bpy.app.handlers.save_pre.append(handler.ensureIfcExported)
bpy.types.TOPBAR_MT_file_export.append(menu_func_export)
bpy.types.TOPBAR_MT_file_import.append(menu_func_import)
bpy.types.Scene.BIMProperties = bpy.props.PointerProperty(type=prop.BIMProperties)
bpy.types.Scene.DocProperties = bpy.props.PointerProperty(type=prop.DocProperties)
bpy.types.Object.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties)
bpy.types.Material.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties)
bpy.types.Collection.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties) # Check if we need this
bpy.types.Collection.BIMObjectProperties = bpy.props.PointerProperty(
type=prop.BIMObjectProperties
) # Check if we need this
bpy.types.Material.BIMMaterialProperties = bpy.props.PointerProperty(type=prop.BIMMaterialProperties)
bpy.types.Mesh.BIMMeshProperties = bpy.props.PointerProperty(type=prop.BIMMeshProperties)
bpy.types.Camera.BIMCameraProperties = bpy.props.PointerProperty(type=prop.BIMCameraProperties)
bpy.types.TextCurve.BIMTextProperties = bpy.props.PointerProperty(type=prop.BIMTextProperties)
bpy.types.Curve.BIMMeshProperties = bpy.props.PointerProperty(type=prop.BIMMeshProperties)
bpy.types.Camera.BIMMeshProperties = bpy.props.PointerProperty(type=prop.BIMMeshProperties)
bpy.types.PointLight.BIMMeshProperties = bpy.props.PointerProperty(type=prop.BIMMeshProperties)
bpy.types.SCENE_PT_unit.append(ui.ifc_units)
for module in modules.values():
module.register()
bpy.app.handlers.depsgraph_update_pre.append(operator.depsgraph_update_pre_handler)
bpy.app.handlers.load_post.append(handler.toggleDecorationsOnLoad)
def unregister():
for cls in reversed(classes):
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)
del bpy.types.Scene.BIMProperties
del bpy.types.Scene.DocProperties
del bpy.types.Object.BIMObjectProperties
del bpy.types.Material.BIMObjectProperties
del bpy.types.Collection.BIMObjectProperties # Check if we need this
del bpy.types.Collection.BIMObjectProperties # Check if we need this
del bpy.types.Material.BIMMaterialProperties
del bpy.types.Mesh.BIMMeshProperties
del bpy.types.Camera.BIMCameraProperties
del bpy.types.TextCurve.BIMTextProperties
del bpy.types.Curve.BIMMeshProperties
del bpy.types.Camera.BIMMeshProperties
del bpy.types.PointLight.BIMMeshProperties
bpy.types.SCENE_PT_unit.remove(ui.ifc_units)
for module in reversed(list(modules.values())):
module.unregister()
bpy.app.handlers.depsgraph_update_pre.remove(operator.depsgraph_update_pre_handler)
bpy.app.handlers.load_post.remove(handler.toggleDecorationsOnLoad)
Binary file not shown.
@@ -0,0 +1,24 @@
<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');
g.setOptions({
vCaptionType: 'Complete', // Set to Show Caption : None,Caption,Resource,Duration,Complete,
vQuarterColWidth: 36,
vDateTaskDisplayFormat: 'day dd month yyyy', // Shown in tool tip box
vDayMajorDateDisplayFormat: 'mon yyyy - Week ww',// Set format to dates in the "Major" header of the "Day" view
vWeekMinorDateDisplayFormat: 'dd mon', // Set format to display dates in the "Minor" header of the "Week" view
vLang: 'en',
vShowTaskInfoLink: 1, // Show link in tool tip (0/1)
vShowEndWeekDate: 0, // Show/Hide the date for the last day of the week in header for daily
vUseSingleCell: 10000, // Set the threshold cell per table row (Helps performance for large data.
vFormatArr: ['Day', 'Week', 'Month', 'Quarter'], // Even with setUseSingleCell using Hour format on such a large chart can cause issues in some browsers,
vTotalHeight: 1000,
});
var json_data = `
{{{json_data}}}
`;
JSGantt.parseJSONString(json_data, g);
g.Draw();
</script>
@@ -1,6 +1,6 @@
* { stroke-linecap: round; }
.cut { fill: black; stroke: black; stroke-linecap: 'round'; stroke-width: 0.35; }
.background { fill: white; stroke: black; stroke-linecap: 'round'; stroke-width: 0.25; }
* { stroke-linecap: round; stroke-linejoin: round; }
*[id] { fill: black; stroke: black; stroke-linecap: 'round'; stroke-width: 0.35; }
.projection { fill: white; stroke: black; stroke-linecap: 'round'; stroke-width: 0.25; }
.annotation { fill: none; stroke: black; stroke-linecap: 'round'; stroke-width: 0.25; }
.hidden { stroke-dasharray: 3, 2; }
.solid {}

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

+187
View File
@@ -0,0 +1,187 @@
import os
import bpy
import json
import numpy as np
import datetime
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
class IfcExporter:
def __init__(self, ifc_export_settings):
self.ifc_export_settings = ifc_export_settings
def export(self):
self.file = IfcStore.get_file()
self.set_header()
if bpy.context.scene.BIMProjectProperties.is_authoring:
self.sync_object_placements_and_deletions()
self.sync_edited_objects()
extension = self.ifc_export_settings.output_file.split(".")[-1]
if extension == "ifczip":
with tempfile.TemporaryDirectory() as unzipped_path:
filename, ext = os.path.splitext(os.path.basename(self.ifc_export_settings.output_file))
tmp_name = "{}.ifc".format(filename)
tmp_file = os.path.join(unzipped_path, tmp_name)
self.file.write(tmp_file)
with zipfile.ZipFile(
self.ifc_export_settings.output_file, mode="w", compression=zipfile.ZIP_DEFLATED, compresslevel=9
) as zf:
zf.write(tmp_file)
elif extension == "ifc":
self.file.write(self.ifc_export_settings.output_file)
elif extension == "ifcjson":
import ifcjson
if self.ifc_export_settings.json_version == "4":
jsonData = ifcjson.IFC2JSON4(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)
elif self.ifc_export_settings.json_version == "5a":
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)
def set_header(self):
self.file.wrapped_data.header.file_name.name = os.path.basename(self.ifc_export_settings.output_file)
self.file.wrapped_data.header.file_name.time_stamp = (
datetime.datetime.utcnow()
.replace(tzinfo=datetime.timezone.utc)
.astimezone()
.replace(microsecond=0)
.isoformat()
)
self.file.wrapped_data.header.file_name.preprocessor_version = "IfcOpenShell {}".format(ifcopenshell.version)
self.file.wrapped_data.header.file_name.originating_system = "{} {}".format(
self.get_application_name(), self.get_application_version()
)
def sync_object_placements_and_deletions(self):
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(self.file)
to_delete = []
for ifc_definition_id, obj in IfcStore.id_map.items():
try:
self.sync_object_placement(obj)
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 in IfcStore.edited_objs.copy():
if not obj:
continue
try:
bpy.ops.bim.update_representation(obj=obj.name)
except ReferenceError:
pass # The object is likely deleted
IfcStore.edited_objs.clear()
def sync_object_placement(self, obj):
blender_matrix = np.matrix(obj.matrix_world)
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
props = bpy.context.scene.BIMGeoreferenceProperties
if props.has_blender_offset and obj.BIMObjectProperties.blender_offset_type == "OBJECT_PLACEMENT":
ifc_matrix = ifcopenshell.util.geolocation.global2local(
ifc_matrix,
float(props.blender_eastings) * self.unit_scale,
float(props.blender_northings) * self.unit_scale,
float(props.blender_orthogonal_height) * self.unit_scale,
float(props.blender_x_axis_abscissa),
float(props.blender_x_axis_ordinate),
)
if not np.allclose(ifc_matrix, blender_matrix, atol=0.0001):
bpy.ops.bim.edit_object_placement(obj=obj.name)
def sync_object_container(self, guid, obj):
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
element_collection = bpy.data.collections.get(obj.name)
if self.file.schema == "IFC2X3":
if element.is_a("IfcProject"):
return
elif element.is_a("IfcContext"):
return
if (
(element.is_a("IfcElement") and element_collection)
or element.is_a("IfcSpatialStructureElement")
or element.is_a("IfcGrid")
):
try:
parent_collection = [c for c in bpy.data.collections if c.children.get(element_collection.name)][0]
except:
return # Out of the spatial tree
else:
parent_collection = obj.users_collection[0]
parent_obj = bpy.data.objects.get(parent_collection.name)
if not parent_obj or not parent_obj.BIMObjectProperties.ifc_definition_id:
return
parent = self.file.by_id(parent_obj.BIMObjectProperties.ifc_definition_id)
if parent.is_a("IfcSpatialStructureElement") and not element.is_a("IfcSpatialStructureElement"):
if parent != ifcopenshell.util.element.get_container(element):
bpy.ops.bim.assign_container(relating_structure=parent.id(), related_element=obj.name)
elif parent != ifcopenshell.util.element.get_aggregate(element):
bpy.ops.bim.assign_object(relating_object=parent_obj.name, related_object=obj.name)
def should_delete(self, obj):
try:
# This will throw an exception if the Blender object no longer exists
foo = obj.name
return False
except:
return True
def get_application_name(self):
return "BlenderBIM"
def get_application_version(self):
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]
]
)
class IfcExportSettings:
def __init__(self):
self.logger = None
self.output_file = None
@staticmethod
def factory(context, output_file, logger):
scene_bim = context.scene.BIMProperties
settings = IfcExportSettings()
settings.output_file = output_file
settings.logger = logger
return settings
+280
View File
@@ -0,0 +1,280 @@
import bpy
import json
import addon_utils
import ifcopenshell.api.owner.settings
from bpy.app.handlers import persistent
from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.attribute.data import Data as AttributeData
from ifcopenshell.api.material.data import Data as MaterialData
from ifcopenshell.api.type.data import Data as TypeData
global_subscription_owner = object()
def mode_callback(obj, data):
objects = bpy.context.selected_objects
if bpy.context.active_object:
objects += [bpy.context.active_object]
for obj in objects:
if (
obj.mode != "EDIT"
or not obj.data
or not isinstance(obj.data, (bpy.types.Mesh, bpy.types.Curve, bpy.types.TextCurve))
or not obj.BIMObjectProperties.ifc_definition_id
or not bpy.context.scene.BIMProjectProperties.is_authoring
):
return
if obj.data.BIMMeshProperties.ifc_definition_id:
representation = IfcStore.get_file().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
if representation.RepresentationType in ["Tessellation", "Brep", "Annotation2D"]:
IfcStore.edited_objs.add(obj)
elif IfcStore.get_file().by_id(obj.BIMObjectProperties.ifc_definition_id).is_a("IfcGridAxis"):
IfcStore.edited_objs.add(obj)
def name_callback(obj, data):
try:
obj.name
except:
return # In case the object RNA is gone during an undo / redo operation
# Blender names are up to 63 UTF-8 bytes
if len(bytes(obj.name, "utf-8")) >= 63:
return
if isinstance(obj, bpy.types.Material):
if obj.BIMObjectProperties.ifc_definition_id:
IfcStore.get_file().by_id(obj.BIMObjectProperties.ifc_definition_id).Name = obj.name
AttributeData.load(IfcStore.get_file(), obj.BIMObjectProperties.ifc_definition_id)
MaterialData.load_materials()
if obj.BIMMaterialProperties.ifc_style_id:
IfcStore.get_file().by_id(obj.BIMMaterialProperties.ifc_style_id).Name = obj.name
return
if not obj.BIMObjectProperties.ifc_definition_id or "/" not in obj.name:
return
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("IfcGrid"):
axis_obj = IfcStore.get_element(element.UAxes[0].id())
axis_collection = axis_obj.users_collection[0]
grid_collection = None
for collection in bpy.data.collections:
if axis_collection.name in collection.children.keys():
grid_collection = collection
break
if grid_collection:
grid_collection.name = obj.name
if element.is_a("IfcTypeProduct"):
TypeData.purge()
element.Name = "/".join(obj.name.split("/")[1:])
AttributeData.load(IfcStore.get_file(), obj.BIMObjectProperties.ifc_definition_id)
def active_object_callback():
obj = bpy.context.active_object
for obj in bpy.context.selected_objects:
if not obj.BIMObjectProperties.ifc_definition_id:
continue
stored_obj = IfcStore.get_element(obj.BIMObjectProperties.ifc_definition_id)
if stored_obj and stored_obj != obj:
bpy.ops.bim.copy_class(obj=obj.name)
def subscribe_to(object, data_path, callback):
try:
subscribe_to = object.path_resolve(data_path, False)
except:
return
bpy.msgbus.subscribe_rna(
key=subscribe_to,
owner=object,
args=(
object,
data_path,
),
notify=callback,
options={
"PERSISTENT",
},
)
def purge_module_data():
from blenderbim.bim import modules
for name, value in modules.items():
try:
getattr(getattr(getattr(ifcopenshell.api, name), "data"), "Data").purge()
except AttributeError:
pass
try:
getattr(value, "prop").purge()
except AttributeError:
pass
@persistent
def loadIfcStore(scene):
IfcStore.purge()
ifc_file = IfcStore.get_file()
if not ifc_file:
return
IfcStore.get_schema()
IfcStore.reload_linked_elements()
purge_module_data()
@persistent
def undo_pre(scene):
IfcStore.update_undo_redo_stack_objects()
@persistent
def undo_post(scene):
if IfcStore.last_transaction != bpy.context.scene.BIMProperties.last_transaction:
IfcStore.last_transaction = bpy.context.scene.BIMProperties.last_transaction
IfcStore.undo()
purge_module_data()
IfcStore.update_undo_redo_stack_objects()
IfcStore.reload_linked_elements(objects=[bpy.data.objects.get(o) for o in IfcStore.undo_redo_stack_objects])
@persistent
def redo_pre(scene):
IfcStore.update_undo_redo_stack_objects()
@persistent
def redo_post(scene):
if IfcStore.last_transaction != bpy.context.scene.BIMProperties.last_transaction:
IfcStore.last_transaction = bpy.context.scene.BIMProperties.last_transaction
IfcStore.redo()
purge_module_data()
IfcStore.update_undo_redo_stack_objects()
IfcStore.reload_linked_elements(objects=[bpy.data.objects.get(o) for o in IfcStore.undo_redo_stack_objects])
@persistent
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 ifcopenshell.api.run(
"owner.add_application",
ifc,
version=get_application_version(),
application_full_name="BlenderBIM Add-on",
application_identifier="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]
]
)
@persistent
def setDefaultProperties(scene):
global global_subscription_owner
active_object_key = bpy.types.LayerObjects, "active"
bpy.msgbus.subscribe_rna(
key=active_object_key, owner=global_subscription_owner, args=(), notify=active_object_callback
)
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"
drawing_style.render_type = "VIEWPORT"
drawing_style.raster_style = json.dumps(
{
"bpy.data.worlds[0].color": (1, 1, 1),
"bpy.context.scene.render.engine": "BLENDER_WORKBENCH",
"bpy.context.scene.render.film_transparent": False,
"bpy.context.scene.display.shading.show_object_outline": True,
"bpy.context.scene.display.shading.show_cavity": False,
"bpy.context.scene.display.shading.cavity_type": "BOTH",
"bpy.context.scene.display.shading.curvature_ridge_factor": 1,
"bpy.context.scene.display.shading.curvature_valley_factor": 1,
"bpy.context.scene.view_settings.view_transform": "Standard",
"bpy.context.scene.display.shading.light": "FLAT",
"bpy.context.scene.display.shading.color_type": "SINGLE",
"bpy.context.scene.display.shading.single_color": (1, 1, 1),
"bpy.context.scene.display.shading.show_shadows": False,
"bpy.context.scene.display.shading.shadow_intensity": 0.5,
"bpy.context.scene.display.light_direction": (0.5, 0.5, 0.5),
"bpy.context.scene.view_settings.use_curve_mapping": False,
"space.overlay.show_wireframes": True,
"space.overlay.wireframe_threshold": 0,
"space.overlay.show_floor": False,
"space.overlay.show_axis_x": False,
"space.overlay.show_axis_y": False,
"space.overlay.show_axis_z": False,
"space.overlay.show_object_origins": False,
"space.overlay.show_relationship_lines": False,
}
)
drawing_style = bpy.context.scene.DocProperties.drawing_styles.add()
drawing_style.name = "Shaded"
drawing_style.render_type = "VIEWPORT"
drawing_style.raster_style = json.dumps(
{
"bpy.data.worlds[0].color": (1, 1, 1),
"bpy.context.scene.render.engine": "BLENDER_WORKBENCH",
"bpy.context.scene.render.film_transparent": False,
"bpy.context.scene.display.shading.show_object_outline": True,
"bpy.context.scene.display.shading.show_cavity": True,
"bpy.context.scene.display.shading.cavity_type": "BOTH",
"bpy.context.scene.display.shading.curvature_ridge_factor": 1,
"bpy.context.scene.display.shading.curvature_valley_factor": 1,
"bpy.context.scene.view_settings.view_transform": "Standard",
"bpy.context.scene.display.shading.light": "STUDIO",
"bpy.context.scene.display.shading.color_type": "MATERIAL",
"bpy.context.scene.display.shading.single_color": (1, 1, 1),
"bpy.context.scene.display.shading.show_shadows": True,
"bpy.context.scene.display.shading.shadow_intensity": 0.5,
"bpy.context.scene.display.light_direction": (0.5, 0.5, 0.5),
"bpy.context.scene.view_settings.use_curve_mapping": False,
"space.overlay.show_wireframes": True,
"space.overlay.wireframe_threshold": 0,
"space.overlay.show_floor": False,
"space.overlay.show_axis_x": False,
"space.overlay.show_axis_y": False,
"space.overlay.show_axis_z": False,
"space.overlay.show_object_origins": False,
"space.overlay.show_relationship_lines": False,
}
)
drawing_style = bpy.context.scene.DocProperties.drawing_styles.add()
drawing_style.name = "Blender Default"
drawing_style.render_type = "DEFAULT"
bpy.ops.bim.save_drawing_style(index="2")
+84
View File
@@ -0,0 +1,84 @@
import bpy
import json
import math
import ifcopenshell
import ifcopenshell.util.attribute
from mathutils import geometry
from mathutils import Vector
from blenderbim.bim.ifc import IfcStore
def draw_attributes(props, layout, copy_operator=None):
for attribute in props:
row = layout.row(align=True)
value = None
if attribute.data_type == "string":
row.prop(attribute, "string_value", text=attribute.name)
value = attribute.string_value
elif attribute.data_type == "boolean":
row.prop(attribute, "bool_value", text=attribute.name)
value = attribute.bool_value
elif attribute.data_type == "integer":
row.prop(attribute, "int_value", text=attribute.name)
value = attribute.int_value
elif attribute.data_type == "float":
row.prop(attribute, "float_value", text=attribute.name)
value = attribute.float_value
elif attribute.data_type == "enum":
row.prop(attribute, "enum_value", text=attribute.name)
value = attribute.enum_value
if attribute.is_optional:
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
if copy_operator:
op = row.operator(f"{copy_operator}", text="", icon="COPYDOWN")
op.data = json.dumps({"name": attribute.name, "value": value, "is_null": attribute.is_null})
def import_attributes(ifc_class, props, data, callback=None):
for attribute in IfcStore.get_schema().declaration_by_name(ifc_class).all_attributes():
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
if data_type == "entity" or (isinstance(data_type, tuple) and "entity" in ".".join(data_type)):
continue
new = props.add()
new.name = attribute.name()
new.is_null = data[attribute.name()] is None
new.is_optional = attribute.optional()
new.data_type = data_type if isinstance(data_type, str) else ""
is_handled_by_callback = callback(attribute.name(), new, data) if callback else None
if is_handled_by_callback:
pass # Our job is done
elif is_handled_by_callback is False:
props.remove(len(props) - 1)
elif data_type == "string":
new.string_value = "" if new.is_null else data[attribute.name()]
elif data_type == "boolean":
new.bool_value = False if new.is_null else data[attribute.name()]
elif data_type == "integer":
new.int_value = 0 if new.is_null else data[attribute.name()]
elif data_type == "float":
new.float_value = 0.0 if new.is_null else data[attribute.name()]
elif data_type == "enum":
new.enum_items = json.dumps(ifcopenshell.util.attribute.get_enum_items(attribute))
if data[attribute.name()]:
new.enum_value = data[attribute.name()]
def export_attributes(props, callback=None):
attributes = {}
for attribute in props:
is_handled_by_callback = callback(attributes, attribute) if callback else False
if attribute.is_null:
attributes[attribute.name] = None
elif is_handled_by_callback:
pass # Our job is done
elif 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 == "float":
attributes[attribute.name] = attribute.float_value
elif attribute.data_type == "enum":
attributes[attribute.name] = attribute.enum_value
return attributes
+235
View File
@@ -0,0 +1,235 @@
import bpy
import uuid
import ifcopenshell
import blenderbim.bim.handler
class IfcStore:
path = ""
file = None
schema = None
id_map = {}
guid_map = {}
edited_objs = set()
pset_template_path = ""
pset_template_file = None
library_path = ""
library_file = None
element_listeners = set()
undo_redo_stack_objects = set()
current_transaction = ""
last_transaction = ""
history = []
future = []
@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():
if IfcStore.file is None:
IfcStore.path = bpy.context.scene.BIMProperties.ifc_file
if IfcStore.path:
try:
IfcStore.file = ifcopenshell.open(IfcStore.path)
except:
IfcStore.file
return IfcStore.file
@staticmethod
def get_schema():
if IfcStore.file is None:
return
elif IfcStore.schema is None:
IfcStore.schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(IfcStore.file.schema)
return IfcStore.schema
@staticmethod
def get_element(id_or_guid):
if isinstance(id_or_guid, int):
map_object = IfcStore.id_map
else:
map_object = IfcStore.guid_map
try:
obj = map_object[id_or_guid]
obj.name # In case the object has been deleted, this triggers an exception
except:
return
return obj
@staticmethod
def add_element_listener(callback):
IfcStore.element_listeners.add(callback)
@staticmethod
def update_undo_redo_stack_objects():
"""Keeps track of selected object names, typically during undo and redo
When any Blender object is stored outside a Blender PointerProperty, such as
in a regular Python list, there is the likely probability that the object
will be invalidated when undo or redo occurs. Object invalidation seems to
only occur for selected objects either pre/post undo/redo event, including
selected objects for consecutive undo/redos.
So if I first select o1, then o2, then o3, then press undo, o3 will be
invalidated. If instead I press undo twice, o3 and o2 will be invalidated.
"""
if bpy.context.active_object:
objects = set([o.name for o in bpy.context.selected_objects + [bpy.context.active_object]])
else:
objects = set([o.name for o in bpy.context.selected_objects])
IfcStore.undo_redo_stack_objects |= objects
@staticmethod
def reload_linked_elements(objects=None):
file = IfcStore.get_file()
if not file:
return
if objects is None:
objects = bpy.data.objects
for obj in objects:
if not obj:
continue
if not obj.BIMObjectProperties.ifc_definition_id:
continue
element = file.by_id(obj.BIMObjectProperties.ifc_definition_id)
data = {"id": element.id(), "obj": obj.name}
if hasattr(element, "GlobalId"):
data["guid"] = element.GlobalId
IfcStore.commit_link_element(data)
@staticmethod
def link_element(element, obj):
IfcStore.id_map[element.id()] = obj
if hasattr(element, "GlobalId"):
IfcStore.guid_map[element.GlobalId] = obj
obj.BIMObjectProperties.ifc_definition_id = element.id()
blenderbim.bim.handler.subscribe_to(obj, "mode", blenderbim.bim.handler.mode_callback)
blenderbim.bim.handler.subscribe_to(obj, "name", blenderbim.bim.handler.name_callback)
for listener in IfcStore.element_listeners:
listener(element, obj)
if IfcStore.history:
data = {"id": element.id(), "guid": getattr(element, "GlobalId", None), "obj": obj.name}
IfcStore.history[-1]["operations"].append(
{"rollback": IfcStore.rollback_link_element, "commit": IfcStore.commit_link_element, "data": data}
)
@staticmethod
def rollback_link_element(data):
del IfcStore.id_map[data["id"]]
if data["guid"]:
del IfcStore.guid_map[data["guid"]]
@staticmethod
def commit_link_element(data):
obj = bpy.data.objects.get(data["obj"])
IfcStore.id_map[data["id"]] = obj
if data["guid"]:
IfcStore.guid_map[data["guid"]] = obj
blenderbim.bim.handler.subscribe_to(obj, "mode", blenderbim.bim.handler.mode_callback)
blenderbim.bim.handler.subscribe_to(obj, "name", blenderbim.bim.handler.name_callback)
@staticmethod
def unlink_element(element=None, obj=None):
if element is None:
try:
element = IfcStore.get_file().by_id(obj.BIMObjectProperties.ifc_definition_id)
except:
pass
try:
if element:
del IfcStore.id_map[element.id()]
else:
del IfcStore.id_map[obj.BIMObjectProperties.ifc_definition_id]
except:
pass
try:
if element and hasattr(element, "GlobalId"):
del IfcStore.guid_map[element.GlobalId]
except:
pass
if obj:
obj.BIMObjectProperties.ifc_definition_id = 0
@staticmethod
def execute_ifc_operator(operator, context):
is_top_level_operator = not bool(IfcStore.current_transaction)
if is_top_level_operator:
IfcStore.begin_transaction(operator)
IfcStore.get_file().begin_transaction()
# This empty transaction ensures that each operator has at least one transaction
IfcStore.add_transaction_operation(operator, rollback=lambda data: True, commit=lambda data: True)
else:
operator.transaction_key = IfcStore.current_transaction
result = getattr(operator, "_execute")(context)
if is_top_level_operator:
IfcStore.get_file().end_transaction()
IfcStore.add_transaction_operation(
operator, rollback=lambda d: IfcStore.get_file().undo(), commit=lambda d: IfcStore.get_file().redo()
)
IfcStore.end_transaction(operator)
return result
@staticmethod
def begin_transaction(operator):
IfcStore.undo_redo_stack_objects = set()
IfcStore.current_transaction = str(uuid.uuid4())
operator.transaction_key = IfcStore.current_transaction
@staticmethod
def end_transaction(operator):
IfcStore.current_transaction = ""
operator.transaction_key = ""
@staticmethod
def add_transaction_operation(operator, rollback=None, commit=None):
key = getattr(operator, "transaction_key", None)
data = getattr(operator, "transaction_data", None)
bpy.context.scene.BIMProperties.last_transaction = key
IfcStore.last_transaction = key
rollback = rollback or getattr(operator, "rollback", lambda data: True)
commit = commit or getattr(operator, "commit", lambda data: True)
if IfcStore.history and IfcStore.history[-1]["key"] == key:
IfcStore.history[-1]["operations"].append({"rollback": rollback, "commit": commit, "data": data})
else:
IfcStore.history.append(
{"key": key, "operations": [{"rollback": rollback, "commit": commit, "data": data}]}
)
IfcStore.future = []
@staticmethod
def undo():
if not IfcStore.history:
return
event = IfcStore.history.pop()
for transaction in event["operations"][::-1]:
transaction["rollback"](transaction["data"])
IfcStore.future.append(event)
@staticmethod
def redo():
if not IfcStore.future:
return
event = IfcStore.future.pop()
for transaction in event["operations"]:
transaction["commit"](transaction["data"])
IfcStore.history.append(event)
@@ -1,47 +1,57 @@
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):
bl_idname = "bim.assign_object"
bl_label = "Assign Object"
bl_options = {"REGISTER", "UNDO"}
relating_object: bpy.props.StringProperty()
related_object: bpy.props.StringProperty()
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
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:
if collection.name.startswith("Ifc"):
collection.objects.unlink(related_object)
relating_collection.objects.link(related_object)
return {"FINISHED"}
def remove_collection(self, parent, child):
@@ -54,6 +64,7 @@ class AssignObject(bpy.types.Operator):
class EnableEditingAggregate(bpy.types.Operator):
bl_idname = "bim.enable_editing_aggregate"
bl_label = "Enable Editing Aggregate"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
bpy.context.active_object.BIMObjectProperties.relating_object = None
@@ -65,6 +76,7 @@ class DisableEditingAggregate(bpy.types.Operator):
bl_idname = "bim.disable_editing_aggregate"
bl_label = "Disable Editing Aggregate"
obj: bpy.props.StringProperty()
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
@@ -75,9 +87,13 @@ class DisableEditingAggregate(bpy.types.Operator):
class AddAggregate(bpy.types.Operator):
bl_idname = "bim.add_aggregate"
bl_label = "Add Aggregate"
bl_options = {"REGISTER", "UNDO"}
obj: bpy.props.StringProperty()
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context):
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
aggregate_collection = bpy.data.collections.new("IfcElementAssembly/Assembly")
bpy.context.scene.collection.children.link(aggregate_collection)
@@ -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
@@ -16,10 +16,12 @@ class BIM_PT_aggregate(Panel):
props = context.active_object.BIMObjectProperties
if not props.ifc_definition_id:
return False
if not IfcStore.get_element(props.ifc_definition_id):
return False
if not IfcStore.get_file().by_id(props.ifc_definition_id).is_a("IfcObjectDefinition"):
return False
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 +31,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,14 +1,15 @@
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):
bl_idname = "bim.enable_editing_attributes"
bl_label = "Enable Editing Attributes"
bl_options = {"REGISTER", "UNDO"}
obj: bpy.props.StringProperty()
obj_type: bpy.props.StringProperty()
@@ -24,7 +25,7 @@ class EnableEditingAttributes(bpy.types.Operator):
props.attributes.remove(0)
for attribute in Data.products[oprops.ifc_definition_id]:
new = props.attributes.add()
if attribute["type"] == "entity":
if attribute["type"] == "entity" or (attribute["type"] == "list" and attribute["list_type"] == "entity"):
continue
new.name = attribute["name"]
new.is_null = attribute["is_null"]
@@ -33,7 +34,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"]:
@@ -45,6 +46,7 @@ class EnableEditingAttributes(bpy.types.Operator):
class DisableEditingAttributes(bpy.types.Operator):
bl_idname = "bim.disable_editing_attributes"
bl_label = "Disable Editing Attributes"
bl_options = {"REGISTER", "UNDO"}
obj: bpy.props.StringProperty()
obj_type: bpy.props.StringProperty()
@@ -61,10 +63,14 @@ class DisableEditingAttributes(bpy.types.Operator):
class EditAttributes(bpy.types.Operator):
bl_idname = "bim.edit_attributes"
bl_label = "Edit Attributes"
bl_options = {"REGISTER", "UNDO"}
obj: bpy.props.StringProperty()
obj_type: bpy.props.StringProperty()
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context):
self.file = IfcStore.get_file()
if self.obj_type == "Object":
obj = bpy.data.objects.get(self.obj)
@@ -95,17 +101,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"}
@@ -113,6 +118,7 @@ class EditAttributes(bpy.types.Operator):
class GenerateGlobalId(bpy.types.Operator):
bl_idname = "bim.generate_global_id"
bl_label = "Regenerate GlobalId"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
index = bpy.context.active_object.BIMAttributeProperties.attributes.find("GlobalId")
@@ -1,6 +1,4 @@
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,14 +8,14 @@ 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)
op = row.operator("bim.edit_attributes", icon="CHECKMARK", text="Save Attributes")
op.obj_type = obj_type
op.obj = obj.name
op = row.operator("bim.disable_editing_attributes", icon="X", text="")
op = row.operator("bim.disable_editing_attributes", icon="CANCEL", text="")
op.obj_type = obj_type
op.obj = obj.name
@@ -41,15 +42,17 @@ def draw_ui(context, layout, obj_type):
icon="RADIOBUT_OFF" if blender_attribute.is_null else "RADIOBUT_ON",
text="",
)
# TODO: reimplement, see #1222
# op = row.operator("bim.copy_attribute_to_selection", icon="COPYDOWN", text="")
# op.attribute_name = attribute.name
# op.attribute_value = attribute.string_value
else:
row = layout.row()
op = row.operator("bim.enable_editing_attributes", icon="GREASEPENCIL", text="Edit")
op.obj_type = obj_type
op.obj = obj.name
if "GlobalId" not in [a["name"] for a in Data.products[oprops.ifc_definition_id]]:
row = layout.row(align=True)
row.label(text="STEP ID")
row.label(text=str(oprops.ifc_definition_id))
for attribute in Data.products[oprops.ifc_definition_id]:
if attribute["value"] is None or attribute["type"] == "entity":
continue
@@ -71,6 +74,8 @@ class BIM_PT_object_attributes(Panel):
@classmethod
def poll(cls, context):
if not IfcStore.get_element(context.active_object.BIMObjectProperties.ifc_definition_id):
return False
return bool(context.active_object.BIMObjectProperties.ifc_definition_id)
def draw(self, context):
@@ -78,7 +83,7 @@ class BIM_PT_object_attributes(Panel):
class BIM_PT_material_attributes(Panel):
bl_label = "IFC Attributes"
bl_label = "IFC Material Attributes"
bl_idname = "BIM_PT_material_attributes"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
@@ -0,0 +1,18 @@
import bpy
from . import ui, prop, operator
classes = (
prop.AuginProperties,
ui.BIM_PT_augin,
operator.AuginLogin,
operator.AuginCreateNewModel,
operator.AuginReset,
)
def register():
bpy.types.Scene.AuginProperties = bpy.props.PointerProperty(type=prop.AuginProperties)
def unregister():
del bpy.types.Scene.AuginProperties
@@ -0,0 +1,136 @@
import os
import bpy
import json
import string
import random
import shutil
import requests
import tempfile
import datetime
import addon_utils
class AuginLogin(bpy.types.Operator):
bl_idname = "bim.augin_login"
bl_label = "Login to Augin"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
props = bpy.context.scene.AuginProperties
url = "https://server.auge.pro.br/API/v3/augin_rest.php/user_login"
payload = {"email": props.username, "password": props.password}
response = requests.request("POST", url, data=payload)
result = response.json()
if result["success_feedback"]["success"]:
props.token = result["token"]
else:
self.report({"ERROR"}, "Login failed :(")
return {"FINISHED"}
class AuginReset(bpy.types.Operator):
bl_idname = "bim.augin_reset"
bl_label = "Upload Another Project"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
props = bpy.context.scene.AuginProperties
props.is_success = False
return {"FINISHED"}
class AuginCreateNewModel(bpy.types.Operator):
bl_idname = "bim.augin_create_new_model"
bl_label = "Create New Augin Model"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
import boto3
from botocore.config import Config
props = bpy.context.scene.AuginProperties
# Create project
url = "https://server.auge.pro.br/API/v3/augin_rest.php/new_model"
chars = string.ascii_uppercase + string.ascii_lowercase + string.digits
props.project_filename = "BBM{}{}".format(
datetime.datetime.now().strftime("%Y%m%d%H%M%S"), "".join(random.choice(chars) for _ in range(6))
)
addon_version = ".".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]
]
)
payload = {
"user_token": props.token,
"partner_token": "Mmf2zpxdgrkHaUNRm6Cv7GLJdBAeb7Rn",
"project_name": props.project_name,
"model_filename": props.project_filename + ".ifc",
"thumb_filename": props.project_filename + ".png",
"app_name": "BlenderBIM",
"sw_version": bpy.app.version_string,
"plugin_version": addon_version,
}
response = requests.request("POST", url, data=payload)
result = response.json()
# Upload to S3
my_config = Config(
region_name=result["s3_region"],
)
client = boto3.client(
"s3",
aws_access_key_id=result["s3_key"],
aws_secret_access_key=result["s3_secret"],
aws_session_token=result["s3_token"],
config=my_config,
)
# Generate thumb
old_file_format = context.scene.render.image_settings.file_format
old_filepath = context.scene.render.filepath
cam = bpy.data.cameras.new("Camera")
cam_obj = bpy.data.objects.new("Camera", cam)
context.scene.collection.objects.link(cam_obj)
context.scene.camera = cam_obj
tmpdir = tempfile.mkdtemp()
thumb_path = os.path.join(tmpdir, "thumb.png")
context.scene.render.image_settings.file_format = "PNG"
context.scene.render.filepath = thumb_path
bpy.ops.render.opengl(write_still=True)
context.scene.render.image_settings.file_format = old_file_format
context.scene.render.filepath = old_filepath
client.upload_file(bpy.context.scene.BIMProperties.ifc_file, result["s3_bucket"], result["model_path"])
client.upload_file(thumb_path, result["s3_bucket"], result["thumb_path"])
# Notify done
url = "https://server.auge.pro.br/API/v3/augin_rest.php/files_uploaded"
payload = {
"user_token": props.token,
"ifc_filesize": os.path.getsize(bpy.context.scene.BIMProperties.ifc_file),
"model_filesize": os.path.getsize(bpy.context.scene.BIMProperties.ifc_file),
"thumb_filesize": os.path.getsize(thumb_path),
"model_upload_path": result["model_path"],
"thumb_upload_path": result["thumb_path"],
"model_id": result["model_id"],
}
headers = {"Content-Type": "application/x-www-form-urlencoded"}
response = requests.request("POST", url, headers=headers, data=payload)
shutil.rmtree(tmpdir)
props.is_success = True
return {"FINISHED"}
@@ -0,0 +1,11 @@
import bpy.props
import bpy.types
class AuginProperties(bpy.types.PropertyGroup):
username: bpy.props.StringProperty(name="Username")
password: bpy.props.StringProperty(name="Password")
token: bpy.props.StringProperty(name="Token")
project_name: bpy.props.StringProperty(name="Project Name")
project_filename: bpy.props.StringProperty(name="IFC Filename")
is_success: bpy.props.BoolProperty(name="Is Successful Upload", default=False)
@@ -0,0 +1,45 @@
import bpy.types
class BIM_PT_augin(bpy.types.Panel):
bl_label = "Augin"
bl_idname = "BIM_PT_augin"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
def draw(self, context):
layout = self.layout
layout.use_property_split = True
scene = context.scene
props = scene.AuginProperties
if not props.token:
row = layout.row()
row.prop(props, "username")
row = layout.row()
row.prop(props, "password")
row = layout.row()
row.operator("bim.augin_login")
return
row = layout.row()
row.label(text="Logged in as " + props.username)
if not bpy.context.scene.BIMProperties.ifc_file:
row = layout.row()
row.label(text="No IFC Found")
return
if props.is_success:
row = layout.row()
row.label(text="Upload Successful")
row = layout.row()
row.operator("bim.augin_reset")
else:
row = layout.row()
row.prop(props, "project_name")
row = layout.row()
row.operator("bim.augin_create_new_model")
@@ -1,5 +1,5 @@
import bcf
import bcf.bcfxml
import bcf.v2.bcfxml
class BcfStore:
bcfxml = None
@@ -7,5 +7,5 @@ class BcfStore:
@staticmethod
def get_bcfxml():
if not BcfStore.bcfxml:
BcfStore.bcfxml = bcf.bcfxml.BcfXml()
BcfStore.bcfxml = bcf.v2.bcfxml.BcfXml()
return BcfStore.bcfxml
@@ -1,6 +1,8 @@
import os
import bpy
import bcf
import bcf.bcfxml
import bcf.v2.data
from . import bcfstore
from blenderbim.bim.ifc import IfcStore
from math import radians, degrees, atan, tan, cos, sin
@@ -9,6 +11,7 @@ from mathutils import Vector, Matrix, Euler, geometry
class NewBcfProject(bpy.types.Operator):
bl_idname = "bim.new_bcf_project"
bl_label = "New BCF Project"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
bpy.context.scene.BCFProperties.is_loaded = False
@@ -22,13 +25,16 @@ class NewBcfProject(bpy.types.Operator):
class LoadBcfProject(bpy.types.Operator):
bl_idname = "bim.load_bcf_project"
bl_label = "Load BCF Project"
bl_options = {"REGISTER", "UNDO"}
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
filter_glob: bpy.props.StringProperty(default="*.bcf;*.bcfzip", options={"HIDDEN"})
def execute(self, context):
bpy.context.scene.BCFProperties.is_loaded = False
bcfxml = bcfstore.BcfStore.get_bcfxml()
if self.filepath:
bcfxml.get_project(self.filepath)
bcfstore.BcfStore.bcfxml = bcf.bcfxml.load(self.filepath)
bcfxml = bcfstore.BcfStore.get_bcfxml()
bcfxml.get_project()
bpy.context.scene.BCFProperties.name = bcfxml.project.name
bpy.ops.bim.load_bcf_topics()
bpy.context.scene.BCFProperties.is_loaded = True
@@ -42,6 +48,7 @@ class LoadBcfProject(bpy.types.Operator):
class LoadBcfTopics(bpy.types.Operator):
bl_idname = "bim.load_bcf_topics"
bl_label = "Load BCF Topics"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
@@ -59,6 +66,7 @@ class LoadBcfTopics(bpy.types.Operator):
class LoadBcfTopic(bpy.types.Operator):
bl_idname = "bim.load_bcf_topic"
bl_label = "Load BCF Topics"
bl_options = {"REGISTER", "UNDO"}
topic_guid: bpy.props.StringProperty()
topic_index: bpy.props.IntProperty()
@@ -129,6 +137,7 @@ class LoadBcfTopic(bpy.types.Operator):
class LoadBcfComments(bpy.types.Operator):
bl_idname = "bim.load_bcf_comments"
bl_label = "Load BCF Comments"
bl_options = {"REGISTER", "UNDO"}
topic_guid: bpy.props.StringProperty()
def execute(self, context):
@@ -157,6 +166,7 @@ class LoadBcfComments(bpy.types.Operator):
class EditBcfProjectName(bpy.types.Operator):
bl_idname = "bim.edit_bcf_project_name"
bl_label = "Edit BCF Project Name"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
@@ -168,6 +178,7 @@ class EditBcfProjectName(bpy.types.Operator):
class EditBcfAuthor(bpy.types.Operator):
bl_idname = "bim.edit_bcf_author"
bl_label = "Edit BCF Author"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
@@ -178,6 +189,7 @@ class EditBcfAuthor(bpy.types.Operator):
class EditBcfTopicName(bpy.types.Operator):
bl_idname = "bim.edit_bcf_topic_name"
bl_label = "Edit BCF Topic Name"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
props = bpy.context.scene.BCFProperties
@@ -192,6 +204,7 @@ class EditBcfTopicName(bpy.types.Operator):
class EditBcfTopic(bpy.types.Operator):
bl_idname = "bim.edit_bcf_topic"
bl_label = "Edit BCF Topic"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
props = bpy.context.scene.BCFProperties
@@ -216,6 +229,7 @@ class EditBcfTopic(bpy.types.Operator):
class SaveBcfProject(bpy.types.Operator):
bl_idname = "bim.save_bcf_project"
bl_label = "Save BCF Project"
bl_options = {"REGISTER", "UNDO"}
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def execute(self, context):
@@ -231,6 +245,7 @@ class SaveBcfProject(bpy.types.Operator):
class AddBcfTopic(bpy.types.Operator):
bl_idname = "bim.add_bcf_topic"
bl_label = "Add BCF Topic"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
@@ -244,13 +259,14 @@ class AddBcfTopic(bpy.types.Operator):
class AddBcfBimSnippet(bpy.types.Operator):
bl_idname = "bim.add_bcf_bim_snippet"
bl_label = "Add BCF BIM Snippet"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = bpy.context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index]
topic = bcfxml.topics[blender_topic.name]
bim_snippet = bcf.data.BimSnippet()
bim_snippet = bcf.v2.data.BimSnippet()
bim_snippet.reference = blender_topic.bim_snippet_reference
bim_snippet.reference_schema = blender_topic.bim_snippet_schema
bim_snippet.snippet_type = blender_topic.bim_snippet_type
@@ -262,6 +278,7 @@ class AddBcfBimSnippet(bpy.types.Operator):
class AddBcfRelatedTopic(bpy.types.Operator):
bl_idname = "bim.add_bcf_related_topic"
bl_label = "Add BCF Related Topic"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
@@ -270,7 +287,7 @@ class AddBcfRelatedTopic(bpy.types.Operator):
related_topic = None
for topic in bcfxml.topics.values():
if topic.title == blender_topic.related_topic:
related_topic = bcf.data.RelatedTopic()
related_topic = bcf.v2.data.RelatedTopic()
related_topic.guid = topic.guid
break
if not related_topic:
@@ -285,13 +302,14 @@ class AddBcfRelatedTopic(bpy.types.Operator):
class AddBcfHeaderFile(bpy.types.Operator):
bl_idname = "bim.add_bcf_header_file"
bl_label = "Add BCF Header File"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = bpy.context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index]
topic = bcfxml.topics[blender_topic.name]
header_file = bcf.data.HeaderFile()
header_file = bcf.v2.data.HeaderFile()
header_file.reference = blender_topic.file_reference
if not os.path.exists(header_file.reference):
header_file.filename = header_file.reference
@@ -307,6 +325,7 @@ class AddBcfHeaderFile(bpy.types.Operator):
class ViewBcfTopic(bpy.types.Operator):
bl_idname = "bim.view_bcf_topic"
bl_label = "Get BCF Topic"
bl_options = {"REGISTER", "UNDO"}
topic_guid: bpy.props.StringProperty()
def execute(self, context):
@@ -319,6 +338,7 @@ class ViewBcfTopic(bpy.types.Operator):
class AddBcfViewpoint(bpy.types.Operator):
bl_idname = "bim.add_bcf_viewpoint"
bl_label = "Add BCF Viewpoint"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
if not bpy.context.scene.camera:
@@ -327,14 +347,14 @@ class AddBcfViewpoint(bpy.types.Operator):
props = bpy.context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index]
topic = bcfxml.topics[blender_topic.name]
viewpoint = bcf.data.Viewpoint()
viewpoint = bcf.v2.data.Viewpoint()
if bpy.context.scene.camera.data.type == "ORTHO":
camera = bcf.data.OrthogonalCamera()
camera = bcf.v2.data.OrthogonalCamera()
camera.view_to_world_scale = bpy.context.scene.camera.data.ortho_scale
viewpoint.orthogonal_camera = camera
elif bpy.context.scene.camera.data.type == "PERSP":
camera = bcf.data.PerspectiveCamera()
camera = bcf.v2.data.PerspectiveCamera()
camera.field_of_view = degrees(bpy.context.scene.camera.data.angle)
viewpoint.perspective_camera = camera
camera.camera_view_point.x = bpy.context.scene.camera.location.x
@@ -366,6 +386,7 @@ class AddBcfViewpoint(bpy.types.Operator):
class RemoveBcfViewpoint(bpy.types.Operator):
bl_idname = "bim.remove_bcf_viewpoint"
bl_label = "Remove BCF Viewpoint"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
@@ -381,6 +402,7 @@ class RemoveBcfViewpoint(bpy.types.Operator):
class RemoveBcfFile(bpy.types.Operator):
bl_idname = "bim.remove_bcf_file"
bl_label = "Remove BCF File"
bl_options = {"REGISTER", "UNDO"}
index: bpy.props.IntProperty()
def execute(self, context):
@@ -396,6 +418,7 @@ class RemoveBcfFile(bpy.types.Operator):
class AddBcfReferenceLink(bpy.types.Operator):
bl_idname = "bim.add_bcf_reference_link"
bl_label = "Add BCF Reference Link"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
@@ -414,6 +437,7 @@ class AddBcfReferenceLink(bpy.types.Operator):
class AddBcfDocumentReference(bpy.types.Operator):
bl_idname = "bim.add_bcf_document_reference"
bl_label = "Add BCF Document Reference"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
@@ -422,7 +446,7 @@ class AddBcfDocumentReference(bpy.types.Operator):
topic = bcfxml.topics[blender_topic.name]
if not blender_topic.document_reference:
return {"FINISHED"}
document_reference = bcf.data.DocumentReference()
document_reference = bcf.v2.data.DocumentReference()
document_reference.referenced_document = blender_topic.document_reference
document_reference.description = blender_topic.document_reference_description or None
bcfxml.add_document_reference(topic, document_reference)
@@ -435,6 +459,7 @@ class AddBcfDocumentReference(bpy.types.Operator):
class AddBcfLabel(bpy.types.Operator):
bl_idname = "bim.add_bcf_label"
bl_label = "Add BCF Label"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
@@ -454,6 +479,7 @@ class AddBcfLabel(bpy.types.Operator):
class EditBcfReferenceLinks(bpy.types.Operator):
bl_idname = "bim.edit_bcf_reference_links"
bl_label = "Edit BCF Reference Link"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
@@ -471,6 +497,7 @@ class EditBcfReferenceLinks(bpy.types.Operator):
class EditBcfLabels(bpy.types.Operator):
bl_idname = "bim.edit_bcf_labels"
bl_label = "Edit BCF Labels"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
@@ -488,6 +515,7 @@ class EditBcfLabels(bpy.types.Operator):
class RemoveBcfReferenceLink(bpy.types.Operator):
bl_idname = "bim.remove_bcf_reference_link"
bl_label = "Remove BCF Reference Link"
bl_options = {"REGISTER", "UNDO"}
index: bpy.props.IntProperty()
def execute(self, context):
@@ -504,6 +532,7 @@ class RemoveBcfReferenceLink(bpy.types.Operator):
class RemoveBcfLabel(bpy.types.Operator):
bl_idname = "bim.remove_bcf_label"
bl_label = "Remove BCF Label"
bl_options = {"REGISTER", "UNDO"}
index: bpy.props.IntProperty()
def execute(self, context):
@@ -520,6 +549,7 @@ class RemoveBcfLabel(bpy.types.Operator):
class RemoveBcfBimSnippet(bpy.types.Operator):
bl_idname = "bim.remove_bcf_bim_snippet"
bl_label = "Remove BCF BIM Snippet"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
@@ -536,6 +566,7 @@ class RemoveBcfBimSnippet(bpy.types.Operator):
class RemoveBcfDocumentReference(bpy.types.Operator):
bl_idname = "bim.remove_bcf_document_reference"
bl_label = "Remove BCF Document Reference"
bl_options = {"REGISTER", "UNDO"}
index: bpy.props.IntProperty()
def execute(self, context):
@@ -551,6 +582,7 @@ class RemoveBcfDocumentReference(bpy.types.Operator):
class RemoveBcfRelatedTopic(bpy.types.Operator):
bl_idname = "bim.remove_bcf_related_topic"
bl_label = "Remove BCF Related Topic"
bl_options = {"REGISTER", "UNDO"}
index: bpy.props.IntProperty()
def execute(self, context):
@@ -567,6 +599,7 @@ class RemoveBcfRelatedTopic(bpy.types.Operator):
class RemoveBcfComment(bpy.types.Operator):
bl_idname = "bim.remove_bcf_comment"
bl_label = "Remove BCF Comment"
bl_options = {"REGISTER", "UNDO"}
comment_guid: bpy.props.StringProperty()
def execute(self, context):
@@ -582,6 +615,7 @@ class RemoveBcfComment(bpy.types.Operator):
class EditBcfComment(bpy.types.Operator):
bl_idname = "bim.edit_bcf_comment"
bl_label = "Edit BCF Comment"
bl_options = {"REGISTER", "UNDO"}
comment_guid: bpy.props.StringProperty()
def execute(self, context):
@@ -600,6 +634,7 @@ class EditBcfComment(bpy.types.Operator):
class AddBcfComment(bpy.types.Operator):
bl_idname = "bim.add_bcf_comment"
bl_label = "Add BCF Comment"
bl_options = {"REGISTER", "UNDO"}
comment_guid: bpy.props.StringProperty()
def execute(self, context):
@@ -609,20 +644,20 @@ class AddBcfComment(bpy.types.Operator):
topic = bcfxml.topics[blender_topic.name]
if not blender_topic.comment:
return {"FINISHED"}
comment = bcf.data.Comment()
comment = bcf.v2.data.Comment()
comment.comment = blender_topic.comment
if blender_topic.has_related_viewpoint and blender_topic.viewpoints:
comment.viewpoint = bcf.data.Viewpoint()
comment.viewpoint = bcf.v2.data.Viewpoint()
comment.viewpoint.guid = blender_topic.viewpoints
bcfxml.add_comment(topic, comment)
bpy.ops.bim.load_bcf_comments(topic_guid = topic.guid)
print(bcfxml.filepath)
return {"FINISHED"}
class ActivateBcfViewpoint(bpy.types.Operator):
bl_idname = "bim.activate_bcf_viewpoint"
bl_label = "Activate BCF Viewpoint"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
self.file = IfcStore.get_file()
@@ -832,6 +867,7 @@ class OpenBcfReferenceLink(bpy.types.Operator):
class SelectBcfHeaderFile(bpy.types.Operator):
bl_idname = "bim.select_bcf_header_file"
bl_label = "Select BCF Header File"
bl_options = {"REGISTER", "UNDO"}
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def execute(self, context):
@@ -849,6 +885,7 @@ class SelectBcfHeaderFile(bpy.types.Operator):
class SelectBcfBimSnippetReference(bpy.types.Operator):
bl_idname = "bim.select_bcf_bim_snippet_reference"
bl_label = "Select BCF BIM Snippet Reference"
bl_options = {"REGISTER", "UNDO"}
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def execute(self, context):
@@ -866,6 +903,7 @@ class SelectBcfBimSnippetReference(bpy.types.Operator):
class SelectBcfDocumentReference(bpy.types.Operator):
bl_idname = "bim.select_bcf_document_reference"
bl_label = "Select BCF Document Reference"
bl_options = {"REGISTER", "UNDO"}
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def execute(self, context):
@@ -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()
@@ -3,9 +3,14 @@ import bpy
import tempfile
import webbrowser
import ifcopenshell
import bimtester
import bimtester.run
import bimtester.reports
try:
import bimtester
import bimtester.run
import bimtester.reports
except:
print("Failed to load BIMTester. Try disabling other add-ons, in particular Blender-OSM. See bug #1318.")
from pathlib import Path
from itertools import cycle
from blenderbim.bim.ifc import IfcStore
@@ -31,7 +36,7 @@ class ExecuteBIMTester(bpy.types.Operator):
"schema_file": "",
"schema_name": "",
"lang": "en",
"steps": props.steps
"steps": props.steps,
}
use_stored_ifc = props.should_load_from_memory and IfcStore.get_file()
runner = bimtester.run.TestRunner(args["ifc"], ifc=IfcStore.get_file() if use_stored_ifc else None)
@@ -59,9 +64,10 @@ class BIMTesterPurge(bpy.types.Operator):
class SelectFeature(bpy.types.Operator):
bl_idname = "bim.select_feature"
bl_label = "Select Feature"
bl_label = "Select Feature / IDS"
bl_options = {"REGISTER", "UNDO"}
filename_ext = ".feature"
filter_glob: bpy.props.StringProperty(default="*.feature", options={"HIDDEN"})
filter_glob: bpy.props.StringProperty(default="*.feature;*.xml", options={"HIDDEN"})
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def execute(self, context):
@@ -76,6 +82,7 @@ class SelectFeature(bpy.types.Operator):
class SelectSteps(bpy.types.Operator):
bl_idname = "bim.select_steps"
bl_label = "Select Steps"
bl_options = {"REGISTER", "UNDO"}
filename_ext = ".py"
filter_glob: bpy.props.StringProperty(default="*.py", options={"HIDDEN"})
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
@@ -92,6 +99,7 @@ class SelectSteps(bpy.types.Operator):
class SelectBIMTesterIfcFile(bpy.types.Operator):
bl_idname = "bim.select_bimtester_ifc_file"
bl_label = "Select BIMTester IFC File"
bl_options = {"REGISTER", "UNDO"}
filename_ext = ".ifc"
filter_glob: bpy.props.StringProperty(default="*.ifc;*.ifczip;*.ifcxml", options={"HIDDEN"})
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
@@ -162,6 +170,7 @@ class RejectClass(bpy.types.Operator):
class SelectAudited(bpy.types.Operator):
bl_idname = "bim.select_audited"
bl_label = "Select Audited"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
audited_global_ids = []
@@ -1,6 +1,5 @@
import os
from pathlib import Path
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.prop import StrProperty
from blenderbim.bim.module.root.prop import getIfcClasses
from bpy.types import PropertyGroup
@@ -19,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:
@@ -41,7 +47,7 @@ def refreshScenarios(self, context):
class BimTesterProperties(PropertyGroup):
feature: StringProperty(default="", name="Feature File", update=refreshScenarios)
feature: StringProperty(default="", name="Feature / IDS", update=refreshScenarios)
steps: StringProperty(default="", name="Custom Steps")
ifc_file: StringProperty(default="", name="IFC File")
audit_ifc_class: EnumProperty(items=getIfcClasses, name="Audit Class")
@@ -0,0 +1,14 @@
import bpy
from . import ui
classes = (
ui.BIM_PT_boundary,
)
def register():
pass
def unregister():
pass
@@ -0,0 +1,36 @@
import bpy
import blenderbim.bim.helper
from bpy.types import Panel, UIList
from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.boundary.data import Data
class BIM_PT_boundary(Panel):
bl_label = "IFC Space Boundaries"
bl_idname = "BIM_PT_boundary"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "object"
@classmethod
def poll(cls, context):
if not context.active_object:
return False
props = context.active_object.BIMObjectProperties
if not props.ifc_definition_id:
return False
if not IfcStore.get_element(props.ifc_definition_id):
return False
if IfcStore.get_file().by_id(props.ifc_definition_id).is_a() not in ["IfcSpace", "IfcExternalSpatialElement"]:
return False
return True
def draw(self, context):
self.oprops = context.active_object.BIMObjectProperties
if not Data.is_loaded:
Data.load(IfcStore.get_file())
for boundary_id in Data.spaces.get(self.oprops.ifc_definition_id, []):
boundary = Data.boundaries[boundary_id]
row = self.layout.row()
row.label(text=f"{boundary_id}", icon="GHOST_ENABLED")
@@ -42,6 +42,7 @@ class ExportClashSets(bpy.types.Operator):
class ImportClashSets(bpy.types.Operator):
bl_idname = "bim.import_clash_sets"
bl_label = "Import Clash Sets"
bl_options = {"REGISTER", "UNDO"}
filename_ext = ".json"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
@@ -77,6 +78,7 @@ class ImportClashSets(bpy.types.Operator):
class AddClashSet(bpy.types.Operator):
bl_idname = "bim.add_clash_set"
bl_label = "Add Clash Set"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
new = bpy.context.scene.BIMClashProperties.clash_sets.add()
@@ -88,6 +90,7 @@ class AddClashSet(bpy.types.Operator):
class RemoveClashSet(bpy.types.Operator):
bl_idname = "bim.remove_clash_set"
bl_label = "Remove Clash Set"
bl_options = {"REGISTER", "UNDO"}
index: bpy.props.IntProperty()
def execute(self, context):
@@ -98,6 +101,7 @@ class RemoveClashSet(bpy.types.Operator):
class AddClashSource(bpy.types.Operator):
bl_idname = "bim.add_clash_source"
bl_label = "Add Clash Source"
bl_options = {"REGISTER", "UNDO"}
group: bpy.props.StringProperty()
def execute(self, context):
@@ -109,6 +113,7 @@ class AddClashSource(bpy.types.Operator):
class RemoveClashSource(bpy.types.Operator):
bl_idname = "bim.remove_clash_source"
bl_label = "Remove Clash Source"
bl_options = {"REGISTER", "UNDO"}
index: bpy.props.IntProperty()
group: bpy.props.StringProperty()
@@ -121,7 +126,9 @@ class RemoveClashSource(bpy.types.Operator):
class SelectClashSource(bpy.types.Operator):
bl_idname = "bim.select_clash_source"
bl_label = "Select Clash Source"
bl_options = {"REGISTER", "UNDO"}
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
filter_glob: bpy.props.StringProperty(default="*.ifc", options={"HIDDEN"})
index: bpy.props.IntProperty()
group: bpy.props.StringProperty()
@@ -138,6 +145,7 @@ class SelectClashSource(bpy.types.Operator):
class SelectClashResults(bpy.types.Operator):
bl_idname = "bim.select_clash_results"
bl_label = "Select Clash Results"
bl_options = {"REGISTER", "UNDO"}
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def execute(self, context):
@@ -152,6 +160,7 @@ class SelectClashResults(bpy.types.Operator):
class SelectSmartGroupedClashesPath(bpy.types.Operator):
bl_idname = "bim.select_smart_grouped_clashes_path"
bl_label = "Select Smart-Grouped Clashes Path"
bl_options = {"REGISTER", "UNDO"}
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def execute(self, context):
@@ -233,6 +242,7 @@ class ExecuteIfcClash(bpy.types.Operator):
class SelectIfcClashResults(bpy.types.Operator):
bl_idname = "bim.select_ifc_clash_results"
bl_label = "Select IFC Clash Results"
bl_options = {"REGISTER", "UNDO"}
filename_ext = ".json"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
@@ -270,6 +280,7 @@ class SelectIfcClashResults(bpy.types.Operator):
class SmartClashGroup(bpy.types.Operator):
bl_idname = "bim.smart_clash_group"
bl_label = "Smart Group Clashes"
bl_options = {"REGISTER", "UNDO"}
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def execute(self, context):
@@ -322,6 +333,7 @@ class SmartClashGroup(bpy.types.Operator):
class LoadSmartGroupsForActiveClashSet(bpy.types.Operator):
bl_idname = "bim.load_smart_groups_for_active_clash_set"
bl_label = "Load Smart Groups for Active Clash Set"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
smart_groups_path = bpy.path.ensure_ext(bpy.context.scene.BIMClashProperties.smart_grouped_clashes_path, ".json")
@@ -355,6 +367,7 @@ class LoadSmartGroupsForActiveClashSet(bpy.types.Operator):
class SelectSmartGroup(bpy.types.Operator):
bl_idname = "bim.select_smart_group"
bl_label = "Select Smart Group"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
# Select smart group in view
@@ -423,6 +436,7 @@ class BlenderClasher:
class SetBlenderClashSetA(bpy.types.Operator):
bl_idname = "bim.set_blender_clash_set_a"
bl_label = "Set Blender Clash Set A"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
while len(bpy.context.scene.BIMClashProperties.blender_clash_set_a) > 0:
@@ -436,6 +450,7 @@ class SetBlenderClashSetA(bpy.types.Operator):
class SetBlenderClashSetB(bpy.types.Operator):
bl_idname = "bim.set_blender_clash_set_b"
bl_label = "Set Blender Clash Set B"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
while len(bpy.context.scene.BIMClashProperties.blender_clash_set_b) > 0:
@@ -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,
@@ -93,6 +93,7 @@ class BIM_PT_ifcclash(Panel):
class BIM_PT_clash_manager(Panel):
bl_idname = "BIM_PT_clash_manager"
bl_label = "Clash Manager"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "VIEW_3D"
bl_region_type = "UI"
bl_category = "BlenderBIM"
@@ -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
@@ -31,19 +26,26 @@ class LoadClassificationLibrary(bpy.types.Operator):
class AddClassification(bpy.types.Operator):
bl_idname = "bim.add_classification"
bl_label = "Add Classification"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
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"}
class EnableEditingClassification(bpy.types.Operator):
bl_idname = "bim.enable_editing_classification"
bl_label = "Enable Editing Classification"
bl_options = {"REGISTER", "UNDO"}
classification: bpy.props.IntProperty()
def execute(self, context):
@@ -67,6 +69,7 @@ class EnableEditingClassification(bpy.types.Operator):
class DisableEditingClassification(bpy.types.Operator):
bl_idname = "bim.disable_editing_classification"
bl_label = "Disable Editing Classification"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
context.scene.BIMClassificationProperties.active_classification_id = 0
@@ -76,20 +79,32 @@ class DisableEditingClassification(bpy.types.Operator):
class RemoveClassification(bpy.types.Operator):
bl_idname = "bim.remove_classification"
bl_label = "Remove Classification"
bl_options = {"REGISTER", "UNDO"}
classification: bpy.props.IntProperty()
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
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"}
class EditClassification(bpy.types.Operator):
bl_idname = "bim.edit_classification"
bl_label = "Edit Classification"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context):
props = context.scene.BIMClassificationProperties
attributes = {}
for attribute in props.classification_attributes:
@@ -100,10 +115,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"}
@@ -111,6 +128,7 @@ class EditClassification(bpy.types.Operator):
class EnableEditingClassificationReference(bpy.types.Operator):
bl_idname = "bim.enable_editing_classification_reference"
bl_label = "Enable Editing Classification Reference"
bl_options = {"REGISTER", "UNDO"}
reference: bpy.props.IntProperty()
obj: bpy.props.StringProperty()
@@ -135,6 +153,7 @@ class EnableEditingClassificationReference(bpy.types.Operator):
class DisableEditingClassificationReference(bpy.types.Operator):
bl_idname = "bim.disable_editing_classification_reference"
bl_label = "Disable Editing Classification Reference"
bl_options = {"REGISTER", "UNDO"}
obj: bpy.props.StringProperty()
def execute(self, context):
@@ -146,30 +165,39 @@ class DisableEditingClassificationReference(bpy.types.Operator):
class RemoveClassificationReference(bpy.types.Operator):
bl_idname = "bim.remove_classification_reference"
bl_label = "Remove Classification Reference"
bl_options = {"REGISTER", "UNDO"}
reference: bpy.props.IntProperty()
obj: bpy.props.StringProperty()
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
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"}
class EditClassificationReference(bpy.types.Operator):
bl_idname = "bim.edit_classification_reference"
bl_label = "Edit Classification Reference"
bl_options = {"REGISTER", "UNDO"}
obj: bpy.props.StringProperty()
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context):
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
props = obj.BIMClassificationReferenceProperties
attributes = {}
@@ -179,10 +207,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"}
@@ -190,10 +220,14 @@ class EditClassificationReference(bpy.types.Operator):
class AddClassificationReference(bpy.types.Operator):
bl_idname = "bim.add_classification_reference"
bl_label = "Add Classification Reference"
bl_options = {"REGISTER", "UNDO"}
reference: bpy.props.IntProperty()
obj: bpy.props.StringProperty()
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context):
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
self.file = IfcStore.get_file()
@@ -206,22 +240,24 @@ 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"}
class ChangeClassificationLevel(bpy.types.Operator):
bl_idname = "bim.change_classification_level"
bl_label = "Change Classification Level"
bl_options = {"REGISTER", "UNDO"}
parent_id: bpy.props.IntProperty()
def execute(self, context):
@@ -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
@@ -70,6 +70,8 @@ class BIM_PT_classification_references(Panel):
@classmethod
def poll(cls, context):
if not IfcStore.get_element(context.active_object.BIMObjectProperties.ifc_definition_id):
return False
return bool(context.active_object.BIMObjectProperties.ifc_definition_id)
def draw(self, context):
@@ -79,9 +81,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()
@@ -11,6 +11,7 @@ from blenderbim.bim.ifc import IfcStore
class SelectCobieIfcFile(bpy.types.Operator):
bl_idname = "bim.select_cobie_ifc_file"
bl_label = "Select COBie IFC File"
bl_options = {"REGISTER", "UNDO"}
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def execute(self, context):
@@ -25,6 +26,7 @@ class SelectCobieIfcFile(bpy.types.Operator):
class SelectCobieJsonFile(bpy.types.Operator):
bl_idname = "bim.select_cobie_json_file"
bl_label = "Select COBie JSON File"
bl_options = {"REGISTER", "UNDO"}
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def execute(self, context):

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