Compare commits

...

180 Commits

Author SHA1 Message Date
Dion Moult 240884e646 No need to specify SI unit names as they can be derived from the unit type 2023-01-05 17:16:46 +11:00
Dion Moult 3567dc8ae5 Merge branch 'mdjska-v0.7.0' into v0.7.0 2023-01-05 16:59:59 +11:00
Sigma Dimensions 54f324436f BBIM fix: prevent "guess_all_quantities" operator from showing up on Property Sets 2023-01-05 16:59:48 +11:00
Dion Moult d5b31bc4f4 Write docs for void API module 2023-01-05 16:59:48 +11:00
Dion Moult 844e6377f4 Write docs for unit API module 2023-01-05 16:59:48 +11:00
Dion Moult 71076d313e Write docs for type API module 2023-01-05 16:59:48 +11:00
Dion Moult 3bc2985b6b Write docs for system API module 2023-01-05 16:59:48 +11:00
Dion Moult e28b837cd6 Write docs for style API module 2023-01-05 16:59:48 +11:00
Dion Moult e22904b41b Documentation example fixes where I forgot to give things with representations a placement 2023-01-05 16:59:48 +11:00
Sigma Dimensions af1949678a Various 4D improvements:
- refactor work schedule visualisation operator
- Fix text handler
- Improve durations UI for task times (proposal to replicate for all durations)
- Simplify durations ( P32H as P4D or P1D8H)
- Implement years/months in various datetime calculations
- Feature to customise animation colors per construction task type
- Feature to add Visual task bars
- Feature to customise Visual task bar material colors
- Move helper functions from blenderbim.bim.module.sequence to ifcopenshell.util.sequence
2023-01-05 16:59:48 +11:00
Dion Moult 83e483555f Write documentation for structural API module 2023-01-05 16:59:48 +11:00
Dion Moult 4442c2a604 Write documentation for spatial API module 2023-01-05 16:59:48 +11:00
Dion Moult 9da91f538b Write documentation for sequence API module 2023-01-05 16:59:48 +11:00
Dion Moult 5ad8ad836a Add new code example for creating a work schedule 2023-01-05 16:59:48 +11:00
Massimo Fabbro 2723f71bfe Finish mapping quantities (see #2533) (#2661)
* Finish Qto_SignBaseQuantities

* Finish Qto_RailBaseQuantities

* Finish Qto_PitorialSignQuantities

* Finish Qto_JunctionBoxBaseQuantities

* Finish Qto_ArealStratumBaseQuantities

* Finish Qto_SiteBaseQuantities

* Finish Qto_RoofBaseQuantities

* Finish Qto_RoofBaseQuantities

* Finish Qto_ChimneyBaseQuantities

* Finish Qto_EarthworksCutBaseQuantities

* Finish Qto_KerbBaseQuantities

* Finish Qto_PavementBaseQuantities

* Finish Qto_RampFlightBaseQuantities

* Finish Qto_SurfaceFeatureBaseQuantities

* Finish Qto_StairFlightBaseQuantities

* Finish Qto_ConstructionMaterialResourceBaseQuantities

* Finish Qto_DistributionChamberElementBaseQuantities

* Finish Qto_CourseBaseQuantities

* Finish Qto_RailingBaseQuantities

* Finish Qto_SleeperBaseQuantities

* Finish Qto_TankSurfaceQuantities

* Finish Qto_BuildingElementProxy

* Finish Qto_MemberBaseQuantities

* Finish Qto_BodyGeometryValidation

* Finish Qto_VolumetricStratumBaseQuantities

* Finish Qto_SpatialZoneBaseQuantities

* Finish Qto_ReinforcingElementBaseQuantities

* Finish update calculate all quantities button with new calculating functions
2023-01-05 16:59:48 +11:00
Thomas Krijnen d298d13713 Fix build-all collada compile on gcc 9.4.0 2023-01-05 16:59:48 +11:00
Sigma Dimensions 7f555b9d07 code clean up for sequence module 2023-01-05 16:59:48 +11:00
Sigma Dimensions 8004a45b54 features to guess schedule date range and setup default task columns, and code clean up 2023-01-05 16:59:48 +11:00
Sigma Dimensions 3fd1209906 Fix: When counting working days, consider if calendar is applicable 2023-01-05 16:59:48 +11:00
Sigma Dimensions 65c6876a23 2 Fixes for 1 commit:
- Consider all tasks descendants when deriving task earliest or latest dates
2023-01-05 16:59:48 +11:00
Sigma Dimensions 72fbfa382f BBIM: feature to display total task Assignements 2023-01-05 16:59:48 +11:00
Thomas Krijnen 50f3178d97 Feature: express rules (#2662)
* temp reasonable state, missing rule_head contents

* better state, with init() and rule_id, but some missing parameters

* performance; embed root in dict; fix lookup

* Add ast_utils

* Fixes related to listnode and typo

* First steps with code gen

* Finalize first steps towards working code generation

* Backwards compat

* Rule execution, error formatting, test cases

* Where rule: support indexing, more expressions, test plane angle measure

* positive length measure test cases

* Venture into entity rules, test actor role

* Fixed for not unary op and address test cases

* Test group qualifier and air terminal type

* typeof() implementation and test annotation curve occ

* Flexible handling of expression, type set intersection, IfcAnnotationSurface test cases

* Functions, derived attrs, fix branch order, allow to terminate branches, apply -1 to index

* Implement express query(), test cases for 2x3 bspline curve

* Function args, if-else, repeat, entity instance construction, cshape test cases

* Multiple function call arguments and other small but influential changes. IfcDotProject() almost working except for variable case

* Make lowercase and fix assignment to qualified lhs

* Entity instance schema without file, aggregate assignment workaround, range fix, lowercase instance locals, extruded area direction test cases

* Enumeration item handling, query() robustness, arbitrary profile test cases

* Empty aggregate_initializer, xor and instance equality, nested expression fix in IfcCrossProduct, axis2-3d test cases

* Numeric stable sort with indices greater then 10, get ast node parent, redeclared derived support, nested expression robustness, define entity functions, case statements, xor, mod, bool literals, nvl, unknown, conversion based unit test cases

* Safeguard for schema name case norm, case norm query variable_id

* free instance comparison, supertype serialization fix, include inherited attributes in locals, proper elif-else in case stmt, loindex, shape rep test cases

* Union operator on set, retain general_aggr_types in parse tree, test cases for property set

* Escape stmt, rule locals and statements

* Return calculated values for redeclared derived attributes

* fix for rules without stmt, add derived and inverse attributes to locals, filter unused locals, wrap exists() arg with lambda to catch indexerror, support repeated aggr init element, typeof() none check, enum namespace uppercase

* Regen 2x3 rules

* Rule test filter only on basename

* Update fixtures for compliance with full body of rules

* Add rule support to ifcopenshell.validate

* Implement usedin() function, test cases for IfcWallSC MLS

* blength

* Run code generation on all schemas

* Try to eliminate runtime pyparsing dep

* Try to eliminate runtime pyparsing dep
2023-01-05 16:59:48 +11:00
Sigma Dimensions 6546b10838 Display Scheduled Resource Usage as a property within rersource tree 2023-01-05 16:59:48 +11:00
Sigma Dimensions 155badc85e Enable displaying number of resources used by a task 2023-01-05 16:59:48 +11:00
Sigma Dimensions d7e2587b1f Task Output selection improvements - allow recursive nesting and add checkbox to toggle between nested and non-nested 2023-01-05 16:59:48 +11:00
Thomas Krijnen d9bbf16e5e Update ci-py-only.yml 2023-01-05 16:59:48 +11:00
Thomas Krijnen 676187b6fc Update ci.yml 2023-01-05 16:59:48 +11:00
Andrej730 800eb67afb Added mention of sverchok's copyrights 2023-01-05 16:59:48 +11:00
Andrej730 46a1b4775c Added base for ifc sverchok modifier
It's located in "Modifiers" section of the properties (the same way as IFC Array or IFC Stair).
It requires Sverchok addon to be enabled (it's lazy importing it when it's needed, so there is no new constant dependency).

What you can now do with it:
1) Add sverchok graphs to this modifier and store simple data from sverchok in IFC ("Update data from sverchok). The "output" node of the sverchok graph has to be type of "Viewer Draw" and it's name should start with "ifcoutput" (no case-sensitive).
2) Import/export json of sverchok graphs.
2023-01-05 16:59:48 +11:00
Andrej730 f80cc7c7f5 Removed deprecated angle annotations operator
As it's currently completely replace with new more intuitive operator.
2023-01-05 16:59:48 +11:00
Andrej730 a7cbc628a8 Fixed annotations angle operator svg rendering bug
Fixed bug where the angle was drawn in svg only for the first two edges of the curve.
Now it takes into account all edges - first angle = edge0 + edge1, second angle = edge1 + edge2 etc.
2023-01-05 16:59:48 +11:00
CyrilWaechter 3ab02a3931 Allow to write to unexisting directory
`ifcopenshell.write("/unexisting/path/model.ifc")` was failing without
throwing an error. It now works and a test ensure it works.
2023-01-05 16:59:48 +11:00
Sigma Dimensions 31b095ff68 fix library import for date utils 2023-01-05 16:59:48 +11:00
Sigma Dimensions b09e19dc8a Proposal to use parent productivity to calculate resource work. 2023-01-05 16:59:48 +11:00
Sigma Dimensions 97327dbc13 BBIM: forgotten commit for "ScheduleDuration" formatting 2023-01-05 16:59:48 +11:00
Sigma Dimensions 9ad0fca10e fix typo for resource_work calculation 2023-01-05 16:59:48 +11:00
Sigma Dimensions bea095afd0 fix offset date calculation when finish date lies outside of calendar range 2023-01-05 16:59:48 +11:00
Dion Moult d34e45fad5 Add support for loading IFCs with aggregated types 2023-01-05 16:59:48 +11:00
Dion Moult efed95615e Write documentation for root API module 2023-01-05 16:59:48 +11:00
Dion Moult 9bb4b0328a Write documentation for resource API module 2023-01-05 16:59:48 +11:00
Dion Moult 5f9d1a5e63 Write documentation for pset_template API module 2023-01-05 16:59:48 +11:00
Dion Moult f6ba95935a Add documentation for pset API module 2023-01-05 16:59:48 +11:00
Dion Moult d9d58af049 Consolidate standard "clean environment" instructions in docs 2023-01-05 16:59:48 +11:00
Dion Moult aed3bdf72a Write docs for project API module 2023-01-05 16:59:48 +11:00
Dion Moult 968f56a189 Revert detailed SPF comments in code example
Sorry I find these really hard to read an intimidating for new users.
2023-01-05 16:59:48 +11:00
Dion Moult 90d92e70b2 Write docs for profile API module 2023-01-05 16:59:48 +11:00
Dion Moult efacef1789 Write docs for owner API module 2023-01-05 16:59:48 +11:00
Ryan Schultz a590cf8330 Added some troubleshooting measures to determine if another plugin is interfering with BB. 2023-01-05 16:59:48 +11:00
fbpyr 05a7c66843 bump python version
bumping python version, so it works with our ubuntu latest image and `actions/setup-python@v2`
2023-01-05 16:59:48 +11:00
CyrilWaechter c4a4752a7e Ignore mypy cache, swig and compiled files 2023-01-05 16:59:48 +11:00
CyrilWaechter 50f1c52d24 Remove bloat line 2023-01-05 16:59:48 +11:00
CyrilWaechter a5e419cdc3 Remove duplicate line 2023-01-05 16:59:48 +11:00
CyrilWaechter d22557c8a0 Move boundary origin to space origin
Ensure that boundary origin is same as space origin to assign correct
geometry into IFC.
2023-01-05 16:59:48 +11:00
Ryan Schultz c6e285cef8 where to look to see if a dependency has changed. 2023-01-05 16:59:48 +11:00
CyrilWaechter f81fc4e748 Remove unecessary line which make build failing 2023-01-05 16:59:48 +11:00
Dion Moult 28d9e435d1 Write documentation for the nest API module. 2023-01-05 16:59:48 +11:00
Dion Moult fe5e35bae4 Add API documentation for the material module 2023-01-05 16:59:48 +11:00
Dion Moult 1dc5b8cd33 Add docs for library API module 2023-01-05 16:59:48 +11:00
Dion Moult 8e04847310 Add docs to layer API 2023-01-05 16:59:48 +11:00
Dion Moult b37d8d3e7f Add API documentation for group module 2023-01-05 16:59:48 +11:00
Dion Moult 7d08360808 Add documentation for grid API module 2023-01-05 16:59:48 +11:00
Ryan Schultz 5ac0e92250 Added more examples to the Code Examples page 2023-01-05 16:59:48 +11:00
Andrej730 61d42a914c Added ReloadSelectedIfcFile operator
Added ReloadSelectedIfcFile operator and added it to IFC Quality Control -> IFC Debug, it's useful when you're debugging and doing a lot of iterations on the same file.
2023-01-05 16:59:48 +11:00
Massimo Fabbro 5d516154b1 Mapping quantities (see #2533) (#2627)
* Finish mapping Qto_PileBaseQuantities

* Finish mapping IfcPipeSegmentBaseQuantities

* Finish mapping IfcVehicleBaseQuantities

* Finish mapping Qto_OpeningElementBaseQuantities

* Finish mapping Qto_PlateBaseQuantities

* Finish Qto_MarineFacilityBaseQuantities

* Finish Qto_FacilityPartBaseQuantities

* Finish Qto_ProjectionElementBaseQuantities

* Better syntax
2023-01-05 16:59:48 +11:00
Thomas Krijnen 59f74eaf8e #2624 2023-01-05 16:59:48 +11:00
Dion Moult 1313cb34bb Add API docs for georeferencing module 2023-01-05 16:59:48 +11:00
Massimo Fabbro 21edf462f3 Finish mapping quantities Qto_SpaceBaseQuantities 2023-01-05 16:59:48 +11:00
CyrilWaechter 69c854f0f4 Allow ifcpatch to work with .ifcZIP | .ifcXML
Closes #2350
2023-01-05 16:59:47 +11:00
CyrilWaechter 78f03c8544 Allow to write .ifcZIP | .ifcXML #2350
Transparently write any .ifc | .ifcXML | .ifcZIP formatted file
 with any extension :
- by guessing format from its extension
(eg. .zip | .ifczip | .ifcZIP | .ifcXML | .ifcxml | .IFC | .ifc)
zipped file can file can be of any supported format
- by specifying a format
- by specifying if it should be zipped
2023-01-05 16:59:47 +11:00
Ryan Schultz 6ba56fdf35 misspellings with outer_boundary 2023-01-05 16:59:47 +11:00
CyrilWaechter 386205cde7 Fix IfcSystem assignment bug introduced in f0b1935
Fixing my own bug…
Note : always run all test before pushing not only the one which used to fail…
2023-01-05 16:59:47 +11:00
CyrilWaechter ba4f8886e9 Add system assignment check
@aothms Is there a way to check against IFC formal propositions ?
Example for [IfcZone](http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcZone.htm#5.4.3.82.4-Formal-propositions)
@Moult Should I move is_assignable to an util ?
2023-01-05 16:59:47 +11:00
CyrilWaechter d55dff8a77 Add IfcSpatialElementType to assignable classes
Required for IFC4+ to assign IfcSpaceType and IfcSpatialZoneType
Solves #2615
2023-01-05 16:59:47 +11:00
Thomas Krijnen 57f21d1e74 IfcOpenShell WASM module build script (#2617) 2023-01-05 16:59:47 +11:00
Thomas Krijnen b99e048aed Moar schema indices 2023-01-05 16:59:47 +11:00
Andrej730 3bf1b9f489 Added new angle annotations operator
New operator should be more intuitive to work with. I kept the old one too for now.
2023-01-05 16:59:47 +11:00
Thomas Krijnen 28261491d0 Test cases for #2586 2023-01-05 16:59:47 +11:00
Thomas Krijnen f10646ad35 Distinguish between nil and derived in validate.py 2023-01-05 16:59:47 +11:00
Thomas Krijnen d571f0871c https://raw.githubusercontent.com/buildingSMART/IFC4.3-html/sep-13-release/IFC/RELEASE/IFC4x3/HTML/IFC4X3_TC1.exp 2023-01-05 16:59:47 +11:00
Dion Moult 267bb65b5e Add API docs for drawing module 2023-01-05 16:59:47 +11:00
Dion Moult 0968687bc3 Add API documentation for document module 2023-01-05 16:59:47 +11:00
CyrilWaechter 71f1236252 Fix path for ci (root at src/ifcopenshell-python) 2023-01-05 16:59:47 +11:00
CyrilWaechter b76933b95f Update test/input submodule to https://github.com/IfcOpenShell/files/commit/20a17fcf3b1be2bc48af0273f7604af5b7cc4068
#2350
2023-01-05 16:59:47 +11:00
CyrilWaechter edee74cb31 Fix type hints #2350 for python <3.10 2023-01-05 16:59:47 +11:00
CyrilWaechter 2007047ae2 Add test suite for ifcopenshell.open #2350
Fix open function: format argument was unused
2023-01-05 16:59:47 +11:00
Thomas Krijnen 8d16ca5d88 Missing parse updates after 4e0a19e 2023-01-05 16:59:47 +11:00
Dion Moult d10325f69d Add documentation for cost module 2023-01-05 16:59:47 +11:00
CyrilWaechter ea56506683 Add stable AUR package 2023-01-05 16:59:47 +11:00
CyrilWaechter 1ed379e466 Allow to open .ifcZIP | .ifcXML #2350
Transparently open any .ifc | .ifcXML | .ifcZIP formatted file
 with any extension :
- by guessing format from its extension
(eg. .zip | .ifczip | .ifcZIP | .ifcXML | .ifcxml | .IFC | .ifc)
zipped file can file can be of any supported format
-  by specifying a format
2023-01-05 16:59:47 +11:00
CyrilWaechter fcb6971d4d IfcZone: Allow to remove an object from an IfcZone 2023-01-05 16:59:47 +11:00
CyrilWaechter 10f56a7ae2 IfcZone : Allow basic selection/deletion/creation 2023-01-05 16:59:47 +11:00
Thomas Krijnen 29bbe814a0 https://raw.githubusercontent.com/buildingSMART/IFC4.3-html/v4.3.1.0/IFC/RELEASE/IFC4x3/HTML/IFC4X3_ADD1.exp 2023-01-05 16:59:47 +11:00
Thomas Krijnen b161681ffa Fix test case 2023-01-05 16:59:47 +11:00
Massimo Fabbro c71b9c1915 Finish mapping Qto_CoveringBaseQuantities 2023-01-05 16:59:47 +11:00
Massimo Fabbro 02eb846dea Finish mapping Qto_DoorBaseQuantities and Qto_WindowBaseQuantities 2023-01-05 16:59:47 +11:00
Massimo Fabbro 899995cb51 Minor fix 2023-01-05 16:59:47 +11:00
Massimo Fabbro 85dde1033a Fix get_gross_side_area for Qto_WallBaseQuantities 2023-01-05 16:59:47 +11:00
Vukas Pajic fffb5ab6a3 #2602 get_properties no longer throws error when EnumerationValues = None 2023-01-05 16:59:47 +11:00
Kristoffer Andersen d6829d04cb bump conda daily build number 2023-01-05 16:59:47 +11:00
Ryan Schultz efaa79a841 Added diffuse_color inside IFC Style panel 2023-01-05 16:59:47 +11:00
Andrej730 201e290897 documented create_simple_2dcurve() 2023-01-05 16:59:47 +11:00
Dion Moult f43e6069ec Write API documentation for control module 2023-01-05 16:59:47 +11:00
Dion Moult c6fc048fc3 Add API documentation for context module 2023-01-05 16:59:47 +11:00
Dion Moult 8bb940c851 Add API docs for constraint module 2023-01-05 16:59:47 +11:00
Andrej730 c371bf32e7 Added c-, z-profiles with lips to AU steel library
Added c- and z-profiles with lips to IFC AU Steel profile library (based on data from https://github.com/boltsparts/boltsparts/pull/15).

For z-profiles I've created simple 2D curve generation function that can be reused later - it requires list of points and list of points with fillets and the results are list of curve points, segments and IfcIndexedPolyCurve.

Example:

    points, segments, ifc_curve = create_simple_curve_from_coords(coords,
        fillets =     (0,   1,   4, 5, 6,   7,   10, 11),
        fillet_radius=(r+t, r+t, r, r, r+t, r+t, r, r),
        closed=True, ifc_file=ifc_file)
2023-01-05 16:59:47 +11:00
Dion Moult 522d3a8367 Add API documentation for classification module 2023-01-05 16:59:47 +11:00
Dion Moult 37954e35e7 Add API docs for boundary module 2023-01-05 16:59:47 +11:00
Thomas Krijnen 49d5264ed2 Missing file 2023-01-05 16:59:47 +11:00
Thomas Krijnen 1d53a92f8a Refactoring 2023-01-05 16:59:47 +11:00
Dion Moult 2f39490fda Add docs for attribute API 2023-01-05 16:59:47 +11:00
Dion Moult f322ab6c51 Add docs for aggregate api 2023-01-05 16:59:47 +11:00
Massimo Fabbro f93d4ff92f Cleaning around in qto_calculator 2023-01-05 16:59:47 +11:00
Thomas Krijnen 412e850167 Refactoring 2023-01-05 16:59:47 +11:00
Thomas Krijnen 64021720ca Refactoring 2023-01-05 16:59:47 +11:00
Dion Moult 3a8df68710 Minor fix 2023-01-05 16:59:47 +11:00
Dion Moult e9a4b06f2b Minor documentation tweaks 2023-01-05 16:59:47 +11:00
Thomas Krijnen bf8771c586 draw.include_projection setting 2023-01-05 16:59:47 +11:00
Dion Moult 4cca5a64b0 Minor fixes to IfcTester 2023-01-05 16:59:47 +11:00
Erik Abrahamsson fb2788cf7a Fix translation of days in Powerproject importer 2023-01-05 16:59:47 +11:00
Andrej730 c129f4f7d8 IFC Stair - added "Wood / Steel" stair type
Added new stair type "Wood / Steel" to IFC Stair modifier. It's less sophisticated than "Concrete" stair type - it acts more like a "Dumb stair" operator we have.
2023-01-05 16:59:47 +11:00
Andrej730 14e954545e IFC Stair - cosmetic adjustments 2023-01-05 16:59:47 +11:00
Massimo Fabbro f87b4a8072 Fix some errors and update notes 2023-01-05 16:59:47 +11:00
Massimo Fabbro d8b0b097e9 Finish mapping Qto_BeamBaseQuantities 2023-01-05 16:59:47 +11:00
Massimo Fabbro 6345daa708 Fix some get_lateral_area function errors 2023-01-05 16:59:47 +11:00
Massimo Fabbro 341eb4dda9 Finish mapping Qto_BeamBaseQuantities 2023-01-05 16:59:47 +11:00
Dion Moult daf1cadfdb Fix astapowerproject import to ifc which seemed mostly broken for some reason 2023-01-05 16:59:47 +11:00
Sigma Dimensions 5f0aa27996 BBIM feature to load nested tasks outputs, and select all nested task outputs 2023-01-05 16:59:47 +11:00
Otso Alho 9cf946f51d catch missing instances one by one. do not throw away whole relation. 2023-01-05 16:59:47 +11:00
Dion Moult bbc79e7f21 The profile manager now only manages named profiles 2023-01-05 16:59:47 +11:00
Andrej730 112b02c8bb Added "IFC Stair" to object modifiers
1) Added "IFC Stair" to object modifiers - it works in similar way as "IFC Array". It creates a stair from current object (if it's IfcStairFlight) and writes it's parameters to property set "BBIM_Stair" and also to IfcStairFlight parameters.
Then you can edit it - modifying stair parameters and immediately see the changes, then you can save those changes or discard them.
2) "Clever Stair" operator (in Shift+A menu) works a bit different now - it creates new IfcStairFlight with IFC Stair modifier activated.
2023-01-05 16:59:47 +11:00
Dion Moult 74db8db4dd Get cross section area from profile directly rather than mesh parsing 2023-01-05 16:59:47 +11:00
Dion Moult cce0ca600b Distinguish between calculating gross and net surface area 2023-01-05 16:59:47 +11:00
Thomas Krijnen 8432553fda Be a bit more careful in throwing NoSuchObject in case of triangulated face handling https://github.com/IfcOpenShell/wasm-preview/issues/1 2023-01-05 16:59:47 +11:00
Dion Moult 5e80db500d Elementary fixes to the BCF module just so that things load (but lots are still broken) 2023-01-05 16:59:47 +11:00
Thomas Krijnen 33b5b350ef Update build-all.py 2023-01-05 16:59:47 +11:00
Thomas Krijnen 5262a9fec5 Update build-all.py 2023-01-05 16:59:47 +11:00
Dion Moult 20d8b665d4 Fix issue where sometimes world coordinate system would trigger the georeferencing distance threshold 2023-01-05 16:59:47 +11:00
Andrea Ghensi 9838309fe3 [BCF] New BCF XML handler with better performances (#2355)
BREAKING CHANGE: incompatible methods and objects with previous version.

closes #2321
2023-01-05 16:59:46 +11:00
Massimo Fabbro ada8e1e923 Finish Qto_SlabBaseQuantities quantities (#2567) 2023-01-05 16:59:46 +11:00
Dion Moult 57917f89e5 Document how you can get the matrix of elements from IfcOpenShell Python. 2023-01-05 16:59:46 +11:00
Dion Moult be028fe075 Fix some bugs in IfcTester that prevented tests running on IFC2X3 2023-01-05 16:59:46 +11:00
Dion Moult 6ce8ad70d6 Fix #2577. Bug where system editing UI appears multiple times. 2023-01-05 16:59:46 +11:00
Andrej730 ca35d75b0d Updated EU, AU steel profiles libraries
1) Updated AU profiles based on more correct data (https://github.com/boltsparts/boltsparts/pull/4) - also added welded i-beam profiles
2) Added inner and outer fillet radius to hollow profiles since this data was added to https://github.com/boltsparts/boltsparts
3) Added more data to ifc profiles that was available on bolts: FilletRadius for I-beams, C-beams; FilletRadius and EdgeRadius for I-beams. Sovles #2556
4) Set names IfcProfileDef names too. Solves #2556
2023-01-05 16:59:46 +11:00
Martyna Jakubowska fbb3f9e83d Resolved Code Review 2023-01-03 16:59:31 +01:00
Martyna Jakubowska 6bda68e834 Update README.md 2022-11-18 15:08:48 +01:00
Martyna Jakubowska 07e4e0c175 Update README.md 2022-11-18 14:49:05 +01:00
Martyna Jakubowska 29bb5c666d Update README.md 2022-11-14 01:13:02 +01:00
martinaCodes bab281235f Added example model and a corresponding description 2022-11-14 01:06:40 +01:00
martinaCodes 3966f0ad29 Bug fixes in create_entity 2022-11-13 23:05:49 +01:00
martinaCodes cc69dfc136 Bug fixes and changed behaviour of create_entity, so it creates an entity pr. each representation 2022-11-13 15:56:33 +01:00
martinaCodes 7ea8c24b68 - 2022-11-11 12:58:35 +01:00
martinaCodes bc548c785d Updated by_query and removed print statements 2022-11-11 12:52:06 +01:00
Martyna Jakubowska b7ff2347f1 Updated README 2022-11-11 05:08:56 +01:00
martinaCodes b7bdddf640 Updated following nodes to work with the current workflow: add_pset, by_guid, get_attribute, get_property, read_entity. Added tooltips and other minor fixes 2022-11-11 02:50:30 +01:00
martinaCodes cc567534f1 Merge branch 'v0.7.0' of github.com:mdjska/IfcOpenShell into v0.7.0
merge changes
2022-11-09 01:55:05 +01:00
martinaCodes fc29eec6e9 Important rework of handling multiple/nested items in bmesh_to_ifc, create_entity and sverchok_to_ifc nodes. Formatting and other minor fixes. 2022-11-09 01:49:01 +01:00
Martyna Jakubowska 2b4c09dac0 Create README.md 2022-11-07 19:39:38 +01:00
martinaCodes 240c98dbd7 Merge branch 'ifcsverchok_dev' of github.com:mdjska/IfcOpenShell into v0.7.0
Merging gsoc changes
2022-11-07 18:28:33 +01:00
martinaCodes d88adc2333 cleaned print statements etc 2022-11-07 18:08:27 +01:00
martinaCodes 9bf6cb953f write_file added logic that ensures proper model hierarchy 2022-11-07 17:40:18 +01:00
martinaCodes 3361895e1d bmesh_to_ifc now creates separate representations for each distinct bmesh geometry 2022-11-07 17:21:09 +01:00
martinaCodes 9f855c3a83 major changes primarily to file creation/saving logic.
Bug fix in create entity.
Other..
this commit is way to big and unstructured.
2022-11-07 15:45:28 +01:00
martinaCodes 13a97babd8 new pick_ifc_class node + minor fixes. Add_spatial_element is WIP 2022-10-31 18:50:17 +01:00
martinaCodes c72031fa5d added blender panel for saving file + minor fix in create_entity 2022-10-31 00:25:45 +01:00
martinaCodes a592736b22 Add_spatial_element node updated 2022-10-24 14:06:23 +02:00
martinaCodes d460a890a7 Add_spacial_element node updated to work with multiple elements. V0.1 - still a bit buggy. 2022-10-24 01:45:00 +02:00
martinaCodes fb18bd4118 added entity locations 2022-10-24 00:08:59 +02:00
martinaCodes 567de9a3ba write_file node updated 2022-10-23 21:51:26 +02:00
martinaCodes 952a0ac2a4 create_shape node updated + minor changes 2022-10-23 20:25:36 +02:00
martinaCodes cbe3fd42a7 sverchok_to_ifc node added + works for multiple geometries. Parent contexts also get deleted if not used. 2022-10-23 16:18:42 +02:00
Martyna Jakubowska 548f1531f1 Merge branch 'IfcOpenShell:v0.7.0' into ifcsverchok_dev 2022-10-21 17:19:49 +02:00
martinaCodes 720de2ffe5 updated by_type to current workflow + added sv_to_ifc 2022-10-21 17:16:24 +02:00
martinaCodes 93b92dca9d changes
1) pasing ID references
2) bugs in create_entities, where number of representations couldn't be updated
3) by_id node updated to current workflow
... other small fixes
2022-10-21 00:11:45 +02:00
martinaCodes 310cbebba9 fixed create_entity and minor changes to bmesh_to_ifc 2022-10-10 09:01:00 +02:00
martinaCodes 532502a4a3 new nodes, and changes to create_entity2, but not working yet 2022-09-09 09:01:01 +02:00
martinaCodes 95b4867432 created new singleton class SvIfcStore, created new 'create_entity2' node 2022-09-08 01:18:20 +02:00
martinaCodes 7c2162f054 fixed api_WIP, minor cleanup in other nodes 2022-09-04 23:36:30 +02:00
martinaCodes 8b8024ba7e fixed a typo-bug 2022-08-25 22:45:52 +02:00
martinaCodes dd2f4586c7 added new nodes to node index 2022-08-25 22:18:53 +02:00
martinaCodes acee90fa89 Added new nodes: api_WIP, bmesh_to_ifc, create_project and quick_project_setup
Modified nodes: api, create_entity
2022-08-25 22:01:35 +02:00
48 changed files with 25935 additions and 278 deletions
+4 -4
View File
@@ -24,9 +24,9 @@ def assign_scene_units(ifc, unit):
if unit.is_scene_unit_metric():
prefix = unit.get_scene_unit_si_prefix()
lengthunit = ifc.run("unit.add_si_unit", unit_type="LENGTHUNIT", name=length_name, prefix=prefix)
areaunit = ifc.run("unit.add_si_unit", unit_type="AREAUNIT", name=area_name, prefix=prefix)
volumeunit = ifc.run("unit.add_si_unit", unit_type="VOLUMEUNIT", name=volume_name, prefix=prefix)
lengthunit = ifc.run("unit.add_si_unit", unit_type="LENGTHUNIT", prefix=prefix)
areaunit = ifc.run("unit.add_si_unit", unit_type="AREAUNIT", prefix=prefix)
volumeunit = ifc.run("unit.add_si_unit", unit_type="VOLUMEUNIT", prefix=prefix)
else:
lengthunit = ifc.run("unit.add_conversion_based_unit", name=length_name)
areaunit = ifc.run("unit.add_conversion_based_unit", name=area_name)
@@ -68,7 +68,7 @@ def add_monetary_unit(ifc, unit):
def add_si_unit(ifc, unit, unit_type=None):
result = ifc.run("unit.add_si_unit", unit_type=unit_type, name=unit.get_si_name_from_unit_type(unit_type))
result = ifc.run("unit.add_si_unit", unit_type=unit_type)
unit.import_units()
return result
-4
View File
@@ -83,10 +83,6 @@ class Unit(blenderbim.core.tool.Unit):
return
return props.length_unit.replace("METERS", "")
@classmethod
def get_si_name_from_unit_type(cls, unit_type):
return ifcopenshell.util.unit.si_type_names.get(unit_type, None)
@classmethod
def import_unit_attributes(cls, unit):
def callback(name, prop, data):
+4 -7
View File
@@ -26,17 +26,15 @@ class TestAssignSceneUnits:
unit.get_scene_unit_name("length").should_be_called().will_return("name")
unit.get_scene_unit_si_prefix().should_be_called().will_return("prefix")
ifc.run(
"unit.add_si_unit", unit_type="LENGTHUNIT", name="name", prefix="prefix"
"unit.add_si_unit", unit_type="LENGTHUNIT", prefix="prefix"
).should_be_called().will_return("lengthunit")
unit.get_scene_unit_name("area").should_be_called().will_return("name")
ifc.run("unit.add_si_unit", unit_type="AREAUNIT", name="name", prefix="prefix").should_be_called().will_return(
"areaunit"
)
ifc.run("unit.add_si_unit", unit_type="AREAUNIT", prefix="prefix").should_be_called().will_return("areaunit")
unit.get_scene_unit_name("volume").should_be_called().will_return("name")
ifc.run(
"unit.add_si_unit", unit_type="VOLUMEUNIT", name="name", prefix="prefix"
"unit.add_si_unit", unit_type="VOLUMEUNIT", prefix="prefix"
).should_be_called().will_return("volumeunit")
ifc.run("unit.add_conversion_based_unit", name="degree").should_be_called().will_return("planeangleunit")
@@ -104,8 +102,7 @@ class TestAddMonetaryUnit:
class TestAddSIUnit:
def test_run(self, ifc, unit):
unit.get_si_name_from_unit_type("unit_type").should_be_called().will_return("name")
ifc.run("unit.add_si_unit", unit_type="unit_type", name="name").should_be_called().will_return("unit")
ifc.run("unit.add_si_unit", unit_type="unit_type").should_be_called().will_return("unit")
unit.import_units().should_be_called()
assert subject.add_si_unit(ifc, unit, unit_type="unit_type") == "unit"
-6
View File
@@ -148,12 +148,6 @@ class TestGetSceneUnitSIPrefix:
assert subject.get_scene_unit_si_prefix() is None
class TestGetSINameFromUnitType:
def test_run(self):
for key, value in ifcopenshell.util.unit.si_type_names.items():
assert subject.get_si_name_from_unit_type(key) == value
class TestImportUnitAttributes(NewFile):
def test_importing_derived_units(self):
ifc = ifcopenshell.file()
+120
View File
@@ -0,0 +1,120 @@
# IfcSverchok
**IfcSverchok** is a [Sverchok](http://nortikin.github.io/sverchok/) plugin that allows you to create native IFC models alongside your parametric Sverchok geometry.
N.B.! IfcSverchok nodes are WIP. You can experience Blender crashes while using them, especially if you're undoing (ctrl/cmd-Z) something in the node tree.
## Packaged installation[](https://blenderbim.org/docs-python/ifcsverchok/installation.html#packaged-installation "Permalink to this headline")
IfcSverchok is packaged like a regular Blender add-on, so installation is the same as any other Blender add-on. You can download the package for installation at the [Get BlenderBIM](https://blenderbim.org/download.html) website.
Like all Blender add-ons, they can be installed using `Edit > Preferences > Addons > Install > Choose Downloaded ZIP > Enable Add-on Checkbox`. You can enable add-ons permanently by using `Save User Settings` from the Addons menu.
Before installing, you will also need to [install the BlenderBIM Add-on](https://blenderbim.org/download.html) and [install Sverchok](https://github.com/nortikin/sverchok/releases/tag/v1.1.0) (v1.1.0).
## Nodes
List of nodes that have been tested to the best of ability (nodes not listed here are not considered ready for use). If you find bugs/unexpected behaviour in any of them, please open an issue or get in touch otherwise.
Most nodes have a help description, and input tooltips, that show show up when hovering over the "?"-icon and socket input respectively.
| Node name | Inputs | Outputs | Description |
|--------------------------|-----------------------------------------------------------------------------------|---------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------|
| IFC Create Entity | - Names<br /> - Descriptions<br /> - IfcClass<br /> - Representations<br /> - Locations<br /> - Properties | - Entities Ids | Create IFC Entity. Takes one or multiple inputs.<br /> If 'Representation(s)' is given, that determines number of output entities. Otherwise, 'Names' is used. |
| IFC Read Entity | - Entity Id | - Id<br /> - is_a<br /> - Other entity attributes (dynamic) | Decompose an IfcEntity into its attributes. Takes one entity id as input, returns attribute values. |
| IFC BMesh to IFC Repr | - Context type<br /> - Context identifier<br /> - Target View<br /> - Blender Objects | - Representations<br /> - Locations | Blender mesh to IfcShapeRepresentation. Takes one or multiple geometries.<br /> Deconstructs joined geometries and creates a separate representation for each. |
| IFC Sverchok to IFC Repr | - Context type<br /> - Context identifier<br /> - Target View<br /> - Vertices<br /> - Edges<br /> - Faces | - Representations | Sverchok geometry to IfcShapeRepresentation. Takes one or multiple nested lists of inputs. |
| IFC Create Blender Shape | - Entity Id(s) | - Blender Object(s) | Create Blender shape from IfcEntity Id. Takes one or multiple IfcEntity Ids.<br /> Creates shapes in scene and returns them in output socket. |
| IFC Add Spatial Element | - Names<br /> - IfcClass<br /> - Elements | - Entity Id(s) | Add IfcElements to an IfcSpatialElement. Takes one or multiple nested lists of inputs.<br /> Input can also be a (list of) spacial elements to be aggregated. |
| IFC Write File | - path | | Writes active Ifc model to path. <br />N.B.! It's recommended to use the 'Write File' panel under the "IfcSverchok" tab instead. |
| IFC Class Picker | - IfcProduct (from drop-down)<br /> - IfcClass (from drop-down) | - IfcClass | Pick an IfcClass from drop-down list. |
| IFC By Id | - Id | - Entities | Get IfcElement by step id. Takes one or multiple step ids. |
| IFC By Guid | - Guid | - Entities | Get IfcElement by guid. Takes one or multiple guids. |
| IFC By Type | - IfcProduct (from drop-down)<br /> - IfcClass (from drop-down)<br /> - Custom IfcClass | - Entities<br /> - Entity Ids | Get IFC element(s) in file by type. Pick an IfcProduct and an IfcClass or give a custom IfcClass. |
| IFC Add Pset | - Name<br /> - Properties<br /> - Element Ids | - (Pset) Entities | Add a property set and corresponding properties, in a JSON key:value format, to IfcElements. |
| IFC Generate Guid | | - Guid | Generate a unique GUID. |
| IFC Get Property | - Entity Ids<br /> - Pset name<br /> - Prop name | - Value | Get the value of a property of an IfcEntity. Can take multiple entity ids. |
| IFC Get Attribute | - Entity Ids<br /> - Attribute Name | - Value | Get the value of an attribute of an IfcEntity. Can take multiple entities. |
### IfcSverchok panel
IfcSverchok creates a "IfcSverchok" tab in the Nodes panel. It includes a "Re-run all nodes" button that creates a fresh IFC File and a "Write File" button. It's recommended to re-run all nodes before saving the file either through the panel or the "Write File" node.
## Examples
### Parametric Facade
The sverchok model was kindly provided by Erindale Woodford [https://twitter.com/erindale_xyz](https://twitter.com/erindale_xyz). Check out the sverchok script and the resulting IFC model in the example_files folder.
The example model consists of columns, floors and parametric facade slats, arranged into three buildings with three storeys each.
#### Overview of the whole script
This is the whole script. Part on the left creates the parametric Sverchok model. Part on the right converts the model to native IFC elements.
![Alt text](./assets/parametric_facade1.png?raw=true)
#### Overview of the IFC part of the script
The IFC of the script consists of four sections: columns, floors, slats and storeys/building.
![Alt text](./assets/parametric_facade2.png?raw=true)
#### Create columns
Columns begin as three blender objects, one for each storey. "Blender Mesh to IFC Repr" node is used to create an IfcRepresentation for each distinct Blender geometry. Joined geometries are split up, but the original nesting is preserved, which results in three nested lists 84 with 84 representation IDs each.
These are then, together with locations, fed to the "Create Entity" node, which creates the same number of entities (here: IfcColumns) and outs them in the same shape as input. (The "By ID" node here only shows the first item of the, in total, 252 columns).
![Alt text](./assets/parametric_facade_columns.png?raw=true)
#### Create Floors
Floors are created in the same manner as columns. They also begin as three Blender objects (one pr storey), with three distinct geometries in each (one per building). Therefore nine representations and nine entities are created with the proper nesting. (The "By ID" node here only shows the first item of the, in total, nine slabs).
![Alt text](./assets/parametric_facade_floors.png?raw=true)
#### Create Slats
Slats are created from Sverchok geometries instead of Blender objects. There is no other reason for this, then to showcase an alternative method. As it can be seen, 353 representations and entities are created with the "Sverchok to IFC Repr" and "Create Entity" nodes.
![Alt text](./assets/parametric_facade_slats.png?raw=true)
#### Create Storeys and Building
Columns and floors are joined pr. storey and fed into the "Add Spatial Element" node, with the IfcClass "IfcBuildingStorey". This creates three storeys with unique names.
These storeys are then, together with slats, aggregated into an IfcBuilding using the same node.
![Alt text](./assets/parametric_facade_storeys.png?raw=true)
#### Saving the model
To save IFC model we have built, you simply need to navigate to the IfcSverchok tab in the node menu and, first, click on "IFC Re-run all nodes" and afterwards click on "Write File".
Re-running all nodes will take a couple of seconds, depending on the size of the model. Re-running creates a fresh IFC model, and removes all the duplicated/unneeded IfcElements that were created while building the model.
In this example model, we have created both storeys and a building, but it's not stricly necessary with IfcSverchok. All orphaned elements and spatial elements (like IfcSpace or IfcBuildingStorey) will be put into a default building and site. So in this example, even though we didn't make an IfcSite, it will be created, and our building put into it, when writing the file.
![Alt text](./assets/exporting_ifc.png?raw=true)
#### Opening The Created IFC Model
Now we can open the created IFC model with any IFC viewer or program. Here it is opened with BlenderBIM.
![Alt text](./assets/imported_ifc.png?raw=true)
### Other examples
<details>
<summary>
<b>Miscellaneous examples</b>
</summary>
Miscellaneous examples that showcase the "Add Pset", "Get Attribute", "Get Property", "By Guid", "By Type" and "Read Entity" nodes.
![Alt text](./assets/other_examples1.png?raw=true)
</details>
<details>
<summary>
<b>Multiple Blender Mesh Geometries</b>
</summary>
This example shows the behaviour of the "Blender Mesh to IFC Repr" and "Create Entity" nodes when dealing with jagged list inputs.
The initial inputs are two Blender objects, one containing a single geometry, and one containing two geometries (that were joined).
"Blender Mesh to IFC Repr" node creates three IfcRepresentations and outputs them in the shape [[[repr_1], [repr_2]], [repr_3]].
The "Create Entity" node preserves the same shape. Notice that names and descriptions are (at least for now) only checked for uniqueness pr. object, so we have the name "testwall1" and description "descr1" repeating between the two objects.
The output of "Create Enity" would look identical if only one name and one description was given as input.
![Alt text](./assets/other_examples2.png?raw=true)
</details>
<details>
<summary>
<b>Multiple Sverchok Geometries</b>
</summary>
Similarly, when we in this example convert two sets of three boxes ([[[box1], [box2], [box3]],[[box4], [box5], [box6]]]), we get six IfcRepresentations and entities with the original shape preserved.
Notice that the "By Id" node doesn't preserve input shape (at this point), since it should only be used for viewing.
![Alt text](./assets/other_examples3.png?raw=true)
</details>
+162 -9
View File
@@ -1,6 +1,5 @@
# IfcSverchok - IFC Sverchok extension
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
# Copyright (C) 2022 Martina Jakubowska <martina@jakubowska.dk>
#
# This file is part of IfcSverchok.
#
@@ -29,14 +28,15 @@ bl_info = {
"warning": "",
}
import sys
import importlib
import nodeitems_utils
import sverchok
from sverchok.core import sv_registration_utils, make_node_list
from sverchok.core import make_node_list
from sverchok.utils import auto_gather_node_classes, get_node_class_reference
from sverchok.menu import SverchNodeItem, SverchNodeCategory, register_node_panels
from sverchok.utils.extra_categories import register_extra_category_provider, unregister_extra_category_provider
from sverchok.menu import SverchNodeItem, SverchNodeCategory
from sverchok.utils.extra_categories import (
register_extra_category_provider,
unregister_extra_category_provider,
)
from sverchok.ui.nodeview_space_menu import make_extra_category_menus
from sverchok.utils.logging import info, debug
@@ -52,17 +52,25 @@ def nodes_index():
("ifc.create_entity", "SvIfcCreateEntity"),
("ifc.create_shape", "SvIfcCreateShape"),
("ifc.read_entity", "SvIfcReadEntity"),
("ifc.pick_ifc_class", "SvIfcPickIfcClass"),
("ifc.by_id", "SvIfcById"),
("ifc.by_guid", "SvIfcByGuid"),
("ifc.by_type", "SvIfcByType"),
("ifc.by_query", "SvIfcByQuery"),
("ifc.add", "SvIfcAdd"),
("ifc.add_pset", "SvIfcAddPset"),
("ifc.add_spatial_element", "SvIfcAddSpatialElement"),
("ifc.remove", "SvIfcRemove"),
("ifc.generate_guid", "SvIfcGenerateGuid"),
("ifc.get_property", "SvIfcGetProperty"),
("ifc.get_attribute", "SvIfcGetAttribute"),
("ifc.select_blender_objects", "SvIfcSelectBlenderObjects"),
("ifc.api", "SvIfcApi"),
("ifc.api_WIP", "SvIfcApiWIP"),
("ifc.bmesh_to_ifc", "SvIfcBMeshToIfcRepr"),
("ifc.sverchok_to_ifc", "SvIfcSverchokToIfcRepr"),
("ifc.create_project", "SvIfcCreateProject"),
("ifc.quick_project_setup", "SvIfcQuickProjectSetup"),
],
)
]
@@ -84,6 +92,145 @@ imported_modules = make_node_list()
reload_event = False
import bpy
from os.path import splitext
import ifcopenshell
from ifcsverchok.ifcstore import SvIfcStore
class IFC_Sv_UpdateCurrent(bpy.types.Operator):
"""Update current Sverchok node tree"""
bl_idname = "ifc.sverchok_update_current"
bl_label = "Update current node tree"
bl_options = {"REGISTER", "UNDO", "INTERNAL"}
node_group: bpy.props.StringProperty(default="")
force_mode: bpy.props.BoolProperty(default=False)
# FIXME For now it's fine to center around buildings.
# But for the future it'd be good to also allow other
# infra-related spatial structure elements, such as IfcBridge.
# https://github.com/IfcOpenShell/IfcOpenShell/pull/2576#discussion_r1016261407
def execute(self, context):
self.file = SvIfcStore.purge()
node_tree = context.space_data.node_tree
if node_tree:
if self.force_mode or node_tree.sv_process:
try:
bpy.context.window.cursor_set("WAIT")
node_tree.force_update()
finally:
bpy.context.window.cursor_set("DEFAULT")
self.report({"INFO"}, "Node tree updated.")
return {"FINISHED"}
class IFC_Sv_write_file(bpy.types.Operator):
bl_idname = "ifc.write_file_panel"
bl_label = "Write File"
bl_options = {"REGISTER", "UNDO"}
bl_description = "File path to write to."
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
node_group: bpy.props.StringProperty(default="")
force_mode: bpy.props.BoolProperty(default=False)
@classmethod
def poll(cls, context):
return any("IFC" in n for n in context.space_data.edit_tree.nodes.keys())
def ensure_hirarchy(self, file):
elements_in_buildings = set()
if len(file.by_type("IfcBuilding")) == 0:
my_building = ifcopenshell.api.run("root.create_entity", file, ifc_class="IfcBuilding", name="My Building")
elements = ifcopenshell.util.element.get_decomposition(my_building)
else:
for building in file.by_type("IfcBuilding"):
elements = ifcopenshell.util.element.get_decomposition(building)
elements_in_buildings.update(elements)
for spatial in file.by_type("IfcSpatialElement") or file.by_type("IfcSpatialStructureElement"):
if not (spatial.is_a("IfcSite") or spatial.is_a("IfcBuilding")) and (spatial not in elements_in_buildings):
elements = ifcopenshell.util.element.get_decomposition(spatial)
ifcopenshell.api.run(
"aggregate.assign_object", file, product=spatial, relating_object=file.by_type("IfcBuilding")[0]
)
for building in file.by_type("IfcBuilding"):
elements = ifcopenshell.util.element.get_decomposition(building)
elements_in_buildings.update(elements)
elements = file.by_type("IfcElement")
for element in elements:
if element not in elements_in_buildings:
ifcopenshell.api.run(
"spatial.assign_container", file, product=element, relating_structure=file.by_type("IfcBuilding")[0]
)
for building in file.by_type("IfcBuilding"):
elements = ifcopenshell.util.element.get_decomposition(building)
if not building.Decomposes:
if len(file.by_type("IfcSite")) == 0:
ifcopenshell.api.run("root.create_entity", file, ifc_class="IfcSite", name="My Site")
ifcopenshell.api.run(
"aggregate.assign_object", file, product=building, relating_object=file.by_type("IfcSite")[0]
)
try:
if file.by_type("IfcSite")[0].Decomposes[0].RelatingObject.is_a("IfcProject"):
continue
except IndexError:
pass
ifcopenshell.api.run(
"aggregate.assign_object",
file,
product=file.by_type("IfcSite")[0],
relating_object=file.by_type("IfcProject")[0],
)
self.file = file
return
def execute(self, context):
self.file = SvIfcStore.file
if not self.file:
raise Exception("No IFC file in SvIfcStore.")
_, ext = splitext(self.filepath)
# FIXME checking for extension not necessary
if not ext:
raise Exception("Bad path. Provide a path to a file ending with a file extension.")
else:
self.ensure_hirarchy(self.file)
self.file.write(self.filepath)
self.report({"INFO"}, f"File written to: {self.filepath}")
return {"FINISHED"}
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
return {"RUNNING_MODAL"}
class IFC_PT_write_file_panel(bpy.types.Panel):
bl_idname = "IFC_PT_write_file_panel"
bl_label = "Write IFC to file"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "NODE_EDITOR"
bl_region_type = "UI"
bl_category = "IfcSverchok"
@classmethod
def poll(cls, context):
if context.space_data.edit_tree and any("IFC" in n for n in context.space_data.edit_tree.nodes.keys()):
return True
def draw(self, context):
ng = context.space_data.node_tree
layout = self.layout
row = layout.split(factor=0.2, align=True)
row = layout.row()
row2 = layout.row()
row.operator("ifc.sverchok_update_current", text="IFC Re-run all nodes")
row2.operator("ifc.write_file_panel")
CLASSES = [IFC_Sv_UpdateCurrent, IFC_Sv_write_file, IFC_PT_write_file_panel]
def register_nodes():
@@ -109,7 +256,10 @@ def make_menu():
nodetype = item[1]
rna = get_node_class_reference(nodetype)
if not rna:
info("Node `%s' is not available (probably due to missing dependencies).", nodetype)
info(
"Node `%s' is not available (probably due to missing dependencies).",
nodetype,
)
else:
node_item = SverchNodeItem.new(nodetype)
node_items.append(node_item)
@@ -135,7 +285,8 @@ def register():
global our_menu_classes
debug("Registering ifcsverchok")
for klass in CLASSES:
bpy.utils.register_class(klass)
register_nodes()
extra_nodes = importlib.import_module(".nodes", "ifcsverchok")
auto_gather_node_classes(extra_nodes)
@@ -158,3 +309,5 @@ def unregister():
print(e)
unregister_extra_category_provider("IFCSVERCHOK")
unregister_nodes()
for klass in CLASSES:
bpy.utils.unregister_class(klass)
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 362 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 653 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 403 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 257 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 230 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 327 KiB

File diff suppressed because one or more lines are too long
+1 -2
View File
@@ -1,6 +1,5 @@
# IfcSverchok - IFC Sverchok extension
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
# Copyright (C) 2020, 2021, 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcSverchok.
#
+97
View File
@@ -0,0 +1,97 @@
# IfcSverchok - IFC Sverchok extension
# Copyright (C) 2022 Martina Jakubowska <martina@jakubowska.dk>
#
# This file is part of IfcSverchok.
#
# IfcSverchok is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcSverchok is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with IfcSverchok. If not, see <http://www.gnu.org/licenses/>.
import bpy
import ifcopenshell
from ifcopenshell import template
class SvIfcStore:
path = ""
file = None
schema = None
cache = None
cache_path = None
id_map = {}
guid_map = {}
deleted_ids = set()
edited_objs = set()
pset_template_path = ""
pset_template_file = None
library_path = ""
library_file = None
element_listeners = set()
undo_redo_stack_objects = set()
undo_redo_stack_object_names = {}
current_transaction = ""
last_transaction = ""
history = []
future = []
schema_identifiers = ["IFC4", "IFC2X3"]
@staticmethod
def purge():
SvIfcStore.path = ""
SvIfcStore.file = None
SvIfcStore.schema = None
SvIfcStore.cache = None
SvIfcStore.cache_path = None
SvIfcStore.id_map = {}
SvIfcStore.guid_map = {}
SvIfcStore.deleted_ids = set()
SvIfcStore.edited_objs = set()
SvIfcStore.pset_template_path = ""
SvIfcStore.pset_template_file = None
SvIfcStore.library_path = ""
SvIfcStore.library_file = None
SvIfcStore.last_transaction = ""
SvIfcStore.history = []
SvIfcStore.future = []
SvIfcStore.schema_identifiers = ["IFC4", "IFC2X3"]
@staticmethod
def create_boilerplate():
file = template.create(
filename="IfcSverchokDemoFile",
organization=None,
creator=None,
project_name="IfcSverchokDemoProject",
)
if bpy.context.scene.unit_settings.system == "IMPERIAL":
# TODO change units to imperial
pass
model = ifcopenshell.util.representation.get_context(file, context="Model")
print("model: ", model)
context = ifcopenshell.api.run(
"context.add_context",
file,
context_type="Model",
context_identifier="Body",
target_view="MODEL_VIEW",
parent=model,
)
SvIfcStore.file = file
return SvIfcStore.file
@staticmethod
def get_file():
if SvIfcStore.file is None:
SvIfcStore.create_boilerplate()
return SvIfcStore.file
+1 -3
View File
@@ -1,6 +1,5 @@
# IfcSverchok - IFC Sverchok extension
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
# Copyright (C) 2020, 2021, 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcSverchok.
#
@@ -16,4 +15,3 @@
#
# You should have received a copy of the GNU General Public License
# along with IfcSverchok. If not, see <http://www.gnu.org/licenses/>.
+1 -3
View File
@@ -1,6 +1,5 @@
# IfcSverchok - IFC Sverchok extension
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
# Copyright (C) 2020, 2021, 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcSverchok.
#
@@ -16,4 +15,3 @@
#
# You should have received a copy of the GNU General Public License
# along with IfcSverchok. If not, see <http://www.gnu.org/licenses/>.
+1 -2
View File
@@ -1,6 +1,5 @@
# IfcSverchok - IFC Sverchok extension
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
# Copyright (C) 2020, 2021, 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcSverchok.
#
+121
View File
@@ -0,0 +1,121 @@
# IfcSverchok - IFC Sverchok extension
# Copyright (C) 2022 Martina Jakubowska <martina@jakubowska.dk>
#
# This file is part of IfcSverchok.
#
# IfcSverchok is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcSverchok is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with IfcSverchok. If not, see <http://www.gnu.org/licenses/>.
import bpy
import ifcopenshell
import ifcsverchok.helper
from ifcsverchok.ifcstore import SvIfcStore
import bpy
import json
import ifcopenshell
from bpy.props import StringProperty
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode, flatten_data
class SvIfcAddPset(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
bl_idname = "SvIfcAddPset"
bl_label = "IFC Add Pset"
Name: StringProperty(
name="Name",
description="Name of the property set. Eg. Pset_WallCommon.",
update=updateNode,
default="My_Pset",
)
Properties: StringProperty(
name="Properties",
description='Propertied in a JSON key:value format.Eg. {"IsExternal":"True"}',
update=updateNode,
default='{"Foo":"Bar"}',
)
Elements: StringProperty(name="Element Ids", update=updateNode)
def sv_init(self, context):
self.inputs.new("SvStringsSocket", "Name").prop_name = "Name"
self.inputs.new("SvTextSocket", "Properties").prop_name = "Properties"
self.inputs.new("SvStringsSocket", "Elements").prop_name = "Elements"
self.outputs.new("SvStringsSocket", "Entity")
def draw_buttons(self, context, layout):
layout.operator(
"node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False
).tooltip = "Add a property set and corresponding properties to IfcElements."
def process(self):
if not any(socket.is_linked for socket in self.outputs):
return
name = self.inputs["Name"].sv_get()[0][0]
properties = self.inputs["Properties"].sv_get()[0][0]
element_ids = flatten_data(self.inputs["Elements"].sv_get(), target_level=1)
self.file = SvIfcStore.get_file()
try:
elements = [self.file.by_id(int(step_id)) for step_id in element_ids]
except Exception as e:
raise Exception("Instance ID not found", e)
if self.node_id not in SvIfcStore.id_map:
element = self.create(name, properties, elements)
else:
element = self.edit(name, properties, elements)
self.outputs["Entity"].sv_set([element])
def create(self, name, properties, elements):
results = []
for element in elements:
result = ifcopenshell.api.run(
"pset.add_pset", self.file, product=element, name=name
)
ifcopenshell.api.run(
"pset.edit_pset",
self.file,
pset=result,
properties=json.loads(properties),
)
SvIfcStore.id_map.setdefault(self.node_id, []).append(result.id())
results.append(result)
return results
def edit(self, name, properties, elements):
result_ids = SvIfcStore.id_map[self.node_id]
results = []
for result_id in result_ids:
result = self.file.by_id(result_id)
ifcopenshell.api.run(
"pset.edit_pset",
self.file,
pset=result,
name=name,
properties=json.loads(properties),
)
results.append(result)
return results
def register():
bpy.utils.register_class(SvIfcAddPset)
def unregister():
bpy.utils.unregister_class(SvIfcAddPset)
SvIfcStore.purge()
@@ -0,0 +1,251 @@
# IfcSverchok - IFC Sverchok extension
# Copyright (C) 2022 Martina Jakubowska <martina@jakubowska.dk>
#
# This file is part of IfcSverchok.
#
# IfcSverchok is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcSverchok is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with IfcSverchok. If not, see <http://www.gnu.org/licenses/>.
import bpy
from itertools import chain
import ifcopenshell
import ifcsverchok.helper
from ifcsverchok.ifcstore import SvIfcStore
from bpy.props import StringProperty
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import (
updateNode,
flatten_data,
repeat_last_for_length,
ensure_min_nesting,
)
class SvIfcAddSpatialElement(
bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore
):
bl_idname = "SvIfcAddSpatialElement"
bl_label = "IFC Add Spatial Element"
node_dict = {}
Names: StringProperty(
name="Name(s)",
description="Name or list of names of the spacial element.",
update=updateNode,
)
IfcClass: StringProperty(
name="IFC Class",
description='IfcClass of the spacial element. Eg. "IfcSpace".\nYou can use the "IFC Class Picker" node to find a relevant class.',
update=updateNode,
default="IfcSpace",
)
Elements: StringProperty(
name="Elements",
description="The IfcElements you want to add to the spacial element.\nThis can also be a (list of) spacial elements. Eg. IfcSpaces aggregated to an IfcBuildingStorey.",
update=updateNode,
)
def sv_init(self, context):
self.inputs.new("SvStringsSocket", "Names").prop_name = "Names"
self.inputs.new("SvStringsSocket", "IfcClass").prop_name = "IfcClass"
self.inputs.new("SvStringsSocket", "Elements").prop_name = "Elements"
self.outputs.new("SvStringsSocket", "Entities")
self.node_dict[hash(self)] = {}
def draw_buttons(self, context, layout):
layout.operator(
"node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False
).tooltip = "Add IfcElements to an IfcSpatialElement."
def process(self):
self.sv_input_names = [i.name for i in self.inputs]
if hash(self) not in self.node_dict:
self.node_dict[
hash(self)
] = {} # happens if node is already on canvas when blender loads
if not self.node_dict[hash(self)]:
self.node_dict[hash(self)].update(dict.fromkeys(self.sv_input_names, 0))
if not any(socket.is_linked for socket in self.outputs):
return
edit = False
edit_elements = False
for i in range(len(self.inputs)):
input = self.inputs[self.sv_input_names[i]].sv_get(
deepcopy=True, default=[]
)
if (
isinstance(self.node_dict[hash(self)][self.inputs[i].name], list)
and input != self.node_dict[hash(self)][self.inputs[i].name]
):
edit = True
if self.inputs[i].name == "Elements":
edit_elements = True
self.node_dict[hash(self)][self.inputs[i].name] = input.copy()
self.names = flatten_data(self.inputs["Names"].sv_get(), target_level=1)
self.ifc_class = flatten_data(self.inputs["IfcClass"].sv_get(), target_level=1)[
0
]
self.elements = ensure_min_nesting(self.inputs["Elements"].sv_get(), 2)
if isinstance(self.elements[0][0], list):
self.elements = [list(chain.from_iterable(el)) for el in self.elements]
if not self.elements[0][0]:
raise Exception('Mandatory input "Element(s)" is missing.')
return
self.file = SvIfcStore.get_file()
self.elements = [
[self.file.by_id(step_id) for step_id in element]
for element in self.elements
]
if "len" not in self.node_dict[hash(self)]:
self.node_dict[hash(self)]["len"] = 0
self.names = self.repeat_input_unique(self.names, len(self.elements))
if (self.node_id not in SvIfcStore.id_map) or (
len(self.elements) != self.node_dict[hash(self)]["len"]
):
self.remove()
elements = self.create()
self.node_dict[hash(self)]["len"] = len(self.elements)
else:
if edit is True:
elements = self.edit(edit_elements)
else:
elements = SvIfcStore.id_map[self.node_id]
self.outputs["Entities"].sv_set(elements)
def create(self, index=None):
spatial_ids = []
iterator = range(len(self.elements))
if index is not None:
iterator = [index]
for i in iterator:
result = ifcopenshell.api.run(
"root.create_entity",
self.file,
name=self.names[i],
ifc_class=self.ifc_class,
)
for items in self.elements[i]:
if items.is_a("IfcSpatialElement") or items.is_a(
"IfcSpatialStructureElement"
):
ifcopenshell.api.run(
"aggregate.assign_object",
self.file,
product=items,
relating_object=result,
)
else:
ifcopenshell.api.run(
"spatial.assign_container",
self.file,
product=items,
relating_structure=result,
)
SvIfcStore.id_map.setdefault(self.node_id, []).append(result.id())
spatial_ids.append(result.id())
return spatial_ids
def edit(self, edit_elements):
spatial_ids = []
id_map_copy = SvIfcStore.id_map[self.node_id].copy()
for i, element in enumerate(self.elements):
try:
result_id = id_map_copy[i]
except IndexError:
id = self.create(index=i)
spatial_ids.append(id[0])
continue
result = self.file.by_id(result_id)
result.Name = self.names[i]
if edit_elements:
subelements = ifcopenshell.util.element.get_decomposition(result)
subelements = set(subelements)
for element in self.elements[i]:
element_set = set([element])
for removed_element in subelements - element_set:
if removed_element.is_a(
"IfcSpatialElement"
) or removed_element.is_a("IfcSpatialStructureElement"):
ifcopenshell.api.run(
"aggregate.unassign_object",
self.file,
product=removed_element,
relating_object=result,
)
else:
ifcopenshell.api.run(
"spatial.unassign_container",
self.file,
product=removed_element,
relating_object=result,
)
for added_element in element_set - subelements:
if added_element.is_a(
"IfcSpatialElement"
) or added_element.is_a("IfcSpatialStructureElement"):
ifcopenshell.api.run(
"aggregate.assign_object",
self.file,
product=added_element,
relating_object=result,
)
else:
ifcopenshell.api.run(
"spatial.assign_container",
self.file,
product=added_element,
relating_structure=result,
)
spatial_ids.append(result.id())
SvIfcStore.id_map[self.node_id] = spatial_ids
return spatial_ids
def remove(self):
if self.node_id in SvIfcStore.id_map:
for element_id in SvIfcStore.id_map[self.node_id]:
element = self.file.by_id(element_id)
ifcopenshell.api.run("root.remove_product", self.file, product=element)
del SvIfcStore.id_map[self.node_id]
def repeat_input_unique(self, input, count):
input = repeat_last_for_length(input, count, deepcopy=False)
if input[0]:
input = [
a if not (s := sum(j == a for j in input[:i])) else f"{a}-{s+1}"
for i, a in enumerate(input)
] # add number to duplicates
return input
def sv_free(self):
try:
del SvIfcStore.id_map[self.node_id]
del self.node_dict[hash(self)]
# print('Node was deleted')
except KeyError or AttributeError:
pass
def register():
bpy.utils.register_class(SvIfcAddSpatialElement)
def unregister():
bpy.utils.unregister_class(SvIfcAddSpatialElement)
SvIfcStore.purge()
+2 -2
View File
@@ -1,6 +1,5 @@
# IfcSverchok - IFC Sverchok extension
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
# Copyright (C) 2020, 2021, 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcSverchok.
#
@@ -27,6 +26,7 @@ from sverchok.data_structure import updateNode
def update_usecase(self, context):
print("API - running update usecase!")
module_usecase = self.get_module_usecase()
if module_usecase:
self.generate_node(*module_usecase)
+135
View File
@@ -0,0 +1,135 @@
# IfcSverchok - IFC Sverchok extension
# Copyright (C) 2022 Martina Jakubowska <martina@jakubowska.dk>
#
# This file is part of IfcSverchok.
#
# IfcSverchok is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcSverchok is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with IfcSverchok. If not, see <http://www.gnu.org/licenses/>.
import bpy
import ifcopenshell
import ifcopenshell.api
import ifcsverchok.helper
from bpy.props import StringProperty
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode
import importlib
class SvIfcTooltip(bpy.types.Operator):
bl_idname = "node.sv_ifc_tooltip"
bl_label = "IFC Info"
tooltip: bpy.props.StringProperty()
@classmethod
def description(cls, context, properties):
return properties.tooltip
def execute(self, context):
return {"FINISHED"}
class SvIfcApiWIP(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
bl_idname = "SvIfcApiWIP"
bl_label = "IFC API WIP"
tooltip: StringProperty(name="Tooltip")
usecase: StringProperty(name="usecase", update=updateNode)
current_usecase: StringProperty(name="current_usecase")
def sv_init(self, context):
self.inputs.new("SvStringsSocket", "usecase").prop_name = "usecase"
# input_socket.tooltip = "ifcopenshell.api usecase, written like 'module.usecase' \n E.g.: 'project.create_file'"
self.outputs.new("SvVerticesSocket", "file")
def draw_buttons(self, context, layout):
op = layout.operator(
"node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False
).tooltip = "ifcopenshell.api usecase, written like 'module.usecase' \n E.g.: 'project.create_file"
# op.tooltip = self.tooltip
def process(self):
self.sv_input_names = ["usecase"]
module_usecase = self.inputs["usecase"].sv_get()[0][0]
if module_usecase:
try:
module_usecase = self.get_module_usecase()
except:
raise Exception(f"Couldn't run generate_node(). Module usecase: {module_usecase}")
if ".".join(module_usecase) != self.current_usecase:
self.generate_node(*module_usecase)
try:
for i in range(0, len(self.inputs)):
input = self.inputs[i].sv_get()
except:
# This occurs when a blender save file is reloaded
self.generate_node(*module_usecase)
self.sv_input_names = [i.name for i in self.inputs]
super().process() # super().process() uses zip_long_repeat() which doesn't take objects like bmesh
def get_module_usecase(self):
usecase = self.inputs["usecase"].sv_get()[0][0]
if usecase:
return usecase.split(".")
def generate_node(self, module, usecase):
importlib.import_module(f"ifcopenshell.api.{module}.{usecase}")
local_module = getattr(getattr(ifcopenshell.api, module), usecase)
node_inputs = {}
if hasattr(local_module.Usecase(local_module), "file"):
node_inputs["file"] = None
node_inputs.update(getattr(local_module.Usecase(local_module), "settings", {}))
# print("node inputs: ", node_inputs)
while len(self.inputs) > 1:
self.inputs.remove(self.inputs[-1])
if node_inputs:
self.tooltip = ""
for name, data in node_inputs.items():
setattr(SvIfcApiWIP, name, StringProperty(name=name))
self.inputs.new("SvStringsSocket", name).prop_name = name
if data is not None:
self.tooltip = f"{name} ({data}): {data}\n"
else:
self.tooltip = f"{name}: None\n"
self.tooltip = self.tooltip.strip()
self.current_usecase = ".".join([module, usecase])
def process_ifc(self, usecase, *setting_values):
if usecase and setting_values:
settings = dict(zip(self.sv_input_names[1:], setting_values))
settings = {k: v for k, v in settings.items() if v != ""}
try:
if "file" in settings:
file = settings["file"]
settings.pop("file")
self.outputs["file"].sv_set([ifcopenshell.api.run(usecase, file, **settings)])
else:
self.outputs["file"].sv_set([ifcopenshell.api.run(usecase, **settings)])
except:
raise Exception(f"Couldn't run usecase.")
def register():
bpy.utils.register_class(SvIfcTooltip)
bpy.utils.register_class(SvIfcApiWIP)
def unregister():
bpy.utils.unregister_class(SvIfcApiWIP)
bpy.utils.unregister_class(SvIfcTooltip)
+310
View File
@@ -0,0 +1,310 @@
# IfcSverchok - IFC Sverchok extension
# Copyright (C) 2022 Martina Jakubowska <martina@jakubowska.dk>
#
# This file is part of IfcSverchok.
#
# IfcSverchok is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcSverchok is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with IfcSverchok. If not, see <http://www.gnu.org/licenses/>.
from copy import deepcopy
from decimal import Context
from email.policy import default
import bpy
import ifcopenshell
import ifcsverchok.helper
import ifcopenshell.api
from ifcsverchok.ifcstore import SvIfcStore
import blenderbim.tool as tool
import blenderbim.core.geometry as core
from bpy.props import (
StringProperty,
EnumProperty,
IntProperty,
BoolProperty,
PointerProperty,
)
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode, flatten_data, fixed_iter, flat_iter
from blenderbim.bim.module.root.prop import get_contexts
from sverchok.data_structure import zip_long_repeat, node_id
from sverchok.core.socket_data import sv_get_socket
from itertools import chain, cycle
class SvIfcBMeshToIfcRepr(
bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore
):
"""
Triggers: BMesh to Ifc Repr
Tooltip: Blender mesh to Ifc Shape Representation
"""
bl_idname = "SvIfcBMeshToIfcRepr"
bl_label = "IFC Blender Mesh to IFC Repr"
node_dict = {}
is_scene_dependent = True # if True and is_interactive then the node will be updated upon scene changes
def refresh_node(self, context):
if self.refresh_local:
self.process()
self.refresh_local = False
refresh_local: BoolProperty(
name="Update Node", description="Update Node", update=refresh_node
)
context_types = [
("Model", "Model", "Context type: Model", 0),
("Plan", "Plan", "Context type: Plan", 1),
]
context_identifiers = [
("Body", "Body", "Context identifier: Body", 0),
("Annotation", "Annotation", "Context identifier: Annotation", 1),
("Box", "Box", "Context identifier: Box", 2),
("Axis", "Axis", "Context identifier: Axis", 3),
]
target_views = [
("MODEL_VIEW", "MODEL_VIEW", "Target View: MODEL_VIEW", 0),
("PLAN_VIEW", "PLAN_VIEW", "Target View: PLAN_VIEW", 1),
("GRAPH_VIEW", "GRAPH_VIEW", "Target View: GRAPH_VIEW", 2),
("SKETCH_VIEW", "SKETCH_VIEW", "Target View: SKETCH_VIEW", 3),
]
blender_objects: PointerProperty(
name="Blender Mesh(es)",
description="Blender Mesh Object(s)",
update=updateNode,
type=bpy.types.Object,
)
context_type: EnumProperty(
name="Context Type",
description="Default: Model",
default="Model",
items=context_types,
update=updateNode,
)
context_identifier: EnumProperty(
name="Context Identifier",
description="Default: Body",
default="Body",
items=context_identifiers,
update=updateNode,
)
target_view: EnumProperty(
name="Target View",
description="Default: MODEL VIEW",
default="MODEL_VIEW",
items=target_views,
update=updateNode,
)
tooltip: StringProperty(name="Tooltip")
def sv_init(self, context):
self.inputs.new("SvStringsSocket", "context_type").prop_name = "context_type"
self.inputs.new(
"SvStringsSocket", "context_identifier"
).prop_name = "context_identifier"
self.inputs.new("SvStringsSocket", "target_view").prop_name = "target_view"
self.inputs.new(
"SvObjectSocket", "blender_objects"
).prop_name = "blender_objects" # no prop for now
self.outputs.new("SvVerticesSocket", "Representations")
self.outputs.new("SvMatrixSocket", "Locations")
def draw_buttons(self, context, layout):
layout.operator(
"node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False
).tooltip = "Blender mesh to Ifc Shape Representation. \nTakes one or multiple geometries.\nDeconstructs joined geometries and creates a separate representation for each."
row = layout.row(align=True)
row.prop(self, "is_interactive", icon="SCENE_DATA", icon_only=True)
row.prop(self, "refresh_local", icon="FILE_REFRESH")
def process(self):
self.sv_input_names = [i.name for i in self.inputs]
if hash(self) not in self.node_dict:
self.node_dict[
hash(self)
] = {} # happens if node is already on canvas when blender loads
if not self.node_dict[hash(self)]:
self.node_dict[hash(self)].update(dict.fromkeys(self.sv_input_names, 0))
if not self.inputs["blender_objects"].sv_get(deepcopy=True)[0]:
return
edit = False
for i in range(len(self.inputs)):
input = self.inputs[i].sv_get(deepcopy=True)
if (
isinstance(self.node_dict[hash(self)][self.inputs[i].name], list)
and input != self.node_dict[hash(self)][self.inputs[i].name]
):
edit = True
self.node_dict[hash(self)][self.inputs[i].name] = input.copy()
blender_objects = self.inputs["blender_objects"].sv_get(deepcopy=True)
self.file = SvIfcStore.get_file()
if self.refresh_local:
edit = True
if self.node_id not in SvIfcStore.id_map:
representations, locations = self.create(blender_objects)
else:
if edit is True:
self.edit()
representations, locations = self.create(blender_objects)
else:
representations = SvIfcStore.id_map[self.node_id]["Representations"]
locations = SvIfcStore.id_map[self.node_id]["Locations"]
self.outputs["Representations"].sv_set(representations)
self.outputs["Locations"].sv_set(locations)
def create(self, blender_objects):
representations_ids = []
locations = []
context = self.get_context()
for blender_object in blender_objects:
bpy.context.view_layer.objects.active = blender_object
try:
bpy.ops.object.mode_set(mode="OBJECT")
except Exception as e:
print(e)
pass
bpy.ops.object.select_all(action="DESELECT")
blender_object.select_set(True)
try:
bpy.ops.object.mode_set(mode="EDIT")
except Exception as e:
print(e)
pass
bpy.ops.mesh.separate(
type="LOOSE"
) # This isn't a great solution bc it creates new objects in the scene, thus changing the users model
representations_ids_obj = []
locations_obj = []
try:
bpy.ops.object.mode_set(mode="OBJECT")
except Exception as e:
print(e)
pass
for obj in bpy.context.selected_objects:
if blender_object.type == "MESH":
representation = ifcopenshell.api.run(
"geometry.add_representation",
self.file,
should_run_listeners=False,
blender_object=obj,
geometry=obj.data,
context=context,
)
if not representation:
raise Exception(
"Couldn't create representation. Possibly wrong context."
)
representations_ids_obj.append([representation.id()])
locations_obj.append([obj.matrix_world])
representations_ids.append(representations_ids_obj)
locations.append(locations_obj)
SvIfcStore.id_map.setdefault(self.node_id, {}).setdefault(
"Representations", []
).append(representations_ids_obj)
SvIfcStore.id_map.setdefault(self.node_id, {}).setdefault(
"Locations", []
).append(locations_obj)
bpy.ops.object.select_all(action="DESELECT")
return representations_ids, locations
def edit(self):
if "Representations" not in SvIfcStore.id_map[self.node_id]:
return
for obj in SvIfcStore.id_map[self.node_id]["Representations"]:
for step_id in obj:
ifcopenshell.api.run(
"geometry.remove_representation",
self.file,
representation=self.file.by_id(step_id[0]),
)
del SvIfcStore.id_map[self.node_id]["Representations"]
del SvIfcStore.id_map[self.node_id]["Locations"]
return
def get_context(self):
context = ifcopenshell.util.representation.get_context(
self.file, self.context_type, self.context_identifier, self.target_view
)
if not context:
parent = ifcopenshell.util.representation.get_context(
self.file, self.context_type
)
if not parent:
parent = ifcopenshell.api.run(
"context.add_context", self.file, context_type=self.context_type
)
context = ifcopenshell.api.run(
"context.add_context",
self.file,
context_type=self.context_type,
context_identifier=self.context_identifier,
target_view=self.target_view,
parent=parent,
)
SvIfcStore.id_map.setdefault(self.node_id, {}).setdefault(
"Contexts", []
).append(context.id())
return context
def sv_free(self):
try:
self.file = SvIfcStore.get_file()
if "Representations" in SvIfcStore.id_map[self.node_id]:
for obj in SvIfcStore.id_map[self.node_id]["Representations"]:
for step_id in obj:
ifcopenshell.api.run(
"geometry.remove_representation",
self.file,
representation=self.file.by_id(step_id),
)
if "Contexts" in SvIfcStore.id_map[self.node_id]:
for context_id in SvIfcStore.id_map[self.node_id]["Contexts"]:
context = self.file.by_id(context_id)
if not self.file.get_inverse(context):
if self.file.by_id(context_id).ParentContext:
parent = self.file.by_id(context_id).ParentContext
ifcopenshell.api.run(
"context.remove_context", self.file, context=context
)
if parent:
if not self.file.get_inverse(parent):
ifcopenshell.api.run(
"context.remove_context", self.file, context=parent
)
SvIfcStore.id_map[self.node_id]["Contexts"].remove(context_id)
del SvIfcStore.id_map[self.node_id]
del self.node_dict[hash(self)]
# print('Node was deleted')
except KeyError or AttributeError:
pass
def register():
bpy.utils.register_class(SvIfcBMeshToIfcRepr)
def unregister():
bpy.utils.unregister_class(SvIfcBMeshToIfcRepr)
+20 -12
View File
@@ -1,6 +1,5 @@
# IfcSverchok - IFC Sverchok extension
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
# Copyright (C) 2020, 2021, 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcSverchok.
#
@@ -17,31 +16,40 @@
# You should have received a copy of the GNU General Public License
# along with IfcSverchok. If not, see <http://www.gnu.org/licenses/>.
import itertools
import bpy
import ifcopenshell
import ifcsverchok.helper
from ifcsverchok.ifcstore import SvIfcStore
from bpy.props import StringProperty
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode
from sverchok.data_structure import updateNode, flatten_data
class SvIfcByGuid(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
bl_idname = "SvIfcByGuid"
bl_label = "IFC By Guid"
file: StringProperty(name="file", update=updateNode)
guid: StringProperty(name="guid", update=updateNode)
n_id: StringProperty(default="")
guid: StringProperty(name="Guid(s)", update=updateNode)
id_iter = itertools.count()
def sv_init(self, context):
self.inputs.new("SvStringsSocket", "file").prop_name = "file"
self.inputs.new("SvStringsSocket", "guid").prop_name = "guid"
self.outputs.new("SvStringsSocket", "entity")
self.outputs.new("SvStringsSocket", "Entities")
def draw_buttons(self, context, layout):
layout.operator(
"node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False
).tooltip = "Get IFC element by guid. Takes one or multiple guids."
def process(self):
self.sv_input_names = ["file", "guid"]
super().process()
def process_ifc(self, file, guid):
self.outputs["entity"].sv_set([[file.by_guid(guid)]])
self.guids = flatten_data(self.inputs["guid"].sv_get(), target_level=1)
if not self.guids[0]:
return
self.file = SvIfcStore.get_file()
self.entities = [self.file.by_guid(guid) for guid in self.guids]
self.outputs["Entities"].sv_set(self.entities)
def register():
+20 -13
View File
@@ -1,6 +1,5 @@
# IfcSverchok - IFC Sverchok extension
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
# Copyright (C) 2022 Martina Jakubowska <martina@jakubowska.dk>
#
# This file is part of IfcSverchok.
#
@@ -17,31 +16,39 @@
# You should have received a copy of the GNU General Public License
# along with IfcSverchok. If not, see <http://www.gnu.org/licenses/>.
import bpy
import ifcopenshell
import ifcsverchok.helper
from ifcsverchok.ifcstore import SvIfcStore
from bpy.props import StringProperty
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode
from sverchok.data_structure import updateNode, flatten_data
class SvIfcById(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
bl_idname = "SvIfcById"
bl_label = "IFC By Id"
file: StringProperty(name="file", update=updateNode)
id: StringProperty(name="id", update=updateNode)
id: StringProperty(
name="Id(s)",
update=updateNode,
)
def sv_init(self, context):
self.inputs.new("SvStringsSocket", "file").prop_name = "file"
self.inputs.new("SvStringsSocket", "id").prop_name = "id"
self.outputs.new("SvStringsSocket", "entity")
self.outputs.new("SvStringsSocket", "Entities")
def draw_buttons(self, context, layout):
layout.operator(
"node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False
).tooltip = "Get IFC element by step id. Takes one or multiple step ids."
def process(self):
self.sv_input_names = ["file", "id"]
super().process()
def process_ifc(self, file, id):
self.outputs["entity"].sv_set([[file.by_id(int(id))]])
self.ids = flatten_data(self.inputs["id"].sv_get(), target_level=1)
if not self.ids[0]:
return
self.file = SvIfcStore.get_file()
self.entities = [self.file.by_id(int(step_id)) for step_id in self.ids]
self.outputs["Entities"].sv_set(self.entities)
def register():
+10 -9
View File
@@ -1,6 +1,5 @@
# IfcSverchok - IFC Sverchok extension
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
# Copyright (C) 2020, 2021, 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcSverchok.
#
@@ -21,6 +20,7 @@ import bpy
import ifcopenshell
import ifcopenshell.util.selector
import ifcsverchok.helper
from ifcsverchok.ifcstore import SvIfcStore
from bpy.props import StringProperty
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode
@@ -29,21 +29,22 @@ from sverchok.data_structure import updateNode
class SvIfcByQuery(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
bl_idname = "SvIfcByQuery"
bl_label = "IFC By Query"
file: StringProperty(name="file", update=updateNode)
query: StringProperty(name="query", update=updateNode)
query: StringProperty(name="Query", update=updateNode)
def sv_init(self, context):
self.inputs.new("SvStringsSocket", "file").prop_name = "file"
self.inputs.new("SvStringsSocket", "query").prop_name = "query"
self.outputs.new("SvStringsSocket", "entity")
self.outputs.new("SvStringsSocket", "Entity")
def process(self):
self.sv_input_names = ["file", "query"]
if not self.inputs["query"].sv_get()[0][0]:
return
self.file = SvIfcStore.get_file()
self.sv_input_names = ["query"]
super().process()
def process_ifc(self, file, query):
def process_ifc(self, query):
selector = ifcopenshell.util.selector.Selector()
self.outputs["entity"].sv_set([selector.parse(file, query)])
self.outputs["Entity"].sv_set([selector.parse(self.file, query)])
def register():
+50 -18
View File
@@ -1,6 +1,5 @@
# IfcSverchok - IFC Sverchok extension
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
# Copyright (C) 2022 Martina Jakubowska <martina@jakubowska.dk>
#
# This file is part of IfcSverchok.
#
@@ -21,13 +20,14 @@ import bpy
import ifcopenshell
import ifcsverchok.helper
from bpy.props import StringProperty, EnumProperty
from ifcsverchok.ifcstore import SvIfcStore
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode
def get_ifc_products(self, context):
ifc_products = getattr(self, "ifc_products", [])
file = self.inputs["file"].sv_get()[0][0]
file = SvIfcStore.get_file()
if not file:
return []
if ifc_products and file:
@@ -48,7 +48,11 @@ def get_ifc_products(self, context):
]
)
if file.schema == "IFC2X3":
ifc_products[2] = ("IfcSpatialStructureElement", "IfcSpatialStructureElement", "")
ifc_products[2] = (
"IfcSpatialStructureElement",
"IfcSpatialStructureElement",
"",
)
return ifc_products
@@ -61,7 +65,7 @@ def get_ifc_classes(self, context):
ifc_classes = getattr(self, "ifc_classes", [])
if ifc_classes:
return self.ifc_classes
file = self.inputs["file"].sv_get()[0][0]
file = SvIfcStore.get_file()
if not file:
return []
schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(file.schema)
@@ -83,31 +87,59 @@ def get_ifc_classes(self, context):
class SvIfcByType(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
bl_idname = "SvIfcByType"
bl_label = "IFC By Type"
file: StringProperty(name="file", update=updateNode)
ifc_product: EnumProperty(items=get_ifc_products, name="Products", update=update_ifc_products)
ifc_class: EnumProperty(items=get_ifc_classes, name="Class", update=updateNode)
custom_ifc_class: StringProperty(name="Custom Ifc Class", update=updateNode)
ifc_product: EnumProperty(
items=get_ifc_products,
name="IfcProduct",
description="Pick an IfcProduct from drop-down.",
update=update_ifc_products,
)
ifc_class: EnumProperty(
items=get_ifc_classes,
name="IfcClass",
description="Pick an IfcClass from drop-down.",
update=updateNode,
)
custom_ifc_class: StringProperty(
name="Custom IfcClass",
description="Give the name of your custom IfcClass.",
update=updateNode,
)
def sv_init(self, context):
self.inputs.new("SvStringsSocket", "file").prop_name = "file"
self.inputs.new("SvStringsSocket", "ifc_product").prop_name = "ifc_product"
self.inputs.new("SvStringsSocket", "ifc_class").prop_name = "ifc_class"
self.inputs.new("SvStringsSocket", "custom_ifc_class").prop_name = "custom_ifc_class"
self.outputs.new("SvStringsSocket", "entity")
self.inputs.new(
"SvStringsSocket", "custom_ifc_class"
).prop_name = "custom_ifc_class"
self.outputs.new("SvStringsSocket", "Entities")
self.outputs.new("SvStringsSocket", "Entity Ids")
self.width = 200
def draw_buttons(self, context, layout):
layout.operator(
"node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False
).tooltip = "Get IFC element(s) in file by type. \nPick an IfcProduct and an IfcClass or give a custom IfcClass."
def process(self):
file = self.inputs["file"].sv_get()[0][0]
if file:
self.file = SvIfcStore.get_file()
if self.file:
self.ifc_products = get_ifc_products(self, bpy.context)
self.ifc_classes = get_ifc_classes(self, bpy.context)
self.sv_input_names = ["file", "ifc_product", "ifc_class", "custom_ifc_class"]
self.sv_input_names = ["ifc_product", "ifc_class", "custom_ifc_class"]
super().process()
def process_ifc(self, file, ifc_product, ifc_class, custom_ifc_class):
def process_ifc(self, ifc_product, ifc_class, custom_ifc_class):
if custom_ifc_class:
self.outputs["entity"].sv_set([file.by_type(custom_ifc_class)])
entities = self.file.by_type(custom_ifc_class)
self.outputs["Entities"].sv_set(entities)
self.outputs["Entity Ids"].sv_set([e.id() for e in entities])
elif ifc_class:
entities = self.file.by_type(ifc_class)
self.outputs["Entities"].sv_set(self.file.by_type(ifc_class))
self.outputs["Entity Ids"].sv_set([e.id() for e in entities])
else:
self.outputs["entity"].sv_set([file.by_type(ifc_class)])
self.outputs["Entities"].sv_set([])
self.outputs["Entity Ids"].sv_set([])
def register():
+262 -72
View File
@@ -1,6 +1,5 @@
# IfcSverchok - IFC Sverchok extension
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
# Copyright (C) 2022 Martina Jakubowska <martina@jakubowska.dk>
#
# This file is part of IfcSverchok.
#
@@ -18,94 +17,285 @@
# along with IfcSverchok. If not, see <http://www.gnu.org/licenses/>.
import bpy
from mathutils import Matrix
import ifcopenshell
import ifcsverchok.helper
from bpy.props import StringProperty
from ifcsverchok.ifcstore import SvIfcStore
from bpy.props import StringProperty, BoolProperty
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode
from sverchok.data_structure import (
updateNode,
flatten_data,
repeat_last_for_length,
ensure_min_nesting,
)
class SvIfcCreateEntity(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
class SvIfcCreateEntity(
bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore
):
bl_idname = "SvIfcCreateEntity"
bl_label = "IFC Create Entity"
file: StringProperty(name="file", update=updateNode)
ifc_class: StringProperty(name="ifc_class", update=updateNode)
current_ifc_class: StringProperty(name="current_ifc_class")
node_dict = {}
is_scene_dependent = True # if True and is_interactive then the node will be updated upon scene changes
def refresh_node(self, context):
if self.refresh_local:
self.process()
self.refresh_local = False
refresh_local: BoolProperty(
name="Update Node", description="Update Node", update=refresh_node
)
Names: StringProperty(
name="Names",
default="",
description="Entity name or list of names.",
update=updateNode,
)
Descriptions: StringProperty(
name="Descriptions",
default="",
description="Entity description or list of descriptions.",
update=updateNode,
)
IfcClass: StringProperty(name="IfcClass", update=updateNode)
Representations: StringProperty(
name="Representations",
description='IfcRepresentation(s). Use eg. "IFC BMesh to IFC" or "IFC Sverchok to IFC" nodes to create representations.',
default="",
update=updateNode,
)
# Locations: FloatVectorProperty(name="Locations", default="", subtype='MATRIX', update=updateNode)
# Properties: StringProperty(name="properties", update=updateNode)
def sv_init(self, context):
self.inputs.new("SvStringsSocket", "file").prop_name = "file"
self.inputs.new("SvStringsSocket", "ifc_class").prop_name = "ifc_class"
self.outputs.new("SvStringsSocket", "file")
self.outputs.new("SvStringsSocket", "entity")
self.inputs.new("SvStringsSocket", "Names").prop_name = "Names"
self.inputs.new("SvStringsSocket", "Descriptions").prop_name = "Descriptions"
self.inputs.new("SvStringsSocket", "IfcClass").prop_name = "IfcClass"
self.inputs.new(
"SvStringsSocket", "Representations"
).prop_name = "Representations"
self.inputs.new("SvMatrixSocket", "Locations").is_mandatory = False
# self.inputs.new("SvStringsSocket", "Properties").prop_name = "Properties"
self.outputs.new("SvStringsSocket", "Entities")
self.node_dict[hash(self)] = {}
def draw_buttons(self, context, layout):
layout.operator(
"node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False
).tooltip = "Create IFC Entity. Takes one or multiple inputs. \nIf 'Representation(s)' is given, that determines number of output entities. Otherwise, 'Names' is used."
row = layout.row(align=True)
row.prop(self, "is_interactive", icon="SCENE_DATA", icon_only=True)
row.prop(self, "refresh_local", icon="FILE_REFRESH")
def process(self):
self.sv_input_names = ["file", "ifc_class"]
ifc_class = self.inputs["ifc_class"].sv_get()[0][0]
if ifc_class:
file = self.inputs["file"].sv_get()[0][0]
if file:
schema_name = file.wrapped_data.schema
else:
schema_name = "IFC4"
self.schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(schema_name)
self.entity_schema = self.schema.declaration_by_name(ifc_class)
self.names = flatten_data(self.inputs["Names"].sv_get(), target_level=1)
self.descriptions = flatten_data(
self.inputs["Descriptions"].sv_get(), target_level=1
)
self.ifc_class = flatten_data(self.inputs["IfcClass"].sv_get(), target_level=1)[
0
]
self.representations = ensure_min_nesting(
self.inputs["Representations"].sv_get(), 3
)
self.representations = flatten_data(self.representations, target_level=3)
self.locations = ensure_min_nesting(
self.inputs["Locations"].sv_get(default=[]), 3
)
self.locations = flatten_data(self.locations, target_level=3)
self.sv_input_names = [i.name for i in self.inputs]
if ifc_class != self.current_ifc_class:
self.generate_inputs(ifc_class)
try:
for i in range(0, len(self.inputs)):
self.inputs[i].sv_get()
except:
# This occurs when a blender save file is reloaded
self.generate_inputs(ifc_class)
if hash(self) not in self.node_dict:
self.node_dict[
hash(self)
] = {} # happens if node is already on canvas when blender loads
if not self.node_dict[hash(self)]:
self.node_dict[hash(self)].update(dict.fromkeys(self.sv_input_names, 0))
if not self.inputs["IfcClass"].sv_get()[0][0]:
raise Exception('Mandatory input "IfcClass" is missing.')
for i in range(0, self.entity_schema.attribute_count()):
self.sv_input_names.append(self.entity_schema.attribute_by_index(i).name())
super().process()
edit = False
for i in range(len(self.inputs)):
input = self.inputs[self.sv_input_names[i]].sv_get(
deepcopy=True, default=[]
)
if (
isinstance(self.node_dict[hash(self)][self.inputs[i].name], list)
and input != self.node_dict[hash(self)][self.inputs[i].name]
):
edit = True
self.node_dict[hash(self)][self.inputs[i].name] = input.copy()
def generate_inputs(self, ifc_class):
while len(self.inputs) > 2:
self.inputs.remove(self.inputs[-1])
for i in range(0, self.entity_schema.attribute_count()):
name = self.entity_schema.attribute_by_index(i).name()
setattr(SvIfcCreateEntity, name, StringProperty(name=name))
self.inputs.new("SvStringsSocket", name).prop_name = name
self.current_ifc_class = ifc_class
def process_ifc(self, file, ifc_class, *attributes):
entity = file.create_entity(ifc_class)
for i in range(0, len(entity)):
try:
value = attributes[i]
if isinstance(value, str) and value == "":
value = None
except:
value = None
if value is None and entity.is_a("IfcRoot") and i == 0:
value = ifcopenshell.guid.new()
if value is not None:
value = self.cast_value(self.entity_schema.attribute_by_index(i), value)
try:
entity[i] = value
except:
raise Exception(
"The IFC class {} requires a valid value for {} - you provided {}".format(
ifc_class, entity.attribute_name(i), value
if self.refresh_local:
edit = True
self.file = SvIfcStore.get_file()
if self.representations[0][0][0]:
representations = []
names = []
descriptions = []
for group in self.representations:
try:
group_representations = [
[self.file.by_id(step_id) for step_id in representation]
for representation in group
]
representations.append(group_representations)
except Exception as e:
raise
names.append(
self.repeat_input_unique(self.names, len(group_representations))
)
descriptions.append(
self.repeat_input_unique(
self.descriptions, len(group_representations)
)
)
self.outputs["file"].sv_set([[file]])
self.outputs["entity"].sv_set([[entity]])
self.representations = representations
self.names = names
self.descriptions = descriptions
elif not self.representations[0][0][0]:
self.descriptions = self.repeat_input_unique(
self.descriptions, len(self.names)
)
self.names = ensure_min_nesting(self.names, 2)
self.descriptions = ensure_min_nesting(self.descriptions, 2)
if self.node_id not in SvIfcStore.id_map:
entities = self.create()
else:
if edit is True:
entities = self.edit()
else:
entities = SvIfcStore.id_map[self.node_id]
def cast_value(self, attribute, value):
data_type = self.get_attribute_data_type(attribute.type_of_attribute())
if data_type == "integer":
value = int(value)
return value
self.outputs["Entities"].sv_set(entities)
def get_attribute_data_type(self, data_type):
if hasattr(data_type, "declared_type"):
return self.get_attribute_data_type(data_type.declared_type())
return data_type
def create(self, index=None):
entities_ids = []
iterator1 = range(len(self.names))
if index is not None:
iterator1 = [index[0]]
for i in iterator1:
group = self.names[i]
group_entities_ids = []
iterator2 = range(len(group))
if index is not None:
iterator2 = [index[1]]
for j in iterator2:
try:
entity = ifcopenshell.api.run(
"root.create_entity",
self.file,
ifc_class=self.ifc_class,
name=self.names[i][j],
description=self.descriptions[i][j],
)
try:
for repr in self.representations[i][j]:
if repr:
ifcopenshell.api.run(
"geometry.assign_representation",
self.file,
product=entity,
representation=repr,
)
except IndexError:
pass
try:
for loc in self.locations[i][j]:
if isinstance(loc, Matrix):
ifcopenshell.api.run(
"geometry.edit_object_placement",
self.file,
product=entity,
matrix=loc,
)
except IndexError:
pass
group_entities_ids.append(entity.id())
except Exception as e:
raise Exception("Something went wrong. Cannot create entity.", e)
SvIfcStore.id_map.setdefault(self.node_id, []).append(group_entities_ids)
entities_ids.append(group_entities_ids)
return entities_ids
def edit(self):
entities_ids = []
id_map_copy = SvIfcStore.id_map[self.node_id].copy()
for i, group in enumerate(self.names):
group_entities_ids = []
for j, _ in enumerate(group):
try:
step_id = id_map_copy[i][j]
except IndexError:
id = self.create(index=[i, j])
group_entities_ids.append(id[0][0])
continue
entity = self.file.by_id(step_id)
entity.Name = self.names[i][j]
if self.descriptions[i][j]:
entity.Description = self.descriptions[i][j]
try:
for repr in self.representations[i][j]:
if repr and repr.is_a("IfcProductDefinitionShape"):
entity.Representation = repr
elif repr:
ifcopenshell.api.run(
"geometry.assign_representation",
self.file,
product=entity,
representation=repr,
)
except IndexError:
pass
try:
for loc in self.locations[i][j]:
if isinstance(loc, Matrix):
ifcopenshell.api.run(
"geometry.edit_object_placement",
self.file,
product=entity,
matrix=loc,
)
except IndexError:
pass
if entity.is_a() != self.ifc_class:
SvIfcStore.id_map[self.node_id][i].remove(step_id)
entity = ifcopenshell.util.schema.reassign_class(
self.file, entity, self.ifc_class
)
group_entities_ids.append(entity.id())
entities_ids.append(group_entities_ids)
SvIfcStore.id_map[self.node_id] = entities_ids
return entities_ids
def repeat_input_unique(self, input, count, flag=False):
input = repeat_last_for_length(input, count, deepcopy=False)
if input[0]:
if flag:
return [
[a] if not (s := sum(j == a for j in input[:i])) else [f"{a}-{s+1}"]
for i, a in enumerate(input)
]
input = [
a if not (s := sum(j == a for j in input[:i])) else f"{a}-{s+1}"
for i, a in enumerate(input)
] # add number to duplicates
return input
def sv_free(self):
try:
del SvIfcStore.id_map[self.node_id]
del self.node_dict[hash(self)]
# print('Node was deleted')
except KeyError or AttributeError:
pass
def register():
+1 -2
View File
@@ -1,6 +1,5 @@
# IfcSverchok - IFC Sverchok extension
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
# Copyright (C) 2020, 2021, 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcSverchok.
#
@@ -0,0 +1,78 @@
# IfcSverchok - IFC Sverchok extension
# Copyright (C) 2022 Martina Jakubowska <martina@jakubowska.dk>
#
# This file is part of IfcSverchok.
#
# IfcSverchok is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcSverchok is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with IfcSverchok. If not, see <http://www.gnu.org/licenses/>.
import bpy
import ifcopenshell
import ifcsverchok.helper
from sverchok.node_tree import SverchCustomTreeNode
class SvIfcCreateProject(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
bl_idname = "SvIfcCreateProject"
bl_label = "IFC Create Project"
def sv_init(self, context):
input_socket = self.inputs.new("SvStringsSocket", "file")
input_socket.tooltip = "ifc file to add the project to"
input_socket = self.inputs.new("SvStringsSocket", "project_name")
input_socket.tooltip = "Project name"
self.outputs.new("SvVerticesSocket", "file")
def draw_buttons(self, context, layout):
op = layout.operator(
"node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False
).tooltip = "Adds project, unit and context to IFC file"
# op.tooltip = self.tooltip
def process(self):
# file
file = self.inputs["file"].sv_get()[0][0]
if file:
schema_name = file.wrapped_data.schema
else:
schema_name = "IFC4"
# project name
project_name = self.inputs["project_name"].sv_get()[0][0]
self.process_ifc(file, project_name)
def process_ifc(self, file, project_name):
# create project
project = ifcopenshell.api.run("root.create_entity", file, ifc_class="IfcProject", name=str(project_name))
lengthunit = ifcopenshell.api.run("unit.add_si_unit", file, unit_type="LENGTHUNIT", name="METRE")
ifcopenshell.api.run("unit.assign_unit", file, units=[lengthunit])
model = ifcopenshell.api.run("context.add_context", file, context_type="Model")
context = ifcopenshell.api.run(
"context.add_context",
file,
context_type="Model",
context_identifier="Body",
target_view="MODEL_VIEW",
parent=model,
)
self.outputs["file"].sv_set([[file]])
def register():
bpy.utils.register_class(SvIfcCreateProject)
def unregister():
bpy.utils.unregister_class(SvIfcCreateProject)
+66 -43
View File
@@ -1,6 +1,5 @@
# IfcSverchok - IFC Sverchok extension
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
# Copyright (C) 2022 Martina Jakubowska <martina@jakubowska.dk>
#
# This file is part of IfcSverchok.
#
@@ -21,65 +20,89 @@ import bpy
import logging
import ifcopenshell
import ifcsverchok.helper
from ifcsverchok.ifcstore import SvIfcStore
import blenderbim.bim.import_ifc
from bpy.props import StringProperty
from bpy.props import StringProperty, PointerProperty, BoolProperty
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode
class SvIfcCreateShapeRefresh(bpy.types.Operator):
bl_idname = "node.sv_ifc_create_shape_refresh"
bl_label = "IFC Create Shape Refresh"
bl_options = {"UNDO"}
tree_name: StringProperty(default="")
node_name: StringProperty(default="")
def execute(self, context):
node = bpy.data.node_groups[self.tree_name].nodes[self.node_name]
node.process()
return {"FINISHED"}
from sverchok.data_structure import updateNode, flatten_data
class SvIfcCreateShape(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
"""
Triggers: Ifc create shape by entity
Tooltip: Create Blender shape by Ifc Entity
"""
is_scene_dependent = True
is_interactive = False
node_dict = {}
def refresh_node(self, context):
if self.refresh_local:
self.process()
self.refresh_local = False
refresh_local: BoolProperty(name="Create shape(s)", description="Update Node", update=refresh_node)
bl_idname = "SvIfcCreateShape"
bl_label = "IFC Create Shape"
file: StringProperty(name="file", update=updateNode)
entity: StringProperty(name="entity", update=updateNode)
bl_label = "IFC Create Blender Shape"
entity: StringProperty(name="Entity Id(s)", update=updateNode)
def sv_init(self, context):
self.inputs.new("SvStringsSocket", "file").prop_name = "file"
self.inputs.new("SvStringsSocket", "entity").prop_name = "entity"
self.inputs.new("SvStringsSocket", "Entities").prop_name = "entity"
self.outputs.new("SvStringsSocket", "Object(s)")
def draw_buttons(self, context, layout):
self.wrapper_tracked_ui_draw_op(layout, "node.sv_ifc_create_shape_refresh", icon="FILE_REFRESH", text="Refresh")
row = layout.row(align=True)
row.operator(
"node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False
).tooltip = "Create Blender shape from IfcEntity Id. Takes one or multiple IfcEntity IDs."
row.prop(self, "refresh_local", icon="FILE_REFRESH")
def process(self):
self.sv_input_names = ["file", "entity"]
super().process()
self.entities = flatten_data(self.inputs["Entities"].sv_get(), target_level=1)
if not self.entities[0]:
return
def process_ifc(self, file, entity):
try:
if not entity.is_a("IfcProduct"):
return
logger = logging.getLogger("ImportIFC")
self.ifc_import_settings = blenderbim.bim.import_ifc.IfcImportSettings.factory(bpy.context, "", logger)
settings = ifcopenshell.geom.settings()
shape = ifcopenshell.geom.create_shape(settings, entity)
ifc_importer = blenderbim.bim.import_ifc.IfcImporter(self.ifc_import_settings)
ifc_importer.file = file
mesh = ifc_importer.create_mesh(entity, shape)
obj = bpy.data.objects.new("IFC Element", mesh)
bpy.context.scene.collection.objects.link(obj)
except:
print("Entity could not be converted into a shape", entity)
if self.refresh_local or hash(self) not in self.node_dict:
self.file = SvIfcStore.get_file()
try:
self.entities = [self.file.by_id(step_id) for step_id in self.entities]
except Exception as e:
raise
blender_objects = self.create()
self.node_dict[hash(self)] = blender_objects
else:
blender_objects = self.node_dict[hash(self)]
self.outputs["Object(s)"].sv_set(blender_objects)
def create(self):
blender_objects = []
for entity in self.entities:
try:
if not entity.is_a("IfcProduct"):
return
logger = logging.getLogger("ImportIFC")
self.ifc_import_settings = blenderbim.bim.import_ifc.IfcImportSettings.factory(bpy.context, "", logger)
settings = ifcopenshell.geom.settings()
shape = ifcopenshell.geom.create_shape(settings, entity)
ifc_importer = blenderbim.bim.import_ifc.IfcImporter(self.ifc_import_settings)
ifc_importer.file = self.file
mesh = ifc_importer.create_mesh(entity, shape)
obj = bpy.data.objects.new("IFC Element", mesh)
blender_objects.append(obj)
bpy.context.scene.collection.objects.link(obj)
except:
raise Exception("Entity could not be converted into a shape. Entity: {}".format(entity))
return blender_objects
def register():
bpy.utils.register_class(SvIfcCreateShapeRefresh)
# bpy.utils.register_class(SvIfcCreateShapeRefresh)
bpy.utils.register_class(SvIfcCreateShape)
def unregister():
bpy.utils.unregister_class(SvIfcCreateShape)
bpy.utils.unregister_class(SvIfcCreateShapeRefresh)
# bpy.utils.unregister_class(SvIfcCreateShapeRefresh)
+1 -4
View File
@@ -1,6 +1,5 @@
# IfcSverchok - IFC Sverchok extension
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
# Copyright (C) 2020, 2021, 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcSverchok.
#
@@ -20,9 +19,7 @@
import bpy
import ifcopenshell
import ifcsverchok.helper
from bpy.props import StringProperty
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode
class SvIfcGenerateGuid(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
+34 -8
View File
@@ -1,6 +1,5 @@
# IfcSverchok - IFC Sverchok extension
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
# Copyright (C) 2020, 2021, 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcSverchok.
#
@@ -21,25 +20,52 @@ import bpy
import ifcopenshell
import ifcopenshell.util.element
import ifcsverchok.helper
from ifcsverchok.ifcstore import SvIfcStore
from bpy.props import StringProperty
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode
from sverchok.data_structure import updateNode, flatten_data
class SvIfcGetAttribute(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
class SvIfcGetAttribute(
bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore
):
bl_idname = "SvIfcGetAttribute"
bl_label = "IFC Get Attribute"
entity: StringProperty(name="entity", update=updateNode)
attribute_name: StringProperty(name="attribute_name", update=updateNode)
entity: StringProperty(name="Entity Ids", update=updateNode)
attribute_name: StringProperty(
name="Attribute name",
description='Name of attribute, eg. "Name".',
update=updateNode,
)
def sv_init(self, context):
self.inputs.new("SvStringsSocket", "entity").prop_name = "entity"
self.inputs.new("SvStringsSocket", "attribute_name").prop_name = "attribute_name"
self.inputs.new(
"SvStringsSocket", "attribute_name"
).prop_name = "attribute_name"
self.outputs.new("SvStringsSocket", "value")
def draw_buttons(self, context, layout):
layout.operator(
"node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False
).tooltip = (
"Get the value of an attribute of an IfcEntity. Can take multiple entities."
)
def process(self):
self.value_out = []
entity_nested_inputs = self.inputs["entity"].sv_get()
entity_nested_input_ids = flatten_data(
self.inputs["entity"].sv_get(), target_level=1
)
if not entity_nested_input_ids[0]:
return
self.file = SvIfcStore.get_file()
try:
entity_nested_inputs = [
self.file.by_id(int(step_id)) for step_id in entity_nested_input_ids
]
except Exception as e:
raise Exception("Instance ID not found", e)
attribute_name = self.inputs["attribute_name"].sv_get()[0][0]
for entities in entity_nested_inputs:
if hasattr(entities, "__iter__"):
+45 -17
View File
@@ -1,6 +1,5 @@
# IfcSverchok - IFC Sverchok extension
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
# Copyright (C) 2020, 2021, 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcSverchok.
#
@@ -21,35 +20,64 @@ import bpy
import ifcopenshell
import ifcopenshell.util.element
import ifcsverchok.helper
from ifcsverchok.ifcstore import SvIfcStore
from bpy.props import StringProperty
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode
from sverchok.data_structure import updateNode, flatten_data
class SvIfcGetProperty(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
class SvIfcGetProperty(
bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore
):
bl_idname = "SvIfcGetProperty"
bl_label = "IFC Get Property"
entity: StringProperty(name="entity", update=updateNode)
pset_name: StringProperty(name="pset_name", update=updateNode)
prop_name: StringProperty(name="prop_name", update=updateNode)
entity: StringProperty(name="Entity Ids", update=updateNode)
pset_name: StringProperty(
name="Pset Name",
description='Name of the property set, eg. "Pset_WallCommon".',
update=updateNode,
)
prop_name: StringProperty(
name="Prop Name",
description='Name of the property, eg. "IsExternal".',
update=updateNode,
)
def sv_init(self, context):
self.inputs.new("SvStringsSocket", "entity").prop_name = "entity"
self.inputs.new("SvStringsSocket", "entity_ids").prop_name = "entity"
self.inputs.new("SvStringsSocket", "pset_name").prop_name = "pset_name"
self.inputs.new("SvStringsSocket", "prop_name").prop_name = "prop_name"
self.outputs.new("SvStringsSocket", "value")
def process(self):
self.sv_input_names = ["entity", "pset_name", "prop_name"]
self.value_out = []
super().process()
self.outputs["value"].sv_set(self.value_out)
def draw_buttons(self, context, layout):
layout.operator(
"node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False
).tooltip = (
"Get the value of a property of an IfcEntity. Can take multiple entity ids."
)
def process_ifc(self, entity, pset_name, prop_name):
def process(self):
self.sv_input_names = ["entity_ids", "pset_name", "prop_name"]
entities_ids = flatten_data(self.inputs["entity_ids"].sv_get(), target_level=1)
pset_name = flatten_data(self.inputs["pset_name"].sv_get(), target_level=1)[0]
prop_name = flatten_data(self.inputs["prop_name"].sv_get(), target_level=1)[0]
if not entities_ids[0]:
return
self.file = SvIfcStore.get_file()
try:
self.value_out.append(ifcopenshell.util.element.get_psets(entity)[pset_name][prop_name])
except:
pass
self.entities = [self.file.by_id(int(step_id)) for step_id in entities_ids]
except Exception as e:
raise Exception(f"Invalid entity id: {e}")
self.value_out = []
for entity in self.entities:
try:
self.value_out.append(
ifcopenshell.util.element.get_psets(entity)[pset_name][prop_name]
)
except:
pass
self.outputs["value"].sv_set(self.value_out)
def register():
+115
View File
@@ -0,0 +1,115 @@
# IfcSverchok - IFC Sverchok extension
# Copyright (C) 2022 Martina Jakubowska <martina@jakubowska.dk>
#
# This file is part of IfcSverchok.
#
# IfcSverchok is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcSverchok is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with IfcSverchok. If not, see <http://www.gnu.org/licenses/>.
import bpy
import ifcopenshell
import ifcsverchok.helper
from bpy.props import EnumProperty
from ifcsverchok.ifcstore import SvIfcStore
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode
def get_ifc_products(self, context):
ifc_products = getattr(self, "ifc_products", [])
file = SvIfcStore.get_file()
if not file:
return []
if ifc_products and file:
return ifc_products
ifc_products = []
ifc_products.extend(
[
(e, e, "")
for e in [
"IfcElement",
"IfcElementType",
"IfcSpatialElement",
"IfcGroup",
"IfcStructuralItem",
"IfcContext",
"IfcAnnotation",
]
]
)
if file.schema == "IFC2X3":
ifc_products[2] = ("IfcSpatialStructureElement", "IfcSpatialStructureElement", "")
return ifc_products
def update_ifc_products(self, context):
if hasattr(self, "ifc_classes"):
self.ifc_classes.clear()
def get_ifc_classes(self, context):
ifc_classes = getattr(self, "ifc_classes", [])
if ifc_classes:
return self.ifc_classes
file = SvIfcStore.get_file()
if not file:
return []
schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(file.schema)
declaration = schema.declaration_by_name(self.ifc_product)
def get_classes(declaration):
results = []
if not declaration.is_abstract():
results.append(declaration.name())
for subtype in declaration.subtypes():
results.extend(get_classes(subtype))
return results
classes = get_classes(declaration)
ifc_classes.extend([(c, c, "") for c in sorted(classes)])
return ifc_classes
class SvIfcPickIfcClass(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
bl_idname = "SvIfcPickIfcClass"
bl_label = "IFC Class Picker"
ifc_product: EnumProperty(
items=get_ifc_products,
name="IfcProduct",
description="Pick an IfcProduct from drop-down.",
update=update_ifc_products,
)
ifc_class: EnumProperty(
items=get_ifc_classes, name="IfcClass", description="Pick an IfcClass from drop-down.", update=updateNode
)
def sv_init(self, context):
self.inputs.new("SvStringsSocket", "ifc_product").prop_name = "ifc_product"
self.inputs.new("SvStringsSocket", "ifc_class").prop_name = "ifc_class"
self.outputs.new("SvStringsSocket", "IfcClass")
self.width = 200
def draw_buttons(self, context, layout):
layout.operator("node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False).tooltip = "Ifc Class Picker"
def process(self):
ifc_class = self.inputs["ifc_class"].sv_get()[0][0]
self.outputs["IfcClass"].sv_set([ifc_class])
def register():
bpy.utils.register_class(SvIfcPickIfcClass)
def unregister():
bpy.utils.unregister_class(SvIfcPickIfcClass)
@@ -0,0 +1,96 @@
# IfcSverchok - IFC Sverchok extension
# Copyright (C) 2022 Martina Jakubowska <martina@jakubowska.dk>
#
# This file is part of IfcSverchok.
#
# IfcSverchok is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcSverchok is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with IfcSverchok. If not, see <http://www.gnu.org/licenses/>.
from email.mime import application
import bpy
import ifcopenshell
import ifcsverchok.helper
from bpy.props import StringProperty
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode
from ifcopenshell import template
class SvIfcQuickProjectSetup(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
bl_idname = "SvIfcQuickProjectSetup"
bl_label = "IFC Quick Project Setup"
schema_identifier: StringProperty(name="schema_identifier", update=updateNode, default="IFC4")
timestring: StringProperty(name="timestring", update=updateNode)
application: StringProperty(name="application", update=updateNode)
application_version: StringProperty(name="application_version", update=updateNode)
timestamp: StringProperty(name="timestamp", update=updateNode)
def sv_init(self, context):
input_socket = self.inputs.new("SvStringsSocket", "filename")
input_socket.tooltip = "Ifc file name"
input_socket = self.inputs.new("SvStringsSocket", "timestring")
input_socket.tooltip = "Timestring, default = current time"
input_socket = self.inputs.new("SvStringsSocket", "organization")
input_socket.tooltip = "Organization"
input_socket = self.inputs.new("SvStringsSocket", "creator")
input_socket.tooltip = "creator"
input_socket = self.inputs.new("SvStringsSocket", "schema_identifier")
input_socket.tooltip = "Schema, default = 'IFC4'"
input_socket = self.inputs.new("SvStringsSocket", "application_version")
input_socket.tooltip = "Application version"
input_socket = self.inputs.new("SvStringsSocket", "timestamp")
input_socket.tooltip = "Timestamp, default = current time"
input_socket = self.inputs.new("SvStringsSocket", "application")
input_socket.tooltip = "Application, default = 'IfcOpenShell'"
input_socket = self.inputs.new("SvStringsSocket", "project_globalid")
input_socket.tooltip = "Project GlobalId"
input_socket = self.inputs.new("SvStringsSocket", "project_name")
input_socket.tooltip = "Project name"
self.outputs.new("SvVerticesSocket", "file")
def draw_buttons(self, context, layout):
op = layout.operator(
"node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False
).tooltip = "Quick Project Setup: creates Ifc file and sets up a basic project"
# op.tooltip = self.tooltip
def process(self):
self.sv_input_names = [i.name for i in self.inputs]
super().process()
def process_ifc(self, *setting_values):
settings = dict(zip(self.sv_input_names, setting_values))
settings = {k: v for k, v in settings.items() if v != ""}
file = template.create(
filename=settings["filename"],
timestring=settings["timestring"],
organization=settings["organization"],
creator=settings["creator"],
schema_identifier=settings["schema_identifier"],
application_version=settings["application_version"],
timestamp=settings["timestamp"],
application=settings["application"],
project_globalid=settings["project_globalid"],
project_name=settings["project_name"],
)
self.outputs["file"].sv_set([[file]])
def register():
bpy.utils.register_class(SvIfcQuickProjectSetup)
def unregister():
bpy.utils.unregister_class(SvIfcQuickProjectSetup)
+35 -20
View File
@@ -1,6 +1,5 @@
# IfcSverchok - IFC Sverchok extension
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
# Copyright (C) 2020, 2021, 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcSverchok.
#
@@ -20,30 +19,48 @@
import bpy
import ifcopenshell
import ifcsverchok.helper
from ifcsverchok.ifcstore import SvIfcStore
from bpy.props import StringProperty
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode
from sverchok.data_structure import updateNode, ensure_min_nesting, flatten_data
class SvIfcReadEntity(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
class SvIfcReadEntity(
bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore
):
bl_idname = "SvIfcReadEntity"
bl_label = "IFC Read Entity"
file: StringProperty(name="file", update=updateNode)
entity: StringProperty(name="entity", update=updateNode)
entity: StringProperty(name="Entity Id", update=updateNode)
current_ifc_class: StringProperty(name="current_ifc_class")
def sv_init(self, context):
self.inputs.new("SvStringsSocket", "file").prop_name = "file"
self.inputs.new("SvStringsSocket", "entity").prop_name = "entity"
self.outputs.new("SvStringsSocket", "id")
self.outputs.new("SvStringsSocket", "is_a")
def draw_buttons(self, context, layout):
layout.operator(
"node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False
).tooltip = (
"Decompose an IfcEntity into its attributes. Takes one entity id as input"
)
def process(self):
self.sv_input_names = ["file", "entity"]
ifc_class = self.inputs["entity"].sv_get()[0][0]
if ifc_class:
ifc_class = ifc_class.is_a()
file = self.inputs["file"].sv_get()[0][0]
self.sv_input_names = ["entity"]
entity_id = flatten_data(self.inputs["entity"].sv_get(), target_level=1)
if not entity_id[0]:
return
if len(entity_id) > 1:
raise Exception("Only one entity can be read at a time")
self.file = SvIfcStore.get_file()
try:
entity = self.file.by_id(entity_id[0])
except Exception as e:
raise Exception("Instance with id {} not found".format(entity_id), e)
if entity:
ifc_class = entity.is_a()
file = SvIfcStore.get_file()
if file:
schema_name = file.wrapped_data.schema
else:
@@ -53,7 +70,12 @@ class SvIfcReadEntity(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.S
if ifc_class != self.current_ifc_class:
self.generate_outputs(ifc_class)
super().process()
self.outputs["id"].sv_set([entity.id()])
self.outputs["is_a"].sv_set([entity.is_a()])
for i in range(0, self.entity_schema.attribute_count()):
name = self.entity_schema.attribute_by_index(i).name()
self.outputs[name].sv_set([entity[i]])
def generate_outputs(self, ifc_class):
while len(self.outputs) > 2:
@@ -63,13 +85,6 @@ class SvIfcReadEntity(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.S
self.outputs.new("SvStringsSocket", name).prop_name = name
self.current_ifc_class = ifc_class
def process_ifc(self, file, entity):
self.outputs["id"].sv_set([[entity.id()]])
self.outputs["is_a"].sv_set([[entity.is_a()]])
for i in range(0, self.entity_schema.attribute_count()):
name = self.entity_schema.attribute_by_index(i).name()
self.outputs[name].sv_set([[entity[i]]])
def register():
bpy.utils.register_class(SvIfcReadEntity)
+1 -2
View File
@@ -1,6 +1,5 @@
# IfcSverchok - IFC Sverchok extension
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
# Copyright (C) 2020, 2021, 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcSverchok.
#
+1 -2
View File
@@ -1,6 +1,5 @@
# IfcSverchok - IFC Sverchok extension
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
# Copyright (C) 2020, 2021, 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcSverchok.
#
@@ -1,6 +1,5 @@
# IfcSverchok - IFC Sverchok extension
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
# Copyright (C) 2020, 2021, 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcSverchok.
#
@@ -0,0 +1,249 @@
# IfcSverchok - IFC Sverchok extension
# Copyright (C) 2022 Martina Jakubowska <martina@jakubowska.dk>
#
# This file is part of IfcSverchok.
#
# IfcSverchok is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcSverchok is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with IfcSverchok. If not, see <http://www.gnu.org/licenses/>.
from copy import deepcopy
import bpy
import ifcopenshell
import ifcsverchok.helper
import ifcopenshell.api
from ifcsverchok.ifcstore import SvIfcStore
import blenderbim.tool as tool
import blenderbim.core.geometry as core
from bpy.props import StringProperty, EnumProperty, IntProperty, FloatVectorProperty
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode, ensure_min_nesting
class SvIfcSverchokToIfcRepr(
bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore
):
"""
Triggers: Sv to Ifc Repr
Tooltip: Sverchok geometry to Ifc Shape Representation
"""
bl_idname = "SvIfcSverchokToIfcRepr"
bl_label = "IFC Sverchok to IFC Repr"
node_dict = {}
n_id: StringProperty()
context_types = [
("Model", "Model", "Context type: Model", 0),
("Plan", "Plan", "Context type: Plan", 1),
]
context_identifiers = [
("Body", "Body", "Context identifier: Body", 0),
("Annotation", "Annotation", "Context identifier: Annotation", 1),
("Box", "Box", "Context identifier: Box", 2),
("Axis", "Axis", "Context identifier: Axis", 3),
]
target_views = [
("MODEL_VIEW", "MODEL_VIEW", "Target View: MODEL_VIEW", 0),
("PLAN_VIEW", "PLAN_VIEW", "Target View: PLAN_VIEW", 1),
("GRAPH_VIEW", "GRAPH_VIEW", "Target View: GRAPH_VIEW", 2),
("SKETCH_VIEW", "SKETCH_VIEW", "Target View: SKETCH_VIEW", 3),
]
context_type: EnumProperty(
name="Context Type",
description="Default: Model",
default="Model",
items=context_types,
update=updateNode,
)
context_identifier: EnumProperty(
name="Context Identifier",
description="Default: Body",
default="Body",
items=context_identifiers,
update=updateNode,
)
target_view: EnumProperty(
name="Target View",
description="Default: MODEL VIEW",
default="MODEL_VIEW",
items=target_views,
update=updateNode,
)
def sv_init(self, context):
self.inputs.new("SvStringsSocket", "context_type").prop_name = "context_type"
self.inputs.new(
"SvStringsSocket", "context_identifier"
).prop_name = "context_identifier"
self.inputs.new("SvStringsSocket", "target_view").prop_name = "target_view"
self.inputs.new("SvVerticesSocket", "Vertices")
self.inputs.new("SvStringsSocket", "Edges")
self.inputs.new("SvStringsSocket", "Faces")
self.outputs.new("SvVerticesSocket", "Representation(s)")
self.width = 210
self.node_dict[hash(self)] = {}
def draw_buttons(self, context, layout):
op = layout.operator(
"node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False
).tooltip = "Sverchok geometry to Ifc Shape Representation. \nTakes one or multiple geometries."
def process(self):
if not any(socket.is_linked for socket in self.inputs):
return
self.file = SvIfcStore.get_file()
self.sv_input_names = [i.name for i in self.inputs]
if hash(self) not in self.node_dict:
self.node_dict[
hash(self)
] = {} # happens if node is already on canvas when blender loads
if not self.node_dict[hash(self)]:
self.node_dict[hash(self)].update(dict.fromkeys(self.sv_input_names, 0))
edit = False
for i in range(len(self.inputs)):
input = self.inputs[i].sv_get(deepcopy=False)
if (
isinstance(self.node_dict[hash(self)][self.inputs[i].name], list)
and input != self.node_dict[hash(self)][self.inputs[i].name]
):
edit = True
self.node_dict[hash(self)][self.inputs[i].name] = input
self.vertices = ensure_min_nesting(
self.inputs["Vertices"].sv_get(deepcopy=False), 4
)
self.edges = ensure_min_nesting(self.inputs["Edges"].sv_get(deepcopy=False), 4)
self.faces = ensure_min_nesting(self.inputs["Faces"].sv_get(deepcopy=False), 4)
data = list(zip(self.vertices, self.edges, self.faces))
objects = []
for object in data:
objects.append(list(zip(object[0], object[1], object[2])))
if self.node_id not in SvIfcStore.id_map:
representations = self.create(objects)
else:
if edit is True:
self.edit()
representations = self.create(objects)
else:
representations = SvIfcStore.id_map[self.node_id]["Representations"]
self.outputs["Representation(s)"].sv_set(representations)
def create(self, geo_data):
representations_ids = []
self.context = self.get_context()
for obj in geo_data:
representations_ids_obj = []
for item in obj:
representation = ifcopenshell.api.run(
"geometry.add_sverchok_representation",
self.file,
should_run_listeners=False,
context=self.context,
vertices=[list(map(tuple, item[0]))],
edges=[list(map(tuple, item[1]))],
faces=[list(map(tuple, item[2]))],
)
if not representation:
raise Exception(
"Couldn't create representation. Possibly wrong context."
)
representations_ids_obj.append([representation.id()])
representations_ids.append(representations_ids_obj)
SvIfcStore.id_map.setdefault(self.node_id, {}).setdefault(
"Representations", []
).append(representations_ids_obj)
return representations_ids
def edit(self):
if "Representations" not in SvIfcStore.id_map[self.node_id]:
return
for obj in SvIfcStore.id_map[self.node_id]["Representations"]:
for step_id in obj:
ifcopenshell.api.run(
"geometry.remove_representation",
self.file,
representation=self.file.by_id(step_id[0]),
)
del SvIfcStore.id_map[self.node_id]["Representations"]
return
def get_context(self):
context = ifcopenshell.util.representation.get_context(
self.file, self.context_type, self.context_identifier, self.target_view
)
if not context:
parent = ifcopenshell.util.representation.get_context(
self.file, self.context_type
)
if not parent:
parent = ifcopenshell.api.run(
"context.add_context", self.file, context_type=self.context_type
)
context = ifcopenshell.api.run(
"context.add_context",
self.file,
context_type=self.context_type,
context_identifier=self.context_identifier,
target_view=self.target_view,
parent=parent,
)
SvIfcStore.id_map.setdefault(self.node_id, {}).setdefault(
"Contexts", []
).append(context.id())
return context
def sv_free(self):
try:
self.file = SvIfcStore.get_file()
if "Representations" in SvIfcStore.id_map[self.node_id]:
for step_id in SvIfcStore.id_map[self.node_id]["Representations"]:
ifcopenshell.api.run(
"geometry.remove_representation",
self.file,
representation=self.file.by_id(step_id[0]),
)
if "Contexts" in SvIfcStore.id_map[self.node_id]:
for context_id in SvIfcStore.id_map[self.node_id]["Contexts"]:
context = self.file.by_id(context_id)
if not self.file.get_inverse(context):
if self.file.by_id(context_id).ParentContext:
parent = self.file.by_id(context_id).ParentContext
ifcopenshell.api.run(
"context.remove_context", self.file, context=context
)
if parent:
if not self.file.get_inverse(parent):
ifcopenshell.api.run(
"context.remove_context", self.file, context=parent
)
# print("Removed context with step ID: ", context_id)
SvIfcStore.id_map[self.node_id]["Contexts"].remove(context_id)
del SvIfcStore.id_map[self.node_id]
del self.node_dict[hash(self)]
# print('Node was deleted')
except KeyError or AttributeError:
pass
def register():
bpy.utils.register_class(SvIfcSverchokToIfcRepr)
def unregister():
bpy.utils.unregister_class(SvIfcSverchokToIfcRepr)
+118 -12
View File
@@ -1,6 +1,5 @@
# IfcSverchok - IFC Sverchok extension
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
# Copyright (C) 2022 Martina Jakubowska <martina@jakubowska.dk>
#
# This file is part of IfcSverchok.
#
@@ -17,30 +16,137 @@
# You should have received a copy of the GNU General Public License
# along with IfcSverchok. If not, see <http://www.gnu.org/licenses/>.
from os.path import abspath, splitext
import bpy
import ifcopenshell
import ifcsverchok.helper
from bpy.props import StringProperty
from ifcsverchok.ifcstore import SvIfcStore
from bpy.props import StringProperty, BoolProperty
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode
from sverchok.data_structure import updateNode, flatten_data
class SvIfcWriteFile(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
class SvIfcWriteFile(
bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore
):
"""
Triggers: Ifc write to file
Tooltip: Write active Sverchok Ifc file to path
"""
def refresh_node_local(self, context):
if self.refresh_local:
self.process()
self.refresh_local = False
refresh_local: BoolProperty(
name="Write", description="Write to file", update=refresh_node_local
)
bl_idname = "SvIfcWriteFile"
bl_label = "IFC Write File"
file: StringProperty(name="file", update=updateNode)
path: StringProperty(name="path", update=updateNode)
path: StringProperty(
name="path",
description="File path to write to. Can be relative.",
update=updateNode,
)
def sv_init(self, context):
self.inputs.new("SvStringsSocket", "file").prop_name = "file"
self.inputs.new("SvStringsSocket", "path").prop_name = "path"
self.outputs.new("SvStringsSocket", "output")
def draw_buttons(self, context, layout):
row = layout.row(align=True)
row.operator(
"node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False
).tooltip = "Writes active Ifc file to path.\n It will overwrite an existing file.\n N.B.! It's recommended to create a fresh IFC File using the 're-run all nodes' button in IfcSverchok panel before saving."
row.prop(self, "refresh_local", icon="FILE_REFRESH")
def process(self):
self.sv_input_names = ["file", "path"]
super().process()
path = flatten_data(self.inputs["path"].sv_get(), target_level=1)[0]
if not path:
return
path = abspath(path)
file = SvIfcStore.get_file()
_, ext = splitext(path)
if not ext:
raise Exception("Bad path. Provide a path to a file.")
if self.refresh_local and ext:
self.ensure_hirarchy(file)
file.write(path)
self.outputs["output"].sv_set(f"File written successfully to: {path}.")
def process_ifc(self, file, path):
file.write(path)
def ensure_hirarchy(self, file):
elements_in_buildings = []
if not 0 <= 0 < len(file.by_type("IfcBuilding")):
my_building = ifcopenshell.api.run(
"root.create_entity", file, ifc_class="IfcBuilding", name="My Building"
)
elements = ifcopenshell.util.element.get_decomposition(my_building)
else:
for building in file.by_type("IfcBuilding"):
elements = ifcopenshell.util.element.get_decomposition(building)
elements_in_buildings.extend(elements)
for spatial in file.by_type("IfcSpatialElement") or file.by_type(
"IfcSpatialStructureElement"
):
if not (spatial.is_a("IfcSite") or spatial.is_a("IfcBuilding")) and (
spatial not in elements_in_buildings
):
elements = ifcopenshell.util.element.get_decomposition(spatial)
ifcopenshell.api.run(
"aggregate.assign_object",
file,
product=spatial,
relating_object=file.by_type("IfcBuilding")[0],
)
elements_in_buildings_after = []
for building in file.by_type("IfcBuilding"):
elements = ifcopenshell.util.element.get_decomposition(building)
elements_in_buildings_after.extend(elements)
elements = file.by_type("IfcElement")
for element in elements:
if element not in elements_in_buildings:
ifcopenshell.api.run(
"spatial.assign_container",
file,
product=element,
relating_structure=file.by_type("IfcBuilding")[0],
)
for building in file.by_type("IfcBuilding"):
elements = ifcopenshell.util.element.get_decomposition(building)
if not building.Decomposes:
if not 0 <= 0 < len(file.by_type("IfcSite")):
ifcopenshell.api.run(
"root.create_entity", file, ifc_class="IfcSite", name="My Site"
)
ifcopenshell.api.run(
"aggregate.assign_object",
file,
product=building,
relating_object=file.by_type("IfcSite")[0],
)
try:
if (
file.by_type("IfcSite")[0]
.Decomposes[0]
.RelatingObject.is_a("IfcProject")
):
continue
except IndexError:
pass
ifcopenshell.api.run(
"aggregate.assign_object",
file,
product=file.by_type("IfcSite")[0],
relating_object=file.by_type("IfcProject")[0],
)
self.file = file
return
def register():