Compare commits

..

828 Commits

Author SHA1 Message Date
Sigma Dimensions 9422f804fb minor fix 2023-05-03 04:22:02 +01:00
Sigma Dimensions 415a7746be BBIM 4D Gantt: Support multiple languages, human readable durations, add convenient Print Function with different Sheet Formats,
start improving overall page style; refactor code to split js and css code away from mustache file
2023-05-02 17:27:47 +01:00
Andrej730 ee903208dc Small fix for text alignment in scheduler 2023-05-02 17:41:56 +05:00
Dion Moult f89e834fea Fix #3085. Prioritise shipped BlenderBIM Add-on packages instead of system packages to avoid dependency clashes. 2023-05-02 22:20:34 +10:00
Dion Moult e8b4335741 Minor UI cleanup. Remove align buttons from architectural tool panel. 2023-05-02 22:20:34 +10:00
Dion Moult 83010f0542 See #1676. You can now add basic space boundaries with the BIM Tool with Shift-B, either by selecting both a space and an element (e.g. wall) or selecting a space and placing the 3D cursor on a face. 2023-05-02 22:20:34 +10:00
Dion Moult 04e5f5036c See #1676. Deleting space boundaries now also removes connection geometry. 2023-05-02 22:20:34 +10:00
Andrej730 5d86d38a14 Support cells text alignment building schedule from ods #2885
Before - https://i.imgur.com/9QZ2YW2.png
After - https://i.imgur.com/M7ZHSJq.png
2023-05-02 17:04:48 +05:00
Andrej730 a82db1f622 Support merged columns building schedule from ods #2885
Before - https://i.imgur.com/p1l6ugn.png
After - https://i.imgur.com/9QZ2YW2.png
2023-05-02 16:24:30 +05:00
Andrej730 fb618d536b Keep rows height building svg schedule from ods #2885
Before - https://i.imgur.com/erMH4Ku.png
After - https://i.imgur.com/p1l6ugn.png
2023-05-02 14:56:05 +05:00
Andrej730 23f96779fa More safe adding roof modifier #3080 2023-05-02 10:38:25 +05:00
Dion Moult 7b67854654 Fix #3088. Prioritise body curves to be loaded before trying to load 2D annotations. 2023-05-02 13:11:01 +10:00
Sigma Dimensions 2e7f941465 BBIM 4D: Auto Update Resource Usage from resource tree 2023-05-02 02:47:22 +01:00
Sigma Dimensions 1c1706fde3 BBIM feature to create & view work schedule baselines 2023-05-02 02:43:54 +01:00
Sigma Dimensions ca77a895e0 IOS.API consider removing baseline tasks when deleting tasks 2023-05-02 02:31:48 +01:00
Sigma Dimensions 0f447f964e IOS.API usecase to create a work schedule baseline 2023-05-02 02:30:38 +01:00
Sigma Dimensions 2c31fc6b25 run black 2023-05-02 02:27:32 +01:00
Dion Moult 0ba79dc105 Accommodate space boundaries with no connection geometry, fetch spatial structure for IFC2X3, and fix scale bug when editing boundary profiles. 2023-05-02 10:23:54 +10:00
dependabot[bot] 52c686b656 Bump flask from 2.0.1 to 2.3.2 in /src/opencdeserver
Bumps [flask](https://github.com/pallets/flask) from 2.0.1 to 2.3.2.
- [Release notes](https://github.com/pallets/flask/releases)
- [Changelog](https://github.com/pallets/flask/blob/main/CHANGES.rst)
- [Commits](https://github.com/pallets/flask/compare/2.0.1...2.3.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-02 10:06:31 +10:00
Thomas Krijnen 64d98f1295 #2676 2023-05-01 20:16:39 +02:00
Andrej730 ceb2c4075c Option to scale symbol from ifc tag by scaling ifc text object #2946
Also added some text scaling in viewport if there is some symbol associated with text annotation to roughly visually indicate how big this thing is going to be in the svg.

Example - https://user-images.githubusercontent.com/9417531/235445393-447f3aba-8425-4da6-81d0-6bf9004fcf2b.mp4
2023-05-01 16:25:04 +05:00
Andrej730 166e7d52ec Keep text rotation for text annotation with symbols #2946 2023-05-01 16:25:04 +05:00
Dion Moult 644c2652ea See #1676. You can now toggle boundaries on or off in the BIM tool via Alt-B, with a special boundary decorator. 2023-05-01 21:19:29 +10:00
Andrej730 63b201fd95 Test command to load blend file during test scenario for debugging 2023-05-01 15:35:27 +05:00
Andrej730 d70616056c Moving decorators colors from scene to addon props #3033 2023-05-01 14:54:47 +05:00
Dion Moult a65e376886 See #1676. Tab now toggles space boundary surface profile editing. 2023-05-01 19:40:49 +10:00
Andrej730 a985c70f0c Fixed error on adding Roof modifier to non-footprint objects #3080 2023-05-01 14:03:28 +05:00
Andrej730 0d09e02e7e Fixed bug with double conversion of height/extrusion length #3073
Also fixed some tests
2023-05-01 13:46:24 +05:00
Thomas Krijnen 23e3551f27 Fix regex escape in rule_executor.py 2023-04-30 19:09:43 +02:00
Shohei Kunimatsu 885bca0865 Fix wrong attribute name usage (ValueComponent -> UnitComponent) in IfcFile::getUnit 2023-04-30 19:06:58 +02:00
Sigma Dimensions 0f874dad51 BBIM 4D features & fixes:
- feature to re-order tasks
- fixes for task sorting
- further workSchedule UI/UX improvements
- forgotten bits and bobs
2023-04-30 15:44:13 +01:00
Sigma Dimensions 107873cf6e - refactor IOS.API resource calculation to re-use shared utilities
- run black
2023-04-30 15:35:17 +01:00
Sigma Dimensions 4a8f5af7ba - BBIM 4D: features to review and edit Resource productivity data on the fly
- IOS: new resource utilitiy functions
2023-04-30 15:31:36 +01:00
Dion Moult 83084aeb7b Add profile-style (like slab editing) enable editing, edit, and disable editing operators for energy boundary surface geometry. 2023-04-30 20:57:51 +10:00
Dion Moult 51640ccc28 Fix #3030. Don't bother layerset slicing walls with a single layer. 2023-04-30 13:43:43 +10:00
Dion Moult aefbc134ea Cut merging now only applies to particular elements for the majority of cases. Drawing generation can be twice as fast due to this. 2023-04-30 13:43:43 +10:00
Massimo Fabbro 29e1d70334 Generate space from walls refactor and cleaning around 2023-04-29 22:33:19 +02:00
Massimo Fabbro 9118e529e6 Add generate space button in UI in N-Panel -> BlenderBim -> Architectural 2023-04-29 22:33:19 +02:00
Massimo Fabbro 8ee0a1611d Now it's possible to create spaces automatically from selected walls 2023-04-29 22:33:19 +02:00
Sigma Dimensions 2af4388f40 Fix duplicate task logic in IOS.API 2023-04-29 13:53:04 +01:00
Dion Moult 93a2d18ea6 Filter elements visible in the camera view. Drawing generation is now significantly faster on larger projects (e.g. 24s down to 10s for a portion of the Revit sample project) 2023-04-29 16:33:44 +10:00
Dion Moult fc49859ea4 Fallback to unmerged polygons if merging fails. More investigation needed. 2023-04-29 16:30:40 +10:00
Dion Moult c0ac9cb380 Viewport layerset slicing now uses a cache for speed. 2023-04-28 22:46:49 +10:00
Andrej730 d5cc6c3c8e Fixed double conversion in bim tool props after b4b6404
No more 1000000 and 3000000 mm values
2023-04-28 17:32:19 +05:00
Andrej730 9b22b5000f Added support for different railing cap types
Very similar to this - https://i.imgur.com/SNyvfgh.png
2023-04-28 17:24:41 +05:00
Dion Moult 3b69a4cc78 Fix bug where numerical tolerance caused certain objects to be unnecessarily updated. 2023-04-28 22:15:09 +10:00
Dion Moult adf54efcc5 Fix bug in cut decorator layersets where walls could be joined to odd non-layered things. 2023-04-28 22:14:26 +10:00
Dion Moult f6962eb58e Fix #3053. Bug where very thin meshes (<1mm) couldn't be reverse engineered into extrusions. 2023-04-28 21:05:13 +10:00
Andrej730 11601d6532 Fixed bug with wrong position of support in wall mounted railing
The problem was that that support's disk was not rotated according to the railing path.
2023-04-28 11:43:15 +05:00
Sigma Dimensions be2131d62c BBIM 4D: Auto-refresh task re-ordering 2023-04-27 13:58:08 +01:00
Dion Moult 3ffbf46ac2 Fix #3066. Regression crash when deleting openings. 2023-04-27 22:24:12 +10:00
Sigma Dimensions 3943107eec forgotten core functions & run black 2023-04-27 13:05:51 +01:00
Sigma Dimensions 94e3f75341 Sequence Tools clean-up 2023-04-27 13:03:32 +01:00
Sigma Dimensions 8bc8b950e7 BBIM 4D: Features to select a Work Schedule's assigned / unassigned products 2023-04-27 12:55:31 +01:00
Sigma Dimensions 05a95ac666 Work Schedule Panel UX improvement 2023-04-27 12:53:27 +01:00
Sigma Dimensions f016c803e4 Fix schedule task sorting 2023-04-27 12:49:27 +01:00
Dion Moult bac0dd37fa Fix #3052. Deep deletion of large geometries is now thousands of times faster. 2023-04-27 20:15:17 +10:00
Bruno Postle 75a37cf737 Fix crash when switching between git revisions 2023-04-26 23:50:36 +01:00
Bruno Postle bb7e9c4610 Fix check for invalid Git branch name 2023-04-26 22:25:27 +01:00
Andrej730 ee2f18349f WALL_MOUNTED_HANDRAIL type for Railing modifier
Added WALL_MOUNTED_HANDRAIL type for railing modifier. It's still a bit work in progress - still need to add support for different railing termination types.

Here's the short demonstration - https://user-images.githubusercontent.com/9417531/234582621-6c6f948b-9cdc-4be6-b380-17ddbf5803bf.mp4
2023-04-26 18:04:39 +05:00
Dion Moult 8829a3cbe9 See #1227. Minor fix. 2023-04-26 22:24:37 +10:00
Dion Moult b210e39c7e See #1227. Experimental viewport layerset visualisation. 2023-04-26 22:10:34 +10:00
Bruno Postle 4d4500f061 Fix some refresh glitches in IFC Git panel 2023-04-26 00:07:22 +01:00
Thomas Krijnen 79860a0c04 Fix iterator::get() invalid cast on nullptr 2023-04-25 21:00:01 +02:00
Bruno Postle d53fcdc616 Format ifcgit code with black 2023-04-25 07:30:35 +01:00
Dion Moult 5e6a094ac6 Fix #3046. Renaming drawings now updates all drawing references on sheets. 2023-04-24 22:41:38 +10:00
Andrej730 757bf4e916 Fixing error on using bim.override_mode_set_edit with no active object
Error was:
  File "\addons\blenderbim\bim\module\geometry\operator.py", line 775, in _execute
    obj.select_set(True)
AttributeError: 'NoneType' object has no attribute 'select_set'
2023-04-24 11:17:08 +05:00
Andrej730 7bb57b3ab9 Fixed blurred text with background after bfbced3
before commit - https://user-images.githubusercontent.com/9417531/233907917-f86ad10b-0227-4180-9488-a8b71a00f099.png
after commit https://user-images.githubusercontent.com/9417531/233907920-d100c73a-e206-42fd-bb41-f7257ea6f98c.png

Tested it on firefox too
2023-04-24 10:33:38 +05:00
Dion Moult cb22b5804d Fix #3055. Get paths using forward slashes for platform compatibility. 2023-04-24 13:11:42 +10:00
Dion Moult bfbced3859 Fix #3051. Text background filter now applied on <text> element to make it work on Firefox. 2023-04-24 12:58:36 +10:00
Dion Moult c090ee9b83 Fix #3040. View title template variables now have access to sheet attributes prefixed with "Sheet". 2023-04-23 22:04:49 +10:00
Dion Moult b6caf069d0 Fix #3001. User can now specify join criteria in drawings. Only elements where all the criteria have the same values are merged. 2023-04-23 21:32:00 +10:00
Dion Moult 5b4449eea0 Fix #3054. Edit sheet is now implemented for drawings, schedules, and titleblocks. 2023-04-23 17:49:51 +10:00
Bruno Postle 7dc17a3653 Include git, gitdb and smmp libs in blenderbim zip 2023-04-23 08:35:39 +01:00
Bruno Postle 598bf799de Install GitPython *before* removing the working folder 2023-04-23 08:12:49 +01:00
Bruno Postle 4774a37d48 Add GitPython dependency 2023-04-23 08:03:19 +01:00
Dion Moult d9a1e6f4b1 New default patterns for stack and stretcher bond tiles and floorboards. 2023-04-23 12:44:35 +10:00
Bruno Perdigão 72dae8e9ad applyed the patch by Bruno Postle 2023-04-23 12:05:57 +10:00
Bruno Perdigão b2d7fb9443 changed imports to integrate with blenderbim 2023-04-23 12:05:57 +10:00
Bruno Perdigão bf945e36bd initial commit 2023-04-23 12:05:57 +10:00
Dion Moult 2bcfe93dd2 See #3002. Spaces are now managed separately so that they don't conflict with projection polygon merging and raycasting. 2023-04-22 23:59:33 +10:00
Dion Moult 195ef743c6 See #3002. Experimental SVGFill approach to polygon merging. 2023-04-22 13:05:42 +10:00
Thomas Krijnen 8478eb9aa0 Update draw.py 2023-04-21 16:29:04 +02:00
Andrej730 47f57d20cd Fixed couple bugs in furniture library
1) last polyline point was failing on Consecutive rule causing validation errors
2) Fixed wrong ifc class for dishwasher (was used occurence class instead of type)
2023-04-21 18:24:32 +05:00
Dion Moult 8f8eb06d6b See #3002. Experimental shapely approach to polygon merging for semantic projection elements. 2023-04-21 22:33:08 +10:00
Andrej730 7d6803f325 Roof modifier - remove redundant context fill causing visual artifacts 2023-04-21 16:49:44 +05:00
Andrej730 f9b7a8b92b Roof modifier - removing bottom chord if angle = 90 degrees
This - https://i.imgur.com/75AmYGI.png
instead of https://community.osarch.org/uploads/editor/yp/zcaqxx9b33wu.png
2023-04-21 16:49:44 +05:00
Andrej730 7ee0c047e5 Roof Modifier - separating vertices on defining gable roof angle
Added option (which is activated by default) - when you set gable roof angle for some edge it will duplicate the top vertex instead of just moving which allows you to do shape like this - https://imgur.com/a/OEFZ4pw

Previously you would get something like this in that case - https://community.osarch.org/uploads/editor/ap/hlt70rbw5r1l.png
2023-04-21 16:49:44 +05:00
Andrej730 bfbfe17644 OverrideDelete - remove objects with X if they are not related to ifc
It was a bit frustraiting when you added a cube and can't just get rid of it with X hotkey 😅
2023-04-20 17:15:17 +05:00
Andrej730 046940f949 Fixed error in disabling text exiting after deleting text literal 2023-04-20 14:27:37 +05:00
Dion Moult 2950ddcf4f Fix validation error where IfcURIReference cannot be used as a property data type :( 2023-04-20 18:06:35 +10:00
Dion Moult 3d441b4ad3 Fix #3038. Fix #3039. Selector should default to none instead for throwing errors if the query doesn't have a result. 2023-04-20 17:41:52 +10:00
Dion Moult caa5e8983b Fix #3021. IfcSpatialElements are included in drawings by default. 2023-04-20 17:34:24 +10:00
Andrej 59b7944d8e Merge pull request #3032 from IfcOpenShell/quantities_project_units
Converting quantities to project units to fix #3025
2023-04-20 11:03:12 +05:00
Andrej730 648eb8fdd1 Converting to project units inside QtoCalculator.guess_quantity
Also added some error message if `calculate_quantity` failed to calculate a quantity
2023-04-20 11:01:38 +05:00
Andrej730 f160516772 tool.Qto.convert_to_project_units 2023-04-20 10:32:36 +05:00
Andrej730 4beb7780f8 bim.calculate_quantity - remove convertion part
now the convertion already happens in QtoCalculator.calculate_quantity
2023-04-20 10:32:24 +05:00
Kristoffer Andersen d44b6d40e9 pin lief version for conda build 2023-04-20 06:48:44 +02:00
Kristoffer Andersen 8fd9f5a8cb Add conda to conda daily build trigger path 2023-04-20 06:43:31 +02:00
Kristoffer Andersen 414af2f55e Change conda lief version 2023-04-20 06:40:44 +02:00
Kristoffer Andersen 8624be6bcf Fix conda env 2023-04-20 06:36:10 +02:00
Andrej730 4aa26b613b test_qto.py black format 2023-04-19 19:05:11 +05:00
Andrej730 57cb062d48 Tests for calculating quantities for prefix and imperial project units 2023-04-19 19:04:05 +05:00
Andrej730 b4b6404b65 Added DISTANCE subtype to BIM Tool Extrusion Depth and Length
In BIM Tool it looks like this right now - https://i.imgur.com/JWVPssg.png (note the units)
Previously it was displaying value like this - https://i.imgur.com/244aYTN.png

The benefit is that you can specify other units in that kind of property and it will automatically convert to current project's unit. 

For example if you have millimeters as project's unit you can still type "10m" instead of "5000" and it will automatically convert it to 5000mm. 
Or type in inches like 5" 11'.
2023-04-19 18:03:26 +05:00
Andrej730 989dd2be50 qto_calculator.py black format 2023-04-19 17:04:15 +05:00
Andrej730 50b8baddec Update QtoCalculator to convert to project units at the end
Before this commit it was calculating all values in meters not taking into account actual project units.
2023-04-19 17:03:32 +05:00
Andrej730 2e00c3db0b Update viewport on bim.activate_model
To see the changed context right away in the viewport
2023-04-19 12:12:11 +05:00
Andrej730 232260787e Fixed bug with wrong text value in viewport on canceling text editing
Actual text was replacing with "TEXT" default value if you cancel text editing instead of applying changes
2023-04-19 11:37:10 +05:00
Andrej730 54054c4aee Fixed bug in get_object_bounding_box #3023 2023-04-19 11:27:45 +05:00
arun 1a7843a6b5 Nesting added to util.get_decomposition function 2023-04-19 15:46:51 +10:00
Sigma Dimensions ed4ba4d0d3 fix "cost_item quantity updates" bug when editing a QTO 2023-04-19 01:01:44 +00:00
htlcnn 849d6118d0 fix EditBcfTopic and EditBcfTopicName 2023-04-19 09:23:50 +10:00
Andrej730 364138d408 CutDecorator color connected to preferences too #2987 2023-04-18 18:13:04 +05:00
Dion Moult 32c8da06ca New default triangle, hexagon, capsule, circle, and space tag symbols 2023-04-18 22:32:09 +10:00
Andrej730 2df4c5825f Added some decorator colors to addon preferences #2987
For ProfileDecorator (editing profiles, roof preview) and DecorationsHandler (openings).
Still need to add CutDecorator colors to preferences
2023-04-18 17:29:07 +05:00
Andrej730 527cbc3ae0 Some descriptions for EPset_Drawing 2023-04-18 16:59:07 +05:00
Andrej730 4fee8744d6 Fixed text annotation rotation in viewport #3017
Fixed text annotation rotation in viewport for the views rotated by multipel axis.
Changed text direction calculation to be the same in both viewport and svg.
2023-04-18 16:59:07 +05:00
Thomas Krijnen 8f8862d23d get_mappeditem_transformation() 2023-04-18 13:17:20 +02:00
Thomas Krijnen 836f36fd8e Add util.placement.get_cartesiantransformationoperator3d() 2023-04-18 13:15:13 +02:00
Thomas Krijnen a1326cb5ed Normalize vectors in a2p() 2023-04-18 13:13:29 +02:00
Vukas Pajic 4bfda8851c Update failure reason #3018 2023-04-18 13:12:24 +02:00
Andrej730 f02f15cbfb unsuppress zero inches in formatting distance #3011
Added option to suppress zero inches in the dimension annotation text through SuppressZeroInches property of BBIM_Dimension property set.
Previously by default suppressing zero inches was enabled. Now by default it is disabled.

SupressZeroInches On  - https://i.imgur.com/nrhELnA.png
SupprezZeroInches Off - https://i.imgur.com/gIuO2GW.png
2023-04-18 14:17:40 +05:00
Dion Moult 30182684d5 Fix bug where you could save geometry with no data (no vertices / splines) which is invalid. 2023-04-18 18:10:01 +10:00
Dion Moult 7df80af30b Fix #2913. Bug where properties with multiple inverses were not correctly purged. 2023-04-18 18:08:24 +10:00
Dion Moult 6b2e867107 Fix #2916. Bug where joining swept solids didn't merge styles correctly. 2023-04-18 18:07:44 +10:00
Vukas Pajic d333090b09 cleanup 2023-04-18 09:17:23 +10:00
Vukas Pajic 8ae7763ac5 added new tests for case where spec has no requirements 2023-04-18 09:17:23 +10:00
Andrej730 51ac86be79 Fixed bug in bim.select_assigned_product
It was trying to call method `Drawingcls.get_active_object()` that didn't exist
2023-04-17 14:47:11 +05:00
Andrej730 552078c8a2 TEXT_LEADERS BoxAlignment, FontSize, multiline in viewport preview #3008
Moved code for drawing text in viewport to a separate reusable method `draw_text` in BaseDecorator.
2023-04-17 11:21:07 +05:00
Dion Moult 4b4ede733f Add support for loading IfcProxy 2023-04-17 10:37:30 +10:00
Dion Moult d07e8bc747 Add support for editing ref latitude/longitude 2023-04-17 10:14:22 +10:00
Massimo Fabbro 8850d78e5a Small fix 2023-04-17 09:25:21 +10:00
Massimo Fabbro 4fbf4e7d42 Now it's possible to specify the digits number in a drawing. Fix #2993 2023-04-17 09:25:21 +10:00
Ryan Schultz d6e1a670b9 Library - changed to correct class 2023-04-17 09:24:35 +10:00
Dion Moult 1a32c8827e Fix #3003. 2023-04-17 09:11:33 +10:00
Dion Moult 784b5156df Fix #2975. Fix bug where imperial projects still created drawings with metric scales by default. 2023-04-16 23:18:22 +10:00
Dion Moult 6e3336afb1 See #2975. Fix bug where custom scales were not loaded correctly. 2023-04-16 23:17:56 +10:00
Dion Moult d0ddee3623 Fix bug where changing object visibility didn't first check for view layer existence. 2023-04-16 21:50:58 +10:00
Ryan Schultz 9cf55033d4 Fix #2989 2023-04-16 08:05:05 +10:00
Dion Moult 7371efde17 Fix #2980. Minor cleanup to polish array panel UI. 2023-04-15 16:01:50 +10:00
Dion Moult 156269a53f Fix #2988. Imperial dimensions in viewport are now split between feet and inches. 2023-04-15 15:33:01 +10:00
Ryan Schultz ed7e5fb482 Fix #2990 2023-04-14 15:57:36 -05:00
Andrej730 2e973a4591 Fixed bugs with furniture library toilets and basins #2976
Bug was introduced in cf6acd5 when `shape_builder.get_rectangle_coords` started returning coords in counter-clockwise order instead of clockwise.
2023-04-14 19:07:38 +05:00
Andrej730 f1bd82d301 Dimension description in viewport/option to hide the measurements #2918
same thing for diameter annotations since they are just reusing dimension annotations

+ some refactor along the way
2023-04-14 17:52:54 +05:00
Dion Moult f51bfa314d Fix #2986. Reimplement Metadata property of EPset_Drawing to include arbitrary metadata as CSS classes. 2023-04-14 20:24:02 +10:00
Dion Moult 7b3410c7b1 Fix #2985. Purge obsolete vector style feature. 2023-04-14 20:04:33 +10:00
Dion Moult 6b0e7d51b1 Fix #2979. Batch drawling unselected and selected cut lines. 2023-04-14 18:24:01 +10:00
Andrej730 4e5659efad Annotation Tool
Added Annotation Tool that allows you create annotation types and their occurences. It will also allow us to get rid of "Annotation" panel in N-panel.

I've recorded bunch of short videos to demonstrate the features:

- bulk tagging - currently works only for "Text" and "Stair arrow" type of annotations, adds annotations to all selected objects, automatically assigns products to the annotations
https://user-images.githubusercontent.com/9417531/231968358-28cbb762-3349-470a-beef-d1ab21882769.mp4

- creating annotation type with representation - all occurences will share the same representation which makes bulk tagging really powerful if you use variables like {{PredefinedType}}, {{GlobalId}}, {{Name}}, {{Description}}, {{ObjectType}}, {{Tag}}

https://user-images.githubusercontent.com/9417531/231970500-333514c6-6406-4ef5-a9e7-fddfcc9a2436.mp4

- creating annotation type without representation - it allows occurences to have unique text or line geometry but still share the same classes from inherited property set

Examples:
  creating text type without representation to create title case annotations
  https://user-images.githubusercontent.com/9417531/231969361-bda5f924-a8c8-4f5a-ba61-461aa7101f73.mp4

  creating type for oblique dimension annotations
  https://user-images.githubusercontent.com/9417531/231969902-38e8c3f4-45a6-4c98-bba4-2956288ce10a.mp4

- readjusting - if you created some annotations using bulk tagging but the products were changed / moved afterwards now you can select those annotations and "readjust" them according to new product's position / measurements
https://user-images.githubusercontent.com/9417531/231968975-ee8d6589-60ee-4575-9be2-b0ce68d43ffa.mp4

PS In this commit I've also moved all icons to 1 .blend file and added instructions how to regenerate them.
2023-04-14 12:40:53 +05:00
Dion Moult 66f10adde8 Fix #2980. Array deletion now uses delete tool. Delete moved into tool so it can be used by other functions. 2023-04-14 09:56:55 +10:00
Dion Moult 459c4d7fcc See #2978. Fix crash upon drawing deletion. 2023-04-14 09:36:51 +10:00
Dion Moult 03d9efb790 Fix #2955. Drawings can now set a precision. 2023-04-13 22:20:18 +10:00
Dion Moult 33bd6099ee Fix #2971. Only update placement if object actually moved. 2023-04-13 21:56:32 +10:00
Dion Moult 850a1d326b Purge old code from timer-based type browser system 2023-04-13 21:39:54 +10:00
Thomas Krijnen f0bb19d48d Fixes to validation of selected simple type / enum 2023-04-13 13:16:48 +02:00
Dion Moult a56eac0131 Minor fix 2023-04-13 18:48:20 +10:00
Dion Moult d3201809ab Fix #2968. Deletions are no longer postponed. Should fix or reveal a lot of sync errors. 2023-04-13 18:38:28 +10:00
Dion Moult 3628a0e241 Fix bug where switching representations didn't update the material checksum, leading to unnecessary material syncs at export time. 2023-04-13 18:15:28 +10:00
Dion Moult 07badd3df5 Styles are applied per mesh data now (potentially dangerous). Fix crash when switching to representation which has styles from an unstyled mesh. 2023-04-13 16:10:00 +10:00
Dion Moult f63091ce05 Fix #2974. Type property sets are now properly copied. 2023-04-13 14:08:02 +10:00
Dion Moult 617b2978c0 New activate model operator to switch back to a MODEL_VIEW target view subcontext 2023-04-12 21:27:43 +10:00
Dion Moult 364e96ad1e Activating a drawing now automatically switches to the preferred target view representation of all elements 2023-04-12 21:27:43 +10:00
Dion Moult 9170e5ca65 New "cut decorator" that draws cut lines in drawings. 2023-04-12 21:27:43 +10:00
Dion Moult cbe71f221c Only activate drawing styles if underlay enabled, and default to default Blender styles to prevent confusion 2023-04-12 21:27:43 +10:00
Dion Moult c039c01407 Regenerating fills now maintains the active representation 2023-04-12 21:27:43 +10:00
Dion Moult 629ba41f5f Only allow drawing / sheet / schedule creation once the IFC is saved to enforce correct relative paths. 2023-04-12 21:27:43 +10:00
Andrej730 198fa67cc1 Focus on text field on edit Ifc Text with BIM Tool hotkey (once again)
This option was here before but it was accidentally discarded after f7181aa
2023-04-12 11:51:50 +05:00
CyrilWaechter e7830e3d55 Fix #2964 2023-04-11 22:03:26 +02:00
Andrej730 90bfb25f41 restoring selection on bim.override_mode_set_edit if nothing worked
it was a bit confusing when you're trying to edit some object, you get error message but you also lose object selection
2023-04-11 19:17:03 +05:00
Andrej730 037cb00caa Fixed bug with roof created in projects with milimeters units
the problem was that operators were converting angle using SI and angle was set to 10000
2023-04-11 19:17:03 +05:00
Andrej730 f002124c38 Fixed error in PLAN_LEVEL elevation value in printed svgs #2961
Also added more precision to inPerFoot in helper.format_distance to avoid this a bit annoying precision problem where format function keeps adding 1/256"

Without precision - https://i.imgur.com/D9WYjvJ.png
With precision - https://i.imgur.com/3XgOFSH.png
2023-04-11 19:06:59 +05:00
Andrej730 ac42f9f599 Changed roof preview colors and it's now transparent
Changed roof preview colors to the same as openings
2023-04-11 19:06:58 +05:00
Andrej730 88d4640d3f fixed mistake in tool.Blender.apply_bmesh
was resulting in error when you apply roof path
2023-04-11 17:30:29 +05:00
Andrej730 d2da01041c Added checkboxes to drawing list to mark multiple selected drawings
Checkboxes affect the drawings that will be used when you shift click "Create drawing", "Open Drawing", "Remove Drawing". Previously those opeartors were always applying the action to all drawings.

Also added option to remove selected drawings with shift+click to "Remove Drawing" and added new operator "Select all drawings" to select drawings in one click (you can shift click it to delsect all drawings).

Example - https://i.imgur.com/GAGghu5.png
2023-04-11 17:14:10 +05:00
Dion Moult 75c17b820f Fix bug where invalidly mapped representations shouldn't be edited. 2023-04-11 21:50:28 +10:00
Thomas Krijnen 2dc4bbf8cd Support rule compilation and execution on latebound schema 2023-04-11 13:24:32 +02:00
Thomas Krijnen c9a5abc3be Some small fixes for rule compliance in template.py 2023-04-11 09:46:50 +02:00
Thomas Krijnen 7ea468a9a6 Update build-all.py 2023-04-11 08:47:46 +02:00
Dion Moult c6223520e0 Fix #2947. Embarrassing bug where slabs didn't consider the total thickness. 2023-04-11 16:24:39 +10:00
Dion Moult 176cbf9861 Fix #2948. Bug where IFC array tool didn't track undo properly causing crashes. 2023-04-11 15:21:04 +10:00
Dion Moult 00315a6f2d See #2516. Titleblocks dropdown now also shows project titleblocks. 2023-04-11 14:08:48 +10:00
Dion Moult 2145b2f8e2 Fix #2955. You can now store default paths in a project pset. 2023-04-11 14:06:31 +10:00
Dion Moult 31155e7bc9 See #2516. Fix issue where getting sheet layout URIs were not explicit. 2023-04-11 12:26:05 +10:00
Trashman247 2d07208d0f Reformat parametrized "Save" in IFC project (again) 2023-04-11 10:26:48 +10:00
Trashman247 35ad7e16c6 Reformat parametrized "Save" in IFC project 2023-04-11 10:26:48 +10:00
Trashman247 27c52fe6d5 Add dynamic tool tip for parametrized "Save" in IFC project 2023-04-11 10:26:48 +10:00
Andrej e153f63a23 Merge pull request #2962 from maxfb87/fix_bug
Fix minor bug
2023-04-11 01:23:58 +05:00
Andrej 2cbbc360c0 Merge branch 'v0.7.0' into fix_bug 2023-04-11 01:23:27 +05:00
Thomas Krijnen 8a4a42777f Property transfer ownership of schema from py to c++ upon register_schema() 2023-04-10 22:01:53 +02:00
Thomas Krijnen 352d6b5ef9 Attempt load schema from cwd (IS THIS DANGEROUS?) 2023-04-10 20:31:56 +02:00
Thomas Krijnen cc49b0efdc Don't fail on unmapped attribute types (IFC4.4) 2023-04-10 20:17:26 +02:00
Andrej730 e71ee988bf Fixed error on disabling BlenderBIM addon
Error was occuring because macros doesn't have `is_registered` attribute.

Error: Traceback (most recent call last):
  File "C:\Software\Steam\steamapps\common\Blender\3.5\scripts\modules\addon_utils.py", line 421, in disable
    mod.unregister()
  File "\scripts\addons\blenderbim\__init__.py", line 45, in unregister
    blenderbim.bim.unregister()
  File "\scripts\addons\blenderbim\bim\__init__.py", line 192, in unregister
    if cls.is_registered is not False:
AttributeError: type object 'OverrideDuplicateMoveMacro' has no attribute 'is_registered'

Still getting some other error on disabling the addon, seems to happen due ongoing drawing system restructure.
Error: Traceback (most recent call last):
  File "C:\Software\Steam\steamapps\common\Blender\3.5\scripts\modules\addon_utils.py", line 421, in disable
    mod.unregister()
  File "\addons\blenderbim\__init__.py", line 45, in unregister
    blenderbim.bim.unregister()
  File "\addons\blenderbim\bim\__init__.py", line 177, in unregister
    if bpy.data.scenes['Scene'].BIMProperties.module_visibility['drawing'].is_visible:
KeyError: 'bpy_prop_collection[key]: key "drawing" not found'
2023-04-10 18:05:40 +05:00
Dion Moult 937a5b2c2d Fix #2516. Add/build/remove/view/rename/copy sheet/drawing/schedule now all references and updates user configurable relative paths. 2023-04-10 22:30:33 +10:00
Dion Moult 92221e19c3 Fix bug where unassigning a document was too aggressive and deleted relationships where it shouldn't. 2023-04-10 22:30:33 +10:00
CyrilWaechter e5c920864c Add ifczip and ifcxml to x-ifc mime-type 2023-04-10 11:43:26 +02:00
Nathan Hild 6f63682bdf Correct tooltip messages for enabling and disabling edit buttons 2023-04-10 10:28:10 +02:00
Andrej730 7623eddcd7 Including/Excluding IfcAnnotations from drawing #2903
Including or excluding elements from the drawing using EPset_Drawing.Include/Exclude now also works for IfcAnnotation elements. Both in viewport (on "activate view") and on svg print.

Related change - annotation decorations are now hidden in viewport if their object is hidden.
2023-04-10 12:37:39 +05:00
Andrej730 d1dee1e908 Fixed errors from 9bca705 2023-04-10 12:30:51 +05:00
Andrej730 8b302f8d21 Make BIM Tool alignment buttons clickable and some hotkeys code cleanup 2023-04-10 11:22:17 +05:00
Dion Moult dfeebd6b6b Fix #2960. 2023-04-10 09:01:34 +10:00
Massimo Fabbro df53ce9f6e Better fix about cls without is_registered method 2023-04-09 22:33:37 +02:00
Massimo Fabbro 9bb2c00b6d Fix bug related to operators without is_registered method 2023-04-09 21:58:11 +02:00
Dion Moult 641253c4c6 Document list now shows description and locations instead of names as names are inherited. 2023-04-09 23:03:10 +10:00
Dion Moult 6ce49b7130 See #2516. References to titleblocks, layouts, and published sheets are now explicitly stored. Titleblock paths are now configurable. Generated filenames are sanitised. 2023-04-09 23:03:10 +10:00
Andrej730 804ad0cece Prevent flickering for SectionLevel and Grid decorators 2023-04-09 18:03:08 +05:00
Dion Moult 6c60d14c05 Fix #2959. Oversight when disabling mesh edit mode. Edit grid axes should be allowed. 2023-04-09 12:52:10 +10:00
Dion Moult def2d2bcc9 See #2516. WARNING: breaking changes! Restructure drawing system to use relative paths based on IfcDocumentReference Locations. 2023-04-08 21:59:53 +10:00
Andrej730 9bca705dc3 Solved producing orphan objects data on drawing deletion 2023-04-08 11:37:35 +05:00
Andrej730 2dcc271dff Updating sheet name from UI #2950 2023-04-07 19:06:41 +05:00
Thomas Krijnen 33415fa1af #2685 Fix AttributeError 2023-04-07 16:02:11 +02:00
Andrej730 10315e0e29 annotation and gizmo shaders rework for smoothed lines without bgl #2897
Reworked annotation and gizmo shaders to work without `bgl` and support smooth lines.

Moved LIB_GLSL and DEF_GLSL to shaders module since it's basically the same code and this way it'll be easier to keep track of it.
2023-04-07 17:36:58 +05:00
Dion Moult f321cc81c8 Minor improvement to add error message when mirror elements called without a mirror axis. 2023-04-07 20:07:38 +10:00
Bruno Perdigão 68b685f580 minor fix, sets a minimum to 'count' 2023-04-07 19:58:33 +10:00
Bruno Perdigão c87c79b937 Fix #2937. IFC Array now have the option to sync children 2023-04-07 19:58:33 +10:00
Sigma Dimensions de3dfcdc43 BBIM 4D Features to:
- Display nested task resources/task inputs of the active task
- Show list of tasks related to the current object selection
- Highlight, in the WorkSchedule panel, tasks  related to the current object selection
2023-04-07 04:33:56 +02:00
Kristoffer Andersen 657ddc0fa1 conda daily pin occt version 2023-04-06 21:38:42 +02:00
Dion Moult 1da01489f1 Fix #2935. Duplicating objects clear any parametric array relationships. 2023-04-06 21:18:36 +10:00
Massimo Fabbro 64a27614ff Fix bug #2825 2023-04-06 21:12:18 +10:00
Dion Moult 6c5acfc8bb Fix #2945. Selector should expand enumerated list properties so that you can match any of them. 2023-04-06 19:21:22 +10:00
Andrej730 884ee5b587 Warning when user loading sheets and some svgs are missing
Added this warning because missing svgs can lead to errors later on.
2023-04-06 13:51:12 +05:00
Andrej730 c2320a3c1e ChangeSheetTitleBlock operator
Added operator to change title block of currently selected sheet, it's located right after "Add sheet" button.
The way it works - you select the sheet, select the desired titleblock from dropdown menu and it overrides current titleblock from the sheet with the new one.
2023-04-06 13:51:07 +05:00
Bruno Perdigão ab23030051 ifc array modifier: get cursor position in relation to the object local axes 2023-04-06 10:45:47 +10:00
Bruno Perdigão 699c692f6c ifc array modifier: adds the option to get the dimensions from the 3d cursor 2023-04-06 10:45:47 +10:00
Bruno Perdigão 0c85e26950 ifc array modifier: bugfix, prevent divider from being zero. 2023-04-06 10:45:35 +10:00
Bruno Perdigão 2cd7e737cd ifc array modifier: adds the option to choose between "total" and "increment" as dimension input 2023-04-06 10:45:35 +10:00
Gorgious56 a1adbe2b34 You can now change the section line decorator width in the UI 2023-04-06 09:37:01 +10:00
Gorgious56 a144c2b7ea Add new shader to display the section as a thick line on the mesh 2023-04-06 09:37:01 +10:00
Gorgious56 201c056ffa Add new decorator utility to the Compare node group 2023-04-06 09:37:01 +10:00
Andrej730 2e95168610 Title case naming for operators 2023-04-05 17:49:58 +05:00
Andrej730 b18c05cbb9 Open all drawings at once with shift+click (similar to print all)
Also added filepath check to make sure all drawings were printed first.
2023-04-05 17:47:27 +05:00
Andrej730 102a4c6110 Title case naming for operators 2023-04-05 17:47:27 +05:00
Dion Moult b6c0ef3d19 Fix #2936. Unassigning a type now also unmaps representations. 2023-04-05 22:33:11 +10:00
Andrej730 ea3065dbc2 Sync viewport visibility based on EPset_Drawing/Include Exclude #2613
It's not real time, it's triggered on using "Activate view" button in "Drawings" section.
2023-04-05 16:34:43 +05:00
Gorgious56 534c3ad329 Fix temporary section cutaway socket inputs/outputs not initializing properly
Related to #2547
2023-04-05 13:30:35 +02:00
Dion Moult 90a7d807a1 Fix #2938. Unlinked objects should no longer synchronise edits after they've changed. 2023-04-05 21:07:44 +10:00
Dion Moult 4baafb5e2f See #2938. Fix issue where unlinking didn't consider other users of the object's data or material. 2023-04-05 20:58:24 +10:00
Andrej730 f911765cdb Support for SECTION_LEVEL annotations description in viewport
Example - https://i.imgur.com/ATF9Fp6.png
(similar to svg)
2023-04-05 14:44:27 +05:00
Andrej730 d0a36c79d5 Small fix 2023-04-05 13:10:41 +05:00
Andrej730 ffea5320a0 Background for Ifc Text annotations in SVG
Added option to fill background of Ifc Text annotation with white color.

To make it work need to add "fill-bg" class to EPset_Annotation - https://i.imgur.com/ig2EhkQ.png.

Result - https://i.imgur.com/mq4hzAM.png
2023-04-05 12:38:36 +05:00
Andrej730 0b54e41a40 Small editing assigned product UI fix
Previously checkmark didn't worked at all if the existing and new product were the same.
2023-04-04 19:02:07 +05:00
Andrej730 26c67f770a Sliding doors - more consistent 2d repr between door types #2919 2023-04-04 18:53:59 +05:00
Andrej730 f6623db0e6 Sliding doors - moved annotation arrow to Annotation/PLAN_VIEW #2919 2023-04-04 18:38:44 +05:00
Dion Moult d7844ee8e8 Fix #2114. Duplicate overrides are now defined as macros which means that repeating last function now works. 2023-04-04 22:01:45 +10:00
Andrej730 1c8970bbf2 removed redundant parts from generate_steel_profiles_library.py
removed never used methods and contexts
2023-04-04 14:23:21 +05:00
Dion Moult eb70849a30 Fix #2898. Explicitly check for changed active material in material UI panels. 2023-04-04 17:04:32 +10:00
Andrej730 77ce3c9067 Fixed ifc validation errors for generated openings #2925
Now ELEVATION_VIEW 3d curves converted to 2d to be extruded later on to create opening representation.
2023-04-04 11:18:29 +05:00
Dion Moult 56cd226a27 Deprecate mode subscriptiosn of profiles and walls 2023-04-04 13:59:10 +10:00
Dion Moult a1acdc0977 See #2881. Allow indices in the selector instead of relying on external evaluations. 2023-04-04 09:58:23 +10:00
Dion Moult 1a624c957b See #2916. Minor fix. 2023-04-04 09:43:58 +10:00
Andrej730 1c9eeb2aca Revert "Fixed ifc validation errors for generated openings #2925"
This reverts commit 66a53b486a.
2023-04-03 23:04:27 +05:00
Gorgious56 45f06eeea7 Interface now displays a hint when trying to edit a non-mesh-like object
& Fix case where the active object would go into edit mode even if not mesh-like when multiple objects were selected
2023-04-03 17:55:44 +02:00
Andrej730 66a53b486a Fixed ifc validation errors for generated openings #2925 2023-04-03 19:53:43 +05:00
Andrej730 fc45f5b48f Fixed ifc validation errors for furniture library #2925
Fixed bunch of validation errors with furniture library:

- IfcSpaceType PredefinedType wasn't optional
`<attribute PredefinedType: <enumeration IfcSpaceTypeEnum: (EXTERNAL, GFA, INTERNAL, NOTDEFINED, PARKING, SPACE, USERDEFINED)>> Not optional`

- some object had 0.0 depth extrusion
```
Rule IfcPositiveLengthMeasure_WR1:
    (self > 0.)
Violated by:
    (0.0 > 0.0)
```

- some orphan representations violating:
```
Rule IfcShapeModel_WR11:
    ((sizeof(self.OfProductRepresentation) == 1) ^ (sizeof(self.RepresentationMap) == 1) ^ (sizeof(ofshapeaspect) == 1))
Violated by:
    ((0 == 1 ^ 0 == 1) ^ 0 == 1)
     +  where 0 = sizeof(())
     +    where () = #9091=IfcShapeRepresentation(#15,'Body','SweptSolid',(#9090)).OfProductRepresentation
     +  and   0 = sizeof(())
     +    where () = #9091=IfcShapeRepresentation(#15,'Body','SweptSolid',(#9090)).RepresentationMap
     +  and   0 = sizeof(())
```
2023-04-03 19:53:43 +05:00
Andrej730 c7105a22e6 Fixed validation error for ELEVATION_VIEW curves #2925
Error occured because ShapeBuilder was assigning "Curve2D" representation type for elevation view curves when the correct type is "Curve3D".

It use to occur for both doors and windows created with ifc modifier.

```
Validation error text:
2023-04-03:18:21:28,879 ERROR   [rule_executor.py:154] On instance:
    #135=IfcShapeRepresentation(#21,'Profile','Curve2D',(#134))
Rule IfcShapeRepresentation_CorrectItemsForType:
    (IfcShapeRepresentationTypes(self.RepresentationType,self.Items))
Violated by:
    False
     +  where False = IfcShapeRepresentationTypes('Curve2D', (#134=IfcIndexedPolyCurve(#133,(IfcLineIndex((1,2)),IfcLineIndex((2,3)),IfcLineIndex((3,4)),IfcLineIndex((4,1))),$),))
     +    where 'Curve2D' = #135=IfcShapeRepresentation(#21,'Profile','Curve2D',(#134)).RepresentationType
     +    and   (#134=IfcIndexedPolyCurve(#133,(IfcLineIndex((1,2)),IfcLineIndex((2,3)),IfcLineIndex((3,4)),IfcLineIndex((4,1))),$),) = #135=IfcShapeRepresentation(#21,'Profile','Curve2D',(#134)).Items
```
2023-04-03 19:53:43 +05:00
Andrej730 e1fc7cad44 Fixed all unit.add_si_unit uses after fd87747
Now they were causing `TypeError: Usecase.__init__() got an unexpected keyword argument 'name'`
2023-04-03 19:53:42 +05:00
Andrej730 afeeaeb984 Fixed some validation errors for doors and windows #2925
Fixed the ones caused by using 3d curves as inner/outer curves in profiles.
2023-04-03 19:53:42 +05:00
Andrej730 2bc60b87f2 Fixed issue with tool.Blender.apply_bmesh 2023-04-03 19:53:42 +05:00
Dion Moult 4ad9ecb7db FixRevitTINs patch recipe now also purges non up-facing polygons as Revit doesn't handle them very well. 2023-04-03 21:28:34 +10:00
Dion Moult 3ec7b82b85 Fix #2930. Do not require entity applicability in IfcTester. 2023-04-03 21:28:00 +10:00
Dion Moult 48666def6b Fix #2910. Bug where style colours were not synced prior to duplication. 2023-04-03 20:16:43 +10:00
Chun 5732921fb6 a simplifier workflow and a more consistent coding style 2023-04-03 19:50:53 +10:00
Chun b86c11e1dc Unify variable 2023-04-03 19:50:53 +10:00
Chun 1bae4b219a Update functions of get_connected_to and get_connected_from compatible with IFC4 and IFC2X3 2023-04-03 19:50:53 +10:00
Chun 70ff73f805 Add IFC4 support for functions get_connected_to and get_connected_from in util.system 2023-04-03 19:50:53 +10:00
Chun a9636c5038 Add IFC4 support to get_connected_to and get_connected_from functions 2023-04-03 19:50:53 +10:00
Dion Moult a406b9f7ef Fix #2928. 2023-04-03 18:12:32 +10:00
Dion Moult f291345111 Fix #2924. IfcElements default to a null predefined type so they can inherit where possible. 2023-04-03 13:54:25 +10:00
Dion Moult c947ccb8be Fix #2926. Connections removed when rotating 90 deg to prevent unintuitive behaviour. Layer2 are also now recalculated. 2023-04-03 13:43:42 +10:00
Dion Moult 28c6b0d4bb Link to IfcArchitect tutorial series for quickstart video tutorials 2023-04-03 12:35:10 +10:00
Dion Moult 19317a9cc2 See #2881. Add support for evaluated commands in smart text literals 2023-04-02 21:25:06 +10:00
Dion Moult d67da6425e Fix #2927. Continuing to make mesh editing stricter. 2023-04-02 15:02:43 +10:00
Dion Moult 901f41358f Shortlist which IFC geometries may be treated as a meshlike object. 2023-04-02 13:45:05 +10:00
Dion Moult 061922aea2 Minor fix 2023-04-01 22:19:29 +11:00
Massimo Fabbro 5f7b6a5498 fix qto ui bug where objects with no assigned base qto doesn't allow to show the qto panel 2023-04-01 21:59:53 +11:00
Dion Moult 2ef9d5eed9 Fix #2922. You can now edit extrusion profiles of swept solids. 2023-04-01 21:27:20 +11:00
Dion Moult 9fba57d5db Disable edit mode on non mesh-like representations as well to prevent risk of invalid geometry. 2023-04-01 21:25:45 +11:00
Dion Moult e9074e3b3f Fix bug where generated openings invalidly used 3 dimensions instead of 2 2023-04-01 18:14:54 +11:00
Andrej730 f053ba714f Fixed bug in tool.Blender.get_bmesh_for_mesh
It caused errors because it was expecting mesh object to be selected - when it's not necessary that active_object is the one that mesh belongs to and sometimes no objects can be selected at all.
2023-04-01 12:12:34 +05:00
Andrej730 b6598f09a3 Fixed error with switching from EDIT mode for annotation meshes
Error occured when you were editing annotation mesh and tried to switched from EDIT mode

Error: Python: Traceback (most recent call last):
  File "\blenderbim\bim\module\geometry\operator.py", line 838, in invoke
    elif element.HasOpenings:
  File "\blenderbim\libs\site\packages\ifcopenshell\entity_instance.py", line 153, in __getattr__
    raise AttributeError(
AttributeError: entity instance of type 'IFC4.IfcAnnotation' has no attribute 'HasOpenings'
2023-04-01 12:00:07 +05:00
Andrej730 b9d7b95304 Revert part of 3a3d96be0 #2897
Reverted part of the bgl to gpu transition (to not scare away people with annotations without antialiasing🙂) until I work out the complete solution.
2023-03-31 19:56:26 +05:00
Andrej730 3db540f786 Fixed another typo related to #2889... 2023-03-31 19:50:22 +05:00
Andrej730 071b99c6c6 Fixed crash in Blender 3.5 on creating fill area annotation
Crash was caused by some change in Blender Python API in 3.5.
The code below resulted in `ValueError: bpy_struct: item.attr = val: MeshPolygon.vertices: array length cannot be changed to 4 (expected 0)`

```
obj.data.polygons.add(1)
p1 = obj.data.polygons[-1]
p1.vertices = (v1.index, v2.index, v3.index, v4.index)
```
2023-03-31 17:52:07 +05:00
Andrej730 3a3d96be0a bgl to gpu: profiles and openings decorations #2897
Still need to fix shaders.py and decoration.py
2023-03-31 16:38:24 +05:00
Andrej730 a03fbab4d5 Added description to some annotation operators 2023-03-31 11:06:21 +05:00
Andrej730 cc3b119288 Automatically adjusted STAIR_ARROWs now stop at the top thread
Previously they were ending at the end of the stair object and problem was that the end of the object could be at the slab underneath the stair.

Example - https://i.imgur.com/Y43v0GM.png
Before - https://i.imgur.com/Ze9DoK2.png
2023-03-31 10:59:39 +05:00
Andrej730 1a750d1b85 STAIR_ARROW annotations attached to IfcStairFlight object now readjusted after changes in IFC Stair modifier
Later probably will also need some other way to readjust annotations - so they can be readjusted for custom stairs, not created with modifier.
2023-03-31 10:59:39 +05:00
Andrej730 5b3ae9b9fd Clever STAIR_ARROW annotation
Now adding STAIR_ARROW annotation is a bit more clever - if you have IfcStairFlight selected when adding this type of annotation it will automatically:
1) define stair as it's parent;
2) add the stair as it's product;
3) place itself along the stair (it's assumed that stair direction is X+)

Example - https://i.imgur.com/Ze9DoK2.png
2023-03-30 18:41:55 +05:00
Andrej730 81471d80cd Fixed bug causing errors in IFC4X3 projects
It was causing an error below on opening BIM Tool and probably in some other places.

```
Traceback (most recent call last):
  File "C:\software\Steam\steamapps\common\Blender\3.5\scripts\startup\bl_ui\space_view3d.py", line 35, in draw
    self.draw_tool_settings(context)
  File "C:\software\Steam\steamapps\common\Blender\3.5\scripts\startup\bl_ui\space_view3d.py", line 48, in draw_tool_settings
    tool = ToolSelectPanelHelper.draw_active_tool_header(
  File "C:\software\Steam\steamapps\common\Blender\3.5\scripts\startup\bl_ui\space_toolsystem_common.py", line 809, in draw_active_tool_header
    draw_settings(context, layout, tool)
  File "\Blender\3.5\scripts\addons\blenderbim\bim\module\model\workspace.py", line 67, in draw_settings
    BimToolUI.draw(context, layout)
  File "\Blender\3.5\scripts\addons\blenderbim\bim\module\model\workspace.py", line 89, in draw
    AuthoringData.load()
  File "\Blender\3.5\scripts\addons\blenderbim\bim\module\model\data.py", line 52, in load
    cls.load_ifc_classes()
  File "\Blender\3.5\scripts\addons\blenderbim\bim\module\model\data.py", line 109, in load_ifc_classes
    cls.data["ifc_classes"] = cls.ifc_classes()
  File "\Blender\3.5\scripts\addons\blenderbim\bim\module\model\data.py", line 196, in ifc_classes
    + tool.Ifc.get().by_type("IfcDoorStyle")
  File "\Blender\3.5\scripts\addons\blenderbim\libs\site\packages\ifcopenshell\file.py", line 364, in by_type
    return [entity_instance(e, self) for e in self.wrapped_data.by_type(type)]
  File "\Blender\3.5\scripts\addons\blenderbim\libs\site\packages\ifcopenshell\ifcopenshell_wrapper.py", line 4499, in by_type
    return _ifcopenshell_wrapper.file_by_type(self, *args)
RuntimeError: Entity with name 'IfcDoorStyle' not found in schema 'IFC4X3'
Traceback (most recent call last):
  File "C:\software\Steam\steamapps\common\Blender\3.5\scripts\startup\bl_ui\space_view3d.py", line 35, in draw
    self.draw_tool_settings(context)
  File "C:\software\Steam\steamapps\common\Blender\3.5\scripts\startup\bl_ui\space_view3d.py", line 48, in draw_tool_settings
    tool = ToolSelectPanelHelper.draw_active_tool_header(
  File "C:\software\Steam\steamapps\common\Blender\3.5\scripts\startup\bl_ui\space_toolsystem_common.py", line 809, in draw_active_tool_header
    draw_settings(context, layout, tool)
  File "\Blender\3.5\scripts\addons\blenderbim\bim\module\model\workspace.py", line 67, in draw_settings
    BimToolUI.draw(context, layout)
  File "\Blender\3.5\scripts\addons\blenderbim\bim\module\model\workspace.py", line 92, in draw
    cls.draw_header_interface()
  File "\Blender\3.5\scripts\addons\blenderbim\bim\module\model\workspace.py", line 351, in draw_header_interface
    cls.draw_type_selection_interface()
  File "\Blender\3.5\scripts\addons\blenderbim\bim\module\model\workspace.py", line 367, in draw_type_selection_interface
    if AuthoringData.data["ifc_classes"]:
KeyError: 'ifc_classes'
```
2023-03-30 17:12:00 +05:00
Andrej730 eb14db4deb Fixed typo in ifcopenshell.util.type 2023-03-30 16:42:25 +05:00
Andrej730 50d989363a Added arrow to 2d repr of sliding doors (#2919)
Example - https://i.imgur.com/eJnhDXU.png
2023-03-30 16:36:55 +05:00
Dion Moult e52a99bd71 Load all type products by default (in preparation for IfcAnnotationType support) 2023-03-30 21:26:16 +11:00
Andrej730 a7a7c86657 IFC Door Modifier - sliding doors
Added support for sliding doors in IFC Door Modifier.

The main differences are 2d representation (https://i.imgur.com/VyEH569.png) and the door panel being placed before the lining, not after
2023-03-30 12:41:34 +05:00
Andrej730 59b8347082 Added option for oblique style for viewport dimension annotations
It's available after adding "oblique" to EPset_Annotation classes of the annotaiton.

Examples with and without it:
- https://i.imgur.com/JtgSeUK.png
- https://i.imgur.com/H7TPnfo.png
2023-03-29 16:37:00 +05:00
Andrej730 86883770b8 Fixed wrong text direction for plan level annotation
It occured when plan level spline direction was right-to-left.
2023-03-29 15:02:39 +05:00
Andrej730 d19c7d27a8 Cleaned up meshes produced during slab / arbitrary profile edit
Previously it was producing orphan meshes
2023-03-29 14:30:35 +05:00
Andrej730 eb35aad6e3 Added warning to bim.set_arc_index 2023-03-29 14:30:35 +05:00
Thomas Krijnen 5f952ecd00 Update ci.yml 2023-03-29 11:14:51 +02:00
Thomas Krijnen 74cd28d761 Update ci-py-only.yml 2023-03-29 11:14:41 +02:00
Thomas Krijnen 6e8049c43b Update ci.yml 2023-03-29 10:46:18 +02:00
Thomas Krijnen bbc61483ad Update ci-py-only.yml 2023-03-29 10:45:54 +02:00
Andrej730 db5315249c Fixed bug with slab editing decorator breaking on undo
Because of jumping between modes "bim.set_arc_index" doesn't remove newly created vertex groups on undo which resulted in error in decorator.

    File "\blenderbim\bim\module\model\decorator.py", line 134, in __call__
        if group_index in vertex[deform_layer]:
    AttributeError: BMElem[key]: invalid key, must be a BMLayerItem
2023-03-29 10:34:11 +05:00
Sigma Dimensions 4d34404e47 code clean up 2023-03-29 01:59:42 +00:00
Sigma Dimensions fa261b3281 Features to re-order cost items in a tree (changing parent & changing nesting order) 2023-03-29 01:49:55 +00:00
Sigma Dimensions 73feadcd66 Improved UI for adding cost item values 2023-03-29 01:38:21 +00:00
Sigma Dimensions da84165e13 Enable object selection from Cost item related products' list 2023-03-29 01:35:21 +00:00
Sigma Dimensions cc550c413b fix UI issue for loading product cost items 2023-03-29 01:31:09 +00:00
Sigma Dimensions 5b8b68457f ios utility to transform a human readable date or duration from string into objects 2023-03-29 00:47:05 +00:00
Sigma Dimensions 7850f8c718 You can now view & and highlight cost items assigned to an object, from Viewport Selection 2023-03-29 00:41:59 +00:00
Sigma Dimensions 3502b560f0 You can now select unassigned cost schedule products 2023-03-29 00:32:08 +00:00
Sigma Dimensions 53aa5e5fe0 You can now clear all cost assignements at once 2023-03-29 00:23:32 +00:00
Sigma Dimensions 674526d2df Show total elements/tasks/resources assigned to a cost item 2023-03-29 00:06:31 +00:00
Sigma Dimensions 384c9c9872 Refactor BBIM costing code, and re-use shared IOS utilies code 2023-03-28 23:28:00 +00:00
Sigma Dimensions 1170448cd0 New ifcopenshell cost utilities 2023-03-28 23:11:07 +00:00
Sigma Dimensions a7ec9c097a Enable displaying assigned nested resources for Cost Items 2023-03-28 23:09:07 +00:00
Sigma Dimensions 9b3b29a91c Enable displaying assigned nested tasks for Cost Items 2023-03-28 23:05:26 +00:00
Sigma Dimensions 16c4b212fd Enable displaying nested elements for Cost Item assigned Elements 2023-03-28 22:53:03 +00:00
Andrej730 50ba0815f7 Fixed UI for box alignment
Replaced checkboxes with icons and removed vertical and horizontal space between them, now looks neat.
2023-03-28 19:34:32 +05:00
Thomas Krijnen 8d3347c02b Update Dockerfile 2023-03-28 15:49:27 +02:00
Andrej730 fbd57a2bd7 Fixed array operator bug after 75f56462b (related to #2905) 2023-03-28 18:16:42 +05:00
Andrej730 75c98e5b00 Exiting current editing decorator if user switches from EDIT mode
Switching from EDIT mode acts like canceling editing which seems intuitive.

Now it's also possible to add similar thing to other use of decorators - beware of the possible Blender bug - mentioned it in `ProfileDecorator.install description`

Added this change to editing of profiles, extrusion axis, roof and railing paths, extrusion profiles.
2023-03-28 17:28:59 +05:00
Andrej730 c725eeb5db IFC Roof - small fix 2023-03-28 16:45:46 +05:00
Andrej730 75f56462b5 IFC Array - option to use local space instead of global (#2905) 2023-03-28 15:40:25 +05:00
Andrej730 f6ca12d3b8 IFC Text Multiple literals now displayed in viewport separated by newline 2023-03-28 14:58:12 +05:00
Andrej730 f6b7201bc4 Added default blender selection behaviour to CAD Tool
Similarly to BIM Tool (85bba9d)
Now it works exactly as default blender selection - supports box selection etc
2023-03-28 14:52:23 +05:00
Andrej730 ab618be49f Gable Roof support for roof modifier
How it works:
1) You add Roof Modifier to some object that represents roof footprint.
2) Start editing path (Shift-E in BIM Tool).
3) Then you can select any edge and specify roof angles manually for each edge (Shift-R in BIM Tool)
to make side gabled you just need to set angle to 90 degrees. Preview will update automatically.  You can set it back to 0 for angle to be defined automatically.

Some other changes:
- refactored decorator.py, in decorator now it's possible to supply some custom bmesh function that will be called on redraw, decorator now also can draw faces;
- changed colors for selected verts / edges to less bright, now it in more contrast to selected verts / edges color and seems much more readable;
2023-03-28 12:43:06 +05:00
Sigma Dimensions 5943e7e62e api usecase to re order related objects in a nest 2023-03-28 04:29:08 +00:00
Sigma Dimensions fde654aee7 api usecase to change nest 2023-03-28 04:27:47 +00:00
Sigma Dimensions 4880e1f9eb You can now select individual Task Inputs & Task Ouputs directly from the task quantity assignments. 2023-03-28 04:13:18 +00:00
Sigma Dimensions 8083a635a8 You can now import work schedules from csv 2023-03-28 04:04:48 +00:00
Dion Moult fab76bbcde Fixed bug where enumeration values in psets could be empty lists. 2023-03-27 23:08:47 +11:00
krande 7f78df595f minor clarification in comment 2023-03-27 13:35:24 +02:00
krande 03ff34f89f update ci-restart script with code that works and is testable locally 2023-03-27 13:35:24 +02:00
krande 8820a1dcc3 switch to a python implementation of the same procedure 2023-03-27 13:35:24 +02:00
krande 514975d123 add a workflow to catches and restarts failed conda builds due to 143 errors. And minor cleanup of unused variables in conda meta.yaml 2023-03-27 13:35:24 +02:00
Dion Moult c87911f00e IFC selector now uses "class" as a keyword to get the IFC class to disambiguate between "type" and "class". 2023-03-27 21:47:11 +11:00
Dion Moult 1e77562f0f Fix #2847. You can now join meshes and extrusions together. 2023-03-27 21:46:26 +11:00
Dion Moult b1843ce781 Fix IfcTester bug which did not consider null enumeration values 2023-03-27 17:36:06 +11:00
Dion Moult 02fb09ff7e Implement faux-mirroring. 2023-03-27 14:21:59 +11:00
Dion Moult bdb193f500 Minor fix 2023-03-27 13:38:14 +11:00
Dion Moult e9a3630595 Fix #2898. You can now copy materials from the materials tab. 2023-03-26 19:04:32 +11:00
Dion Moult fb3e4016ec See #2898. Fix bug where UI data was not refreshed when you switched active materials. 2023-03-26 18:07:10 +11:00
Trashman247 15a1aa5355 Move bl_descriptions
Moved bl_descriptions under bl_options when bl_options exists, copying the syntax of already existing operators with descriptions.
2023-03-26 17:46:33 +11:00
Trashman247 f1d6e95e51 Add tooltips to IFC Project Info and Clash Sets Panels 2023-03-26 17:46:33 +11:00
Dion Moult b9c99b35a5 Fix #2881. Support added for referencing nested relationships and material set items. 2023-03-25 22:07:18 +11:00
Dion Moult df0a503b89 Improve implementation of is_moved so that anywhere we check movement, types and projects are not considered 2023-03-24 21:59:09 +11:00
Dion Moult 346071185f Fix bug where appending a product from a library which had relationships to types caused a bunch of types to be pulled in unintentionally 2023-03-24 20:53:55 +11:00
Dion Moult f3f90e0ad5 Fix bug where mesh analysis during updating meshes as solids (i.e. extrusions) did not remove doubles, causing it to fail on sloppy meshes (and meshes are often sloppy) 2023-03-24 20:53:18 +11:00
Dion Moult 8aa2c1eea7 Fix critical bug where types would think they moved and cause long syncs at export time. 2023-03-24 20:52:20 +11:00
Dion Moult 57c32e55c2 Fix #2698. Bug where clash sets couldn't be visualised if you had one of the models open (i.e. not linked) in the active session. 2023-03-24 18:25:33 +11:00
Dion Moult de080a668f Fix #2901. Silly typo. 2023-03-24 17:48:27 +11:00
Dion Moult 00fc71dcbe Fix #2837. Shared subelements mistakenly caused the subelement append loop to break early. The solution is to traverse one level at a time. 2023-03-24 17:36:46 +11:00
Andrej730 808cb6ec14 Fixed outdated code issue 2023-03-24 11:00:08 +05:00
Andrej730 8899776106 Fixed #2900
1) Leaders text decorator wasn't changed after adding support for multiple literals
2) added drawing data update on toggling viewport annotations - so user would see correct values right away; also fixed similar thing on updating product
2023-03-24 11:00:08 +05:00
Andrej730 ae674b00c2 Fixed bugs after adding aggregated text annotations (#2899) 2023-03-24 10:09:23 +05:00
Dion Moult 73b6d23d4f Minor fix 2023-03-24 16:05:27 +11:00
Dion Moult 6d1cabe534 Fix #2887. Fix bug where you could create multiple opening objects. 2023-03-24 15:03:39 +11:00
Dion Moult ab34fda42c See #2887. Save mesh dialog now only shown when mesh is edited. 2023-03-24 14:33:13 +11:00
Dion Moult fab4133df6 Minor fix 2023-03-24 13:22:10 +11:00
Dion Moult 301e368a2c Minor fix 2023-03-23 22:09:31 +11:00
Dion Moult 0a22714fb8 See #2887. Experimental edit mode toggle override to ensure mesh changes stay in sync and so that you can edit meshes without openings applied. 2023-03-23 21:51:06 +11:00
Thomas Krijnen ef2cec7435 #2884 depends python3-pytest 2023-03-23 09:05:39 +01:00
Dion Moult b4f486e9aa Minor fix 2023-03-23 16:54:15 +11:00
Andrej730 297303063e Small changes in default names for saving ifc projects
1) changed first ifc saving handler from save_pre to save_post so that in the situation when you just created with .blend file and started ifc project on save dialog would suggest the same name for the .ifc file and for .blend file (previously it was always suggesting ".ifc" and you had to type it in manually).

2) suggested default ifc project named changed from `<blend_filename>.blend.ifc` to `<blend_filename>.ifc` for simplicity.

PS save_pre didn't really work anyway - by the time "save ifc dialog" file was invoked and appeared .blend file was already saved anyway.
2023-03-23 09:54:16 +05:00
Andrej730 df95bdb48e Fixing tests after 2003bfe4f 2023-03-23 09:53:02 +05:00
Dion Moult 95ac021623 Improve UI to indicate active representation. 2023-03-23 15:37:52 +11:00
Dion Moult 416efa4f14 See #2887. Fix bad UI design that could lead to objects created with the unintended context. 2023-03-23 10:44:57 +11:00
Ryan Schultz 2003bfe4fe Added_MODEL_VIEW_context
added...
context_type="Model", context_identifier="Annotation", target_view="MODEL_VIEW", parent=model
from: https://github.com/IfcOpenShell/IfcOpenShell/issues/2890
2023-03-23 08:00:39 +11:00
Andrej730 fc55a48c11 Automated adjustment of annotations scale if either camera orthographic scale or drawing scale changed 2023-03-22 15:56:55 +05:00
Andrej730 37a6be1e2b Ifc text viewport rotation for text with box alignment 2023-03-22 15:47:07 +05:00
Andrej730 e551bf531c Optimized ifc text viewport rotation algorithm 2023-03-22 14:37:48 +05:00
Andrej730 e173ea7f80 Ifc Text rotation in viewport preview 2023-03-22 14:05:03 +05:00
Andrej730 f8dc421f03 Fixed bug with multiline text in svg
Insertion position at <text> instead of <tspan> creates unnecessary x offsets between tspans
2023-03-22 11:04:43 +05:00
Andrej730 e2afb34e0d Fixed typo causing #2889 2023-03-22 10:09:38 +05:00
Andrej730 f7181aae8a Multiple literals in 1 Ifc Text + aggregated text annotations
Added option to store multiple literals inside 1 Ifc text object.

If multiple literals share the same `BoxAlignment` then only the first one will be displayed in viewport
to prevent visual clutter (although added small asterisk symbol to indicate that there are still multiple literals).

Added option for aggregated text annotations. Example - "door-tag" symbol.
If you create text, specify "door-tag" symbol in EPset_Annotation.Symbol
\and add two literals to it then they'll automatically will fit to that door-tag in the svg drawing
(https://i.imgur.com/OXaIlWe.png).

The way it works - you define symbol in symbols.svg (or in your own symbols svg file)
and then add the `<text>` fields where text from literals will fit right in.
The imporant note: `<text>` tag must have attribute `data-type="text-template"` for it to work.
For example: `<text text-anchor="middle" dominant-baseline="middle" data-type="text-template"></text>`
2023-03-21 17:37:28 +05:00
ArturTomczak 2a0fdd214c update the namespaces to current IDS schema 2023-03-21 20:37:37 +11:00
Dion Moult 45209be0ff You can now easily get a list of FMHEM classes and subtypes to list explicitly in IDS requirements 2023-03-21 17:15:59 +11:00
Dion Moult fcb3640bee IfcTester webapp can now render simple PartOf requirements 2023-03-21 17:13:34 +11:00
Dion Moult d503a4b50b Fix #2883. Regression in text in drawings. 2023-03-21 12:31:32 +11:00
Dion Moult 8bae2984e1 Fix #2886. Typo. 2023-03-21 09:11:30 +11:00
Andrej730 78499cc55d Fixed bugs related to "print all" (#2882) 2023-03-20 15:15:54 +05:00
Dion Moult b6aa5c57c0 Fix bug where copying types with parametric materials could lead to invalidly reused material set items. 2023-03-20 20:40:11 +11:00
Dion Moult d71e34f073 Fix bug where 2D types were not loaded as a fallback from the body representation. 2023-03-20 19:42:54 +11:00
Dion Moult a38d06d7e7 Remove dead code that manually synced drawing references. Now we have an automatic sync_references function instead. 2023-03-20 17:05:43 +11:00
Dion Moult 72b5cc8a44 Minor fix 2023-03-20 17:03:39 +11:00
Dion Moult c97e1079e8 See #2866. Fix issue where annotation should be able to use 3D coordinates (i.e. Model context type) for elevations and sections where it makes more sense. 2023-03-20 16:36:50 +11:00
Carlos Villagrasa 65caa7906d Merge remote-tracking branch 'upstream/v0.7.0' into pset_custom_units_test 2023-03-19 22:33:01 +01:00
Carlos Villagrasa d89a8520a9 Added a test for pset custom IfcUnits 2023-03-19 22:23:36 +01:00
Andres d68356c3a7 Rename class causing unregistering error 2023-03-20 08:21:11 +11:00
Andres cb0e8b6bf2 Add own sublogger for IfcSverchok 2023-03-20 08:21:11 +11:00
Andres b20b99669d Chore: add newline at end of file 2023-03-20 08:21:11 +11:00
Andres d41a277b71 Fix runtime error when unregistering add-on 2023-03-20 08:21:11 +11:00
Andres 33e6a0fdae Update logger to match Sverchok latest 2023-03-20 08:21:11 +11:00
Massimo Fabbro d911d8579d Add qto data file 2023-03-20 08:12:24 +11:00
Massimo Fabbro 05bdc962a3 Now it's possible to see the selected object related cost items and related quantities in qto n-panel 2023-03-20 08:12:24 +11:00
Sigma Dimensions 0e03a01f61 fixing previous commit #ff806dc 2023-03-19 14:11:32 +00:00
Sigma Dimensions ff806dc6e4 improve covering qto calculation 2023-03-19 13:30:35 +00:00
Thomas Krijnen 5fcfbac4a3 Update validate.py 2023-03-19 13:24:27 +01:00
Thomas Krijnen 00b18f67cf #2685 indeterminate upper bound formatting 2023-03-19 12:46:15 +01:00
Dion Moult 088354a96b Fix critical bug where copied representations would be inefficiently or incorrectly replicated. 2023-03-19 22:03:45 +11:00
Dion Moult cfd857233c Fix #2864. Fix bug where element containment was invalidly prioritised over voids and fills in relative placement. 2023-03-19 18:46:34 +11:00
Dion Moult 2816d7f522 Fix bug where deleting the drawing object could lead to orphaned relationships 2023-03-19 18:15:09 +11:00
Dion Moult 3da718a6d1 Fix bug where removing objects could leave orphaned annotation relationships 2023-03-19 15:17:26 +11:00
Dion Moult 558aff72a0 Psets and qtos are now only created when properties exist to prevent invalid IFCs. 2023-03-19 14:49:31 +11:00
Dion Moult bcc399b842 Empty psets are purged to discourage invalid psets with no properties 2023-03-19 13:13:15 +11:00
Andrej730 32e47d03d4 IFC text - box alignment in viewport 2023-03-18 11:56:29 +05:00
Carlos Villagrasa a669c194cf Merge pull request #2869 from cvillagrasa/api_material_fix
Api material fix
2023-03-17 17:04:22 +01:00
Carlos Villagrasa 91f4687eac Revert "Add more parameters to material.add_layer"
This reverts commit 9946117de1.
2023-03-17 17:02:35 +01:00
Andrej730 31f6baaf67 small refactor for Ifc.edit and tool.Ifc
1) replaced IfcStore.edited_objs.add with tool.Ifc.edit
2) updated interface in tool.py
3) removed unlink from tool.Ifc - it was declared there twice
2023-03-17 17:50:56 +05:00
Andrej730 a83c4b2896 Stair, roof, railing - auto updating representation when creating one or update it
So basically there should be no need to manually update representation after you created one of the objects from ifc modifiers.
2023-03-17 17:32:36 +05:00
Andrej730 c685425667 Fixed bugs with text annotations after
1) Bug when new text added without specifying it's font size in class appeared empty;
2) Bug when leader text appeared weirdly bold.
2023-03-17 17:02:42 +05:00
ay-ex 553334d50e add imports to snippet
added imports and syntax highlighting so it is easier to run for newcomers
2023-03-17 20:33:15 +11:00
Andrej730 0b7b0a29a9 Solved redundant spaces in multiline svg text annotations (#2833)
The issue occurs because of svg pretty formatting - indent spaces <text> tag treats as actual spaces. Because of that use some css magic to remove those spaces from <text> tag and assign class to it's children <tspan> tags.

Altough trying to solve this issue I've tried to disable svg pretty format and got empty svg as a result (only templates, styles, no annotations) for some reason.
2023-03-17 11:05:27 +05:00
Andrej730 ae7f834a0f draw_text_annotation refactor
Refactored a code a bit and replaced literal parsing with `text_obj.BIMTextProperties.text` since it's already done beforehand in `tool.Drawing.update_text_value()`.
2023-03-17 11:05:26 +05:00
Andrej730 fe0a98a44f black format 2023-03-16 16:08:58 +05:00
Andrej730 35e748089b Prevent pyrna_enum_to_py warnings on using BIM Tool hotkeys
Type of warning fixed:
WARN (bpy.rna): pyrna_enum_to_py: current value '0' matches no enum in 'BIMModelProperties', '', 'ifc_class'
2023-03-16 16:08:58 +05:00
Andrej730 ff2cda7d18 Small refactor + allowing to preview text in viewport while editing 2023-03-16 16:08:58 +05:00
Andrej730 0d473fa9ac Fixing BoxAlignment value in tests (similar to 9f7db386a) 2023-03-16 16:08:58 +05:00
Andrej730 d8eb765906 BIM Tool - hotkey for editing text annotation
Added hotkey in BIM Tool for editing active text annotation (It's binded on Shift + E).
2023-03-16 16:08:58 +05:00
Carlos Villagrasa fab7b590f7 Allow for custom IfcUnits when editing psets 2023-03-16 20:39:32 +11:00
Andrej730 c9f52d2f8e Option to print all svg drawings #2794
It's working either if you shift click the printing button or supply `print_all = True` argument to `bim.create_drawing operator`.
2023-03-16 11:35:31 +05:00
Andrej730 821993d289 Fixed accidentally removed line in ee1e40171 2023-03-16 10:11:26 +05:00
Andrej730 2d32bc609a Figured a new way to update viewport to preview changes in ifc text 2023-03-16 10:11:26 +05:00
Andrej730 296cec02e8 Removed outdated code from BIMTextProperties 2023-03-16 10:11:26 +05:00
Andrej730 b55ac88c9d Fixing tests after 9f7db386a
TestExportTextLiteralAttributes and TestImportTextAttributes was failing because now we're assuming that BoxAlignment will have 1 of values from the list below and not "BoxAlignment"

"top-left", "top-middle", "top-right", "middle-left", "center", "middle-right", "bottom-left", "bottom-middle", "bottom-right"
2023-03-15 19:43:17 +05:00
Andrej730 c6d3598c52 Fixing tests after e9fdc979d 2023-03-15 19:43:17 +05:00
Andrej730 c4d2b30017 black format 2023-03-15 19:43:17 +05:00
Andrej730 ee1e401719 Ifc Text - drop down menu to switch font size
Added drop down menu to Ifc Text section in object properties where you can change text's size and it will automatically change it in `EPset_Annotation.Classes`. You can also preview it in viewport and it will write in IFC only after you finish editing.

Still need to investigate `annotation.Annotator.resize_text(context.active_object)` in `refreshFontSize`.
2023-03-15 19:43:16 +05:00
Andrej730 3c2ef518e8 IfcText - box alignment interface instead of manually typing text position
realigning box widget
2023-03-15 19:43:16 +05:00
Andrej730 f3e327e088 Added classes description for EPset_Annotation 2023-03-15 19:43:16 +05:00
Andrej730 be1d4e06de IfcText - different text sizes in viewport 2023-03-15 19:43:16 +05:00
Dion Moult 32f5a77d07 New shape utilities for getting top / bottom elevations and bbox. 2023-03-15 18:51:31 +11:00
Dion Moult 9121a6b864 Revert "Fix #2418. Object materials are now found in the materials tab."
This reverts commit 7666463751.
2023-03-15 18:30:11 +11:00
Carlos Villagrasa 9946117de1 Add more parameters to material.add_layer 2023-03-15 08:27:46 +01:00
Carlos Villagrasa ee85055d45 Fix bug in which layer.Material was erased 2023-03-15 08:27:27 +01:00
Andrej730 cce0867f63 IfcText - spacing in multiline text now depends on text size #2833 2023-03-14 19:38:15 +05:00
Andrej730 021321eb65 IfcText - support multiline text in viewport preview 2023-03-14 19:38:15 +05:00
Andrej730 6bd1b84b2b IfcText to appear on top of object properties stack
1) Puts IFC Text only on top of ifc panels to make it easier to change it's text
2) I guess it could be a good practive to set `bl_order = 1` by default so it will be possible later to prioritize something since by default blender sets `bl_order = 0` and then even `bl_order = -1` wouldn't help.
3) May not work in previously created files
2023-03-14 19:38:15 +05:00
Massimo Fabbro f5281a0895 Delete unuseful function 2023-03-14 11:24:37 +11:00
Massimo Fabbro 08fdf6014d weight calculator improvement (see #2859) 2023-03-14 11:24:37 +11:00
Andrej730 94f06d9e18 Another fix for dissolve limit in hiped roof generator... 2023-03-13 17:05:08 +05:00
Andrej730 8405ee4926 Fixed bug dissolve limit in generate hipped roof (e69452883)
Now it will work fine in both edit and object mode
2023-03-13 11:28:36 +05:00
Massimo Fabbro 154c4295df Finish mapping mappable quantities with get_weight 2023-03-13 13:18:02 +11:00
Massimo Fabbro 93519c1c63 Now is possible to calculate an object gross weight 2023-03-13 13:18:02 +11:00
Dion Moult 1250a20e35 Fix #2858. Fix #2856. BlenderBIM Add-on drawings and IfcCSV now support the new quoted and escaped selector capabilities. 2023-03-13 11:09:56 +11:00
Thomas Krijnen 9ec54228f8 Update ci-py-only.yml 2023-03-12 12:01:12 +01:00
Dion Moult 3f94184ace Fix bug where IfcURIReference properties wouldn't display 2023-03-12 21:55:07 +11:00
Thomas Krijnen 88c06f5450 Try different way of invoking ccache 2023-03-12 11:46:25 +01:00
Thomas Krijnen e50045bbed Update ci.yml 2023-03-12 10:06:32 +01:00
Thomas Krijnen 41417a77b9 Update ci-py-only.yml 2023-03-12 10:06:29 +01:00
Thomas Krijnen a5774bd02a Update ci-py-only.yml 2023-03-12 09:40:30 +01:00
Thomas Krijnen c38f48ee62 Try to let pip not install outdated ifopsh 2023-03-12 09:04:33 +01:00
Dion Moult 30c850e37d Fix #2140. Voids and fills can now be added, removed, and edited for non-geometric aggregates. 2023-03-12 12:52:30 +11:00
Dion Moult b6fdbd979e Adding existing objects as openings no longer move to cursor which is more intuitive 2023-03-12 12:52:30 +11:00
Thomas Krijnen 7a8bcbee33 #2685 poly curve test cases 2023-03-11 20:44:59 +01:00
Thomas Krijnen 42d7c7d32c #2685 Recompile rule 2023-03-11 20:44:11 +01:00
Thomas Krijnen c71a5a4d65 #2685 Indexing of simple type aggregates; fix index codegen bug; sizeof of indeterminate; repeat over indeterminate 2023-03-11 20:43:00 +01:00
Andrej730 e69452883f Temporarily commented out dissolve_limited in generate_hipped_roof
Currently it causes an error if you run it in object mode and doesn't really work in edit mode either because you may have no vertices selected. Will fix soon.
2023-03-11 20:47:41 +05:00
Sigma Dimensions 8624888bfc You can now specify custom names for types instead of relying on the default "TYPEX" naming 2023-03-11 14:13:56 +01:00
Andrej730 fad5c7c921 Section annotation - custom markers / toggle displaying the markers
Now it's possible to set custom markers in section annotation.

To do so you need to add BBIM_Section pset to the annotation and then you can either toggle displaying some marker with ShowStartArrow/ShowEndArrow or you can write the id of some custom symbol in StartArrowSymbol/EndArrowSymbol (need to make sure the symbol is included in symbols.svg).

Other changes:

- removed vertex sorting in decoration.py so that start and end points will be the same thing both in svg and in viewport representation (previously the sorting was needed of the direction the arrow in shader wasn't inverted));

- fixed bug when in svg section arrow symbols wouldn't rotate if you move individual verts of the annotation, not the entire object (I guess this bug sneaked in after adapting elevation annotation)
2023-03-11 18:02:16 +05:00
Dion Moult 0dad93bf00 See #2140. You can now bulk unassign aggregates, and they are auto directly contained in any previous indirect containers. 2023-03-11 16:31:23 +11:00
Dion Moult c51aa910cb See #2140. You can now select multiple objects and bulk add them to a single aggregate, and customise the aggregate class. 2023-03-11 16:31:23 +11:00
Andrej730 6d486aeb4d Removed debug print 2023-03-11 10:30:24 +05:00
Dion Moult 7041a4a4a9 Fix #2850. Splitting walls now reassigns voids and fills to the appropriate split portion. 2023-03-11 15:08:26 +11:00
Andrej730 381570cd9e Decorators - a bit more consistent geometry parsing between edit/object modes 2023-03-10 19:26:08 +05:00
Andrej730 c9f93b2316 Section annotation option not to draw full line between the markers
Here's the example - https://i.imgur.com/db09OVF.png

To make it work need to add BBIM_Section property set to the annotation and set "HasConnectedSectionLine" to False (by default it's True).
Also added preview for this feature in viewport.
2023-03-10 19:26:08 +05:00
Dion Moult 3706ecc5f2 Fix #2689. Fully disable wall sketches until we come up with a proper UX for it. 2023-03-10 21:21:03 +11:00
Dion Moult 51ad51036e Generated spaces now remember height and origin represents recalculation point. 2023-03-10 19:48:49 +11:00
Dion Moult 3edbcb9ff8 Fix #2849. Brick.ttl is now bundled. 2023-03-10 17:49:11 +11:00
Dion Moult 3e3c1699da Fix #1047. You can now set offsets to the iterator via Python. 2023-03-10 15:49:45 +11:00
Dion Moult 7666463751 Fix #2418. Object materials are now found in the materials tab. 2023-03-10 15:37:21 +11:00
Dion Moult c532b90c92 Minor fix. 2023-03-10 08:50:25 +11:00
Andrej730 7677733850 small fix for tool.Drawing.get_drawing_metadata
Otherwise it always returned a list with 1 empty string like `[ "" ]` which resulted in messy class names in svg like "-87IfcAnnotation0LtaLiEpzAOxEqmIvCV27EFALLSLOPEANGLE".
2023-03-09 18:50:05 +05:00
Andrej730 972d701801 Fall annotations
Added fall annotation - they appear both in viewport and svg.

There are three types of annotations (SLOPE_ANGLE, SLOPE_FRACTION, SLOPE_PERCENT) you can switch them by changing annotation's ObjectType to one of those values.

By default it uses angle representation.
2023-03-09 18:50:05 +05:00
Dion Moult cdee5532b6 Fix #2838. Bug where invalid 3D entity was used for 2D coordinates. 2023-03-09 21:01:54 +11:00
Dion Moult 38019288de Fix #2839. Bug where undo system could store edits of IFC primitives. 2023-03-09 18:18:56 +11:00
Dion Moult 3e2ca328d2 Get side area lets you choose the side axis now in util.shape. 2023-03-09 17:48:42 +11:00
Dion Moult 93b44b43a5 You can now get all materials including materials in sets via the util module. 2023-03-09 17:48:10 +11:00
Dion Moult 4ebcf8133c Fix #2435. The selector now supports regex filtering. 2023-03-09 11:07:28 +11:00
kzfile 13f8b183cb fix IfcSpace default style 2023-03-08 20:58:49 +01:00
Andrej730 8e5154e65e Minor fix for 6d524b632
Added get_viewport_context to interface too
2023-03-08 19:53:22 +05:00
Andrej730 6d524b632c Small fix for warnings " 'bim.cad_tool' not found for space"
Small fix to allow setting cad_tool even if operators were called not from viewport and to avoid warnings like "Warning: Tool 'bim.cad_tool' not found for space 'PROPERTIES'"
2023-03-08 18:42:36 +05:00
Andrej730 4ad3d65628 UI fixes for railing and roof modifiers
Changes:
1) Added editing railing / roof path as hotkey to bim / cad tools.
2) Added profile decorator
3) Some switching modes and tools for convenience
4) Fixed nasty bug in tool.Blender.apply_bmesh - method wasn't updating current mesh if it was in edit mode.
2023-03-08 18:22:28 +05:00
Dion Moult 4282014bfa Fix #2843. You can now get the bounding box centroid of geometry. 2023-03-08 20:58:21 +11:00
Andrej730 daee666735 Ifc Roof modifier
Added Ifc Roof modifier (also added it to shift-a menu and type manager). It works similar way to railing modifier - you define roof profile and then roof is generated.

Currently it has only 1 type of roof - HIP_ROOF (it utilizes bim.generate_hipped_roof operator).
2023-03-07 18:05:49 +05:00
Andrej730 5a6ae57833 Added ifc operators to some model operators 2023-03-07 09:43:05 +05:00
Andrej730 da8e5dced1 Added test to check #2839 2023-03-06 18:36:29 +05:00
Andrej730 fb38c68c7d Black format after adding railing modifier 2023-03-06 16:01:17 +05:00
Andrej730 2dc3c9b027 Railing - added to type manager 2023-03-06 15:47:55 +05:00
Andrej730 b057312ebf Ifc Railing modifier
Added Ifc Railing modifier (similarly to stair, window, door modifiers...).
You can start working with it either creating some edge based path and adding this modifier or by creating Railing for Shift+A menu (it will create some simple path to start with).

In railing modifier you can switch two edit modes - editing main railing properties and editing path. Editing path will show only the railing path, you can edit it and save it. You cannot edit the railing pass if you're not in this mode.
2023-03-06 15:47:55 +05:00
Thomas Krijnen d1217e61c4 #2822 svg: begin group lazily when data is actually being appended 2023-03-06 09:16:27 +01:00
Thomas Krijnen b521b68fd8 svg: compiler warnings 2023-03-06 09:15:27 +01:00
Thomas Krijnen c9302cde7c svg: prevent garbage data on empty path 2023-03-06 09:14:40 +01:00
Dion Moult 823e3c76eb Fix #2699. Bools and numeric filters are now handled in the selection UI. 2023-03-06 18:10:51 +11:00
Dion Moult 30d6289942 New utility to quickly get the grid north or true north angles. 2023-03-06 17:47:22 +11:00
Dion Moult a43a3d1ecd Fix #2745. You can now quote and escape complex pset names in the selector. 2023-03-06 17:47:03 +11:00
Dion Moult 9e0c5e9244 Fix #2835. Bug where in certain scenarios the decorator would crash due to prematurely freed bmesh. 2023-03-06 15:06:12 +11:00
Dion Moult a8f1df8a50 Fix #2836. Bug where very small lines could accidentally be seen as closed loops due to paperspace rounding. 2023-03-06 12:07:28 +11:00
Dion Moult fba84ecd3c Fix bug where invalid svg embedding was done when building sheets. 2023-03-06 11:52:55 +11:00
Dion Moult 8cc4edf60f Titleblocks now contain north symbol with dynamic angles. 2023-03-06 11:52:39 +11:00
Dion Moult e6eb455d71 Bump 2023-03-04 23:04:05 +11:00
Thomas Krijnen b84782e07f #2810 catch bad positioning warning and empty result 2023-03-03 14:32:02 +01:00
Dion Moult 2e3e0bef12 Minor fix 2023-03-03 22:27:47 +11:00
Dion Moult 51ff2f05bf Fix #2823. Only update colours if you actually change them. 2023-03-03 22:22:47 +11:00
Andrej730 42110e7f63 Added IfcPropertySingleValue to EnablePsetEditing to fix bug
Noticed bug trying to edit "Data" in psets from ifc modifiers and fixed it.

Traceback:
Error: Python: Traceback (most recent call last):
  File "\blenderbim\bim\module\pset\operator.py", line 94, in execute
    self.load_from_pset_data(pset)
  File "\blenderbim\bim\module\pset\operator.py", line 206, in load_from_pset_data
    metadata.set_value(value)
UnboundLocalError: local variable 'value' referenced before assignment
2023-03-03 15:36:14 +05:00
Dion Moult f0733cb148 Minor hotfix 2023-03-03 20:53:10 +11:00
Dion Moult 7383f44f8f See #2645. Update tests to use overridden built-ins. 2023-03-03 18:22:47 +11:00
Dion Moult 311a80955d Fix #2828. Bug where existing "TOP" connections were not removed if you tried to extend to a new slab / roof. Allow generic "top" connections to any LAYER3 element. 2023-03-03 17:59:20 +11:00
Dion Moult 08561b11c3 Fix #2077. You can now edit profiles via their axis line. 2023-03-03 17:47:16 +11:00
Andrej730 47192cc834 Fixed typo in generate_pset_templates.py 2023-03-03 11:08:56 +05:00
Dion Moult 860a0bfc8e Fix #2828. Extending walls to roofs / slabs is now more intuitive as you select the target slab last. 2023-03-03 14:42:08 +11:00
Dion Moult 5677768e99 Fix #2818. Alignment functions now considers layer set usage offsets. 2023-03-03 11:55:49 +11:00
Andrej730 4c58ca28e5 Synchronizing batting viewport and svg dimensions
Also switched to even more simple pattern to indicate insulation because of the vertex limit
2023-03-02 19:00:23 +05:00
Dion Moult 588d6ccaaa Attempt 2. 2023-03-02 21:51:09 +11:00
Dion Moult 21f583800a Ensure Python 3.10 is used to fetch BlenderBIM Add-on dependencies 2023-03-02 21:43:22 +11:00
Andrej730 4ea08a087c Minor array refactor 2023-03-02 14:39:36 +05:00
Dion Moult e5885db187 Get batting thickness from decorator data 2023-03-02 19:02:00 +11:00
Andrej730 1fca38625f A last bit of cleaning up temp files after tests 2023-03-02 10:45:00 +05:00
Sigma Dimensions cf2ad43ed7 refactor pset operator 2023-03-01 15:37:19 +01:00
Andrej730 fb3023bb79 Moved blenderbim tests temporary files to separate directory
Moved temporary test files to separate directory and added it to gitignore

Still have test.exp.cache.dat spawning from `test_feature`...
2023-03-01 18:46:13 +05:00
Andrej730 52f18fea85 Viewport preview for batting annotation
Currently it's always set to the same thickness and doesn't update from pset value.

It's currently using zigzag pattern to indicate batting because some techincal limitation of the shader (Error: C6033: Hardware limitation reached, can only emit 256 vertices of this size)

Also added batting to the annotation tool panel.
2023-03-01 17:37:17 +05:00
Dion Moult d5ecb9b8ea See #2820. Fix issue where some hatches weren't merged when an object had multiple SVG paths. 2023-03-01 22:25:12 +11:00
Dion Moult 67d33001d5 See #2820. Ensure only closed polygons are merged in drawings. 2023-03-01 21:57:33 +11:00
Dion Moult 4f571fa2fc Attempt to fix pytest bundling. 2023-03-01 21:39:15 +11:00
Dion Moult c5bd902dc6 See #2820. See #2822. Fix to accommodate empty element groups in drawings. 2023-03-01 21:08:34 +11:00
Dion Moult 68282c7a1e Fix #2821. 2023-03-01 20:54:07 +11:00
Sigma Dimensions d40dcae212 fix 5d csv imports with funky characters and numbers, fix 5d export 2023-03-01 10:51:52 +01:00
Dion Moult 12151c40ff Fix #2799. Drawing polygon merging now is purely material based. 2023-03-01 17:52:57 +11:00
Dion Moult 9c8b15fa1a Fix #2645. No longer override built-in operators for critical features. 2023-03-01 14:30:56 +11:00
Dion Moult 3d49cc9d13 By default, the site is now cut with a nice thick line on drawings 2023-03-01 11:28:45 +11:00
Dion Moult 955c0d19a6 See #2799. Cut walls with the same material in drawings are now merged by default. 2023-02-28 22:59:45 +11:00
Andrej730 3cf7a9e925 Renamed batting pset 2023-02-28 11:24:47 +05:00
Andrej730 8cbf719772 Replaced tool method from 17add48 with existing one 2023-02-28 10:04:18 +05:00
Dion Moult 2f614330c9 Fix #2781. Usability bug where it wasn't clear references were only loaded from the active external classification library. 2023-02-28 12:39:15 +11:00
Dion Moult 5a40519114 Minor fix 2023-02-28 11:43:36 +11:00
Dion Moult 96271e8bd8 Users can no longer create orphaned opening elements, which are invalid. 2023-02-28 10:05:22 +11:00
Dion Moult 9b75a415d4 See #2812. Resolve issue where obsolete link_to_scene argument was used. 2023-02-28 10:03:56 +11:00
Dion Moult 5f71abb5fa Minor fix to insulation scale 2023-02-28 09:23:28 +11:00
Dion Moult affb066b7b Fix #2813. Bug where the user could try to load thumbnails even though there were no types. 2023-02-28 08:44:36 +11:00
Andrej730 17add48254 Minor refactor for modifiers 2023-02-27 19:18:39 +05:00
Andrej730 f5b6fdcb5b Batting Annotation pattern
Added batting pattern to line annotations.
The way to use it is:
1) Add "Line" Annotation;
2) Change object type to "BATTING"
3) Add object property set "Pset_BBIM_Bating" (also added in this commit) to specify the batting thickness.

Currently there is no preview for batting in viewport.

Other changes:
- EPset_Annotation.ifc -> Psets_BBIM_Annotation.ifc (more general psets ifc library for the future use)
2023-02-27 19:18:39 +05:00
Dion Moult 68bdbe4c4f Bundle bpypolyskel so that users can start playing with the hipped roof generator 2023-02-27 21:49:04 +11:00
Dion Moult 363ddc2a9b Minor fix 2023-02-27 20:49:45 +11:00
Dion Moult 20d44378d9 You can now generate type-based spaces and regenerate spaces when boundaries change. 2023-02-27 20:49:38 +11:00
Dion Moult 4fe321275a Fix bug where space types were not loaded. 2023-02-27 20:47:20 +11:00
Dion Moult 60b1192703 Fix critical bug where a user might override and break existing objects if they reused an object name 2023-02-27 20:46:53 +11:00
Dion Moult b25233a748 Fix critical bug where duplicate orphaned collections were created in the background. 2023-02-27 20:12:39 +11:00
Dion Moult d9b54c923c Fix bug where you couldn't manage spatial types in the type manager 2023-02-27 20:10:27 +11:00
Dion Moult 131ee8270b Minor fix 2023-02-27 10:57:28 +11:00
Massimo Fabbro e71bace6a8 update makefile with shapely 2.0.1 2023-02-27 09:10:46 +11:00
Dion Moult 2c4a0669ed Fix bug where copying to container might get the wrong coordinates if containers weren't synced. 2023-02-26 22:31:21 +11:00
Dion Moult ee451b6b29 Experimental operator to generate hipped roofs from a footprint. 2023-02-26 21:23:35 +11:00
Dion Moult 7b858fffb2 All generated drawing text now has classes so you can customise their style. 2023-02-26 21:22:43 +11:00
Dion Moult 6ce0deeb63 Fix #2808. Bug where you couldn't assign element types in IFC4X3. 2023-02-26 11:26:13 +11:00
Dion Moult 96b2eaeae4 Experimental operator to generate spaces bounded by walls. 2023-02-25 21:40:02 +11:00
Andrej730 8fe8175a87 Monolithic stair variant generator 2023-02-25 10:40:27 +05:00
Andrej730 ee2bedfb64 Update openings position based on fillings position at wall regenarete
So basically now when you move windows or doors attached to the wall, their openings positions will also be updated if you hit "regenerate" for the wall.
2023-02-24 17:12:41 +05:00
Andrej730 aa8da74ed8 ifc2x3 pset library name fix + copyrights 2023-02-24 11:07:33 +05:00
Dion Moult f2c03a508f Fix #2600. Link back to exact docs folder in repo. 2023-02-24 16:21:21 +11:00
Dion Moult aa172488b0 Fix #2594. Document potential lag in PyPI releases of C++ core. 2023-02-24 13:35:46 +11:00
Dion Moult ae09adf854 Fix #2424. Bug where indirect assets (e.g. importing a type using a material) with relationships had their relationships duplicated 2023-02-24 13:01:26 +11:00
Andrej730 5b5dd73684 get_inverse_attributes - added more correct recursive unpacking for aggregates and selections
Not affected the result schemas though
2023-02-23 18:56:24 +05:00
Andrej730 3849a87adc Added simple tests for door and window modifier
This thing used to result in errors before we fixed `replace_object_ifc_representation`
2023-02-23 18:38:20 +05:00
Andrej730 708dc6aca2 IFC4X3/IFC2X3 pset templates library
Generated from xmls from the ISO release of IFC4X3.
Based on https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/annex/annex-a/general-usage/IFC4_ADD2.ifc

Also generated pset templates library for IFC2X3 (also based on similar xmls) but IFC2X3 ifc pset library is using... IFC4X3 schema because IfcPropertySetTemplate and IfcRelDeclares are not available in IFC2X3. Which is okay for our purposes for now.

Will need to update IFC4X3 library if one of the issues will be resolved:
https://github.com/buildingSMART/IFC4.3.x-development/issues/587
https://github.com/buildingSMART/IFC4.3.x-development/issues/586
2023-02-23 18:17:14 +05:00
Dion Moult 2fe2c349fd Fix #2743. obj2ifc scripts updated to support multiple OBJ files. 2023-02-23 22:17:34 +11:00
Dion Moult d396d32a8f You can now extend beams to walls. 2023-02-23 18:33:31 +11:00
Dion Moult 1fd02755f8 Fix BCF module bugs where certain BCF entities were assumed to exist. 2023-02-23 18:32:44 +11:00
Dion Moult 79d92b511b Fix #2442. Fix bug where viewboxes were not clipped when compiling drawing sheets. 2023-02-23 13:16:33 +11:00
Andrej730 ff7d93e66c Undo tests - proof of concept
Created some simple undo tests.

This "flip wall and ctrl-z" test actually is crashing blender if I do it manually - so I guess undo in background mode is still kind of different.
2023-02-22 20:24:43 +05:00
Dion Moult 68a8c7a235 Add basic integration tests for modeling slabs and beams 2023-02-22 21:50:12 +11:00
Dion Moult 5e744ff7d1 Add integration tests for basic wall modeling features 2023-02-22 20:38:02 +11:00
Dion Moult 921a185ffa Fix bug where natively loaded faceted breps didn't respect false origins. 2023-02-22 16:32:32 +11:00
Dion Moult 855751f026 Fix #2767. Removing representations now cleans up after orphaned Blender mesh data. 2023-02-22 16:24:24 +11:00
Sigma Dimensions 255c2efe98 Fix sequence data loading for unnamed items, ref #2783 2023-02-21 22:55:44 +01:00
Sigma Dimensions 892b282054 Fix loading task properties when calendar doesn't have a name, ref issue #2783 2023-02-21 20:23:07 +01:00
Dion Moult 26efc6b4e7 Fix bug where BIM tool hotkeys could activate operations without being in a correct state. 2023-02-21 21:54:53 +11:00
Dion Moult a0618c5827 Fix #2769. Bug where parametric UI was hardcoded to only work for walls. Now works on any generic parametric vertical layers. 2023-02-21 21:41:52 +11:00
Andrej730 d4bbb6e2c7 Added Model/Profile/Elevation_VIEW context as default for new projects #2784
Default Model/Profile/Elevation_VIEW context could be helpful to make openings created with BIM tool more correct.
2023-02-21 11:46:05 +05:00
Andrej730 66b7692227 Fixed windows/openings bugs
1) Window bug (#2792) - lining_offset wasn't converted from ifc units during window properties editing
2) Openings bug - OveallWidth/OverallHeight based openings was converted to ifc units even though their dimensions were already in ifc units.
2023-02-21 11:20:36 +05:00
Dion Moult 2429f507ad Fix #2785. Fix bug where invalid models from Revit that reused material usages could create invalid models. 2023-02-21 13:48:24 +11:00
Dion Moult 03a66d2806 Fix #2787. Fix creating invalid orphaned property and group relationships when removing systems / groups / elements. 2023-02-21 10:59:47 +11:00
Dion Moult 9d8551cb48 Demo library now has profile names to follow native IFC best practices 2023-02-21 10:14:06 +11:00
Dion Moult 2ca0b52c6a Fix failing test when editing a material profile with an unnamed profile 2023-02-21 09:51:30 +11:00
Massimo Fabbro 9cc754488b calculate all quantities smoke test 2023-02-21 09:12:30 +11:00
Massimo Fabbro f81ceb53cf add qto feature test 2023-02-21 09:12:30 +11:00
Dion Moult d1d0b5724d Revert "Solving test fail - keeping unnamed profiles when updating profiles in MaterialsData"
This reverts commit 0e1689c2d9.
2023-02-21 08:51:44 +11:00
Sigma Dimensions 40560b71c2 BBIM: fix material errors, and add smoke tests 2023-02-20 17:49:54 +01:00
Andrej730 378a25bbaa Added some .feature convenience debug fixtures 2023-02-20 19:57:05 +05:00
Andrej730 1dfabe2591 Fixed issue with float vectors assertion during tests
`sequence.feature` - Added `[:]` to make errors more readable values in errors (for example, (0.20000000298023224, 0.20000000298023224, 0.20000000298023224, 1.0) instead of `<bpy_float[4], Object.color>`

`test_feature.py` - fixed `FAILED test/bim/test_feature.py::test_animate_the_consumption_of_a_wall - AssertionError: Value is <bpy_float[4], Object.color>`
Test was failing because of float garbage in object.color (the comparison was (0.20000000298023224, 0.20000000298023224, 0.20000000298023224, 1.0) <> [0.2, 0.2, 0.2, 1] )

After that fix all tests now are passed 🥲
2023-02-20 19:53:16 +05:00
Andrej730 0e1689c2d9 Solving test fail - keeping unnamed profiles when updating profiles in MaterialsData
Solves test_enable_editing_material_set_item test error
```
FAILED test/bim/test_feature.py::test_enable_editing_material_set_item - RuntimeError: Error: Python: Traceback (most recent call last).

RuntimeError: Error: Python: Traceback (most recent call last):
  File "\blenderbim\bim\module\material\operator.py", line 587, in execute
    self.mprops.profiles = str(material_set_item.Profile.id())
TypeError: bpy_struct: item.attr = val: enum "107" not found in ('79', '98')
Location: C:\software\Steam\steamapps\common\Blender\3.4\scripts\modules\bpy\ops.py:113
```

The problem was `bim.enable_editing_material_set_item"` was trying to set `MaterialsData` profiles and wasn't able to set unnamed profile because `MaterialsData.profiles()` was discarding unnamed profiles (which is seems contradictory because `profiles()` is also handling unnamed profiles anyway).
2023-02-20 18:00:48 +05:00
Sigma Dimensions bd071cc472 Add smoke test for editing material profiles 2023-02-20 11:08:15 +01:00
Dion Moult dde847cb5a IfcTester can now output reports in BCF 2023-02-20 17:50:36 +11:00
Dion Moult 3c17718f70 Fix bug where adding a BCF viewpoint didn't take into account project units 2023-02-20 17:50:36 +11:00
Andrej730 0445119e77 Shifting dimensions defined openings by min_x and min_z to prevent #2784 2023-02-20 11:22:02 +05:00
Dion Moult e82cb5c149 IfcTester can now create reports in ODS format 2023-02-20 17:05:07 +11:00
Dion Moult 984548f315 IfcTester can now output reports in plain text 2023-02-20 17:04:38 +11:00
Dion Moult a0be4f58e6 Fix #2780. Fix bug where validation couldn't be run. pytest is now bundled. 2023-02-20 11:26:09 +11:00
Dion Moult 147602e107 Fix bug where ifcopenshell express rule validator required a logger with set_state. 2023-02-20 11:26:09 +11:00
Andrea Ghensi ac81169290 build(bcf): remove conda dependency 2023-02-20 07:26:34 +11:00
Dion Moult 1d78e05370 Fix #2774. Bump IfcOpenShell. 2023-02-19 21:53:49 +11:00
Andrej730 4b15b02ac8 Fixed Door modifier initial opening problem #2784
- add check of OverallHeight and OverallWidth for creating openings for doors and windows;
- OverallHeight, OverallWidth update for all the occurences of the type for doors and windows;
- new tool.Ifc method - get_all_element_occurences
2023-02-18 17:11:04 +05:00
Thomas Krijnen 57d18a0644 Update IfcIShapeProfileDef.cpp 2023-02-18 13:00:45 +01:00
Dion Moult 2f73af7e6a Fix bug where BlenderBIM Add-on BCF module was completely broken. Now works with latest BCF library for BCF-XML v2. 2023-02-18 21:12:36 +11:00
Dion Moult e712ef344d New feature in BCF to allow extracting specific files like header files, snippets, or bitmaps. 2023-02-18 21:11:05 +11:00
Dion Moult f91e9a3119 Fix BCF bug where viewpoints were not created with a .bcfv suffix which is not compliant. 2023-02-18 21:10:27 +11:00
Dion Moult 9cf36f1d91 Fix BCF bug where BCFv2 files were unable to be loaded 2023-02-18 21:09:32 +11:00
Andrej730 ab9a4856ab Update entity-types mapping for ifc4
Added IfcWindowStyle and IfcDoorStyle for suitable StandartCases since StandartCases inherit from IfcDoor and IfcWindow.
2023-02-17 17:57:17 +05:00
Andrej730 d52618277e Ifc4x3 - entity-types mapping
- added mapping entity-types mapping based on iso release of ifc4x3

- added some manual corrections for `generate_util_type_json.py` if someone would regenerate the map for ifc4.

- manual correction in ifc4x3 mapping for IfcCivilElement. It could be removed in the future releases of IFC if https://github.com/buildingSMART/IFC4.3.x-development/issues/583 is resolved

If curious to look what were the changes for mapping between ifc4 and ifc4x3:
https://github.com/IfcOpenShell/IfcOpenShell/commit/da51253fb0a9d08aa93f5fe3c074d3592b3aa997#diff-98f525c91bb47b9820e603fba61cc015d3e6ae7261593d727c51114e3ff73aa7
2023-02-17 17:57:16 +05:00
CyrilWaechter 743bd2c48f Add buttons to selected related boundaries
Related by a common:
- BuildingElement
- BuildingElementType
- Space
- SpaceType
2023-02-17 13:21:24 +01:00
CyrilWaechter 11894de455 Add button space to select its boundaries 2023-02-17 10:43:48 +01:00
Dion Moult 0c8122afc4 See #2762. Final final attempt. 2023-02-17 13:28:02 +11:00
Dion Moult f893503d0c See #2762. Final attempt. 2023-02-17 13:12:09 +11:00
Dion Moult 504782f156 See #2762. Attempt 3. 2023-02-17 12:53:55 +11:00
Dion Moult 5d1f32bfc0 See #2762. 2023-02-17 12:28:19 +11:00
Dion Moult 5bc315be92 See #2762. 2023-02-17 12:27:31 +11:00
Dion Moult e8f21c79b1 See #2762. 2023-02-17 12:03:25 +11:00
Sigma Dimensions ba24f9b491 - Improve ODS export
- Fix XLSX export bug
- Improve active cost item UI active cost item
- Fix missing tool cost function
2023-02-16 20:22:45 +01:00
Andrej730 937fa21cce Fix to prevent pyrna_enum_to_py warnings using BIM Tool
Prevents couple `pyrna_enum_to_py` warnings that occured using BIM Tool

```
WARN (bpy.rna): C:\Users\blender\git\blender-v340\blender.git\source\blender\python\intern\bpy_rna.c:1341 pyrna_enum_to_py: current value '0' matches no enum in 'BIMModelProperties', '', 'ifc_class_browser'
WARN (bpy.rna): C:\Users\blender\git\blender-v340\blender.git\source\blender\python\intern\bpy_rna.c:1341 pyrna_enum_to_py: current value '0' matches no enum in 'BIMModelProperties', '', 'relating_type_id'
WARN (bpy.rna): C:\Users\blender\git\blender-v340\blender.git\source\blender\python\intern\bpy_rna.c:1341 pyrna_enum_to_py: current value '0' matches no enum in 'BIMModelProperties', '', 'ifc_class'
```
2023-02-16 17:43:34 +05:00
Andrej730 16cbab2de7 Commented how to regenerate BIM Tool keymap in case of errors 2023-02-16 16:40:07 +05:00
Andrej730 a85f81d8ca Refactor - moved some helpers functions to tool 2023-02-16 15:16:42 +05:00
Andrej730 1617ede010 Simplified addings hotkeys descriptions in BIM Tool
Now it's more universal and doesn't require some single operator to be attached to hotkey description.
Also added description for some hotkeys
2023-02-16 14:36:24 +05:00
Dion Moult d9f1d501aa See #2762. Try again. 2023-02-16 15:01:19 +11:00
Sigma Dimensions 8ee60db0df Re-assigning a product type class should also reassign the class of its typed instances 2023-02-15 23:11:30 +01:00
Sigma Dimensions d2e02c8ab3 complete cost schedule exports commit 9dc69f3 2023-02-15 23:09:15 +01:00
Sigma Dimensions 2d4e4d2445 forgotten commit for duplicating tasks 2023-02-15 23:00:43 +01:00
Sigma Dimensions 9dc69f3aa7 You can now export Cost Schedules as CSV, ODS, XSLX with BlenderBIM or using the cli app 2023-02-15 22:50:39 +01:00
Andrej730 476ab506d0 Added ifc4x3 json schema and it's parser
API for docs works the same way as it worked for ifc2x3 and ifc4.

Besides changing `doc.py` and adding `.json` schemas added `ifc4x3dev_scrape_data_for_docs.py` (it's kind of hacky now and relies on `server.py` code from the further mentioned repo) that's used to parse some entities descriptions from https://github.com/buildingSMART/IFC4.3.x-development for later use in `doc.py`.
2023-02-15 18:38:04 +05:00
CyrilWaechter a6fa6201f7 Fix bugs introduced in cf87852 2023-02-15 10:34:25 +01:00
Dion Moult 26f7db0504 Fix #2698. IfcClash results are now aware of multiple linked IFC models. 2023-02-15 20:32:50 +11:00
Dion Moult 77a0da2a17 See #2762. Another attempt at fixing PyPI build. 2023-02-15 20:06:07 +11:00
Thomas Krijnen 785d2e601c Fix after 31cf1127a6 2023-02-15 09:52:21 +01:00
Massimo Fabbro 3687e4ed91 finish mapping Qto_FootingBaseQuantities 2023-02-15 09:35:29 +11:00
Thomas Krijnen 31cf1127a6 Validation output enhancements 2023-02-14 20:55:14 +01:00
Thomas Krijnen 97385efbca entity_instance.get_info(scalar_only=) 2023-02-14 20:11:24 +01:00
Andrej730 6a87b68c3b Window Modifier - added 2d representation
+ fixed lining offset for door 2d representation
2023-02-14 16:54:43 +05:00
Andrej730 be06bd6369 Added note about core.geometry representations functions 2023-02-14 16:54:19 +05:00
Andrea Ghensi a5b4ac863b fix: ifc clash operator fixes
fixes #2698
2023-02-14 21:55:41 +11:00
Thomas Krijnen e535f911d1 Update extrusion-dir cases for undeterminate handling 2023-02-14 10:04:46 +01:00
Thomas Krijnen 18670bcbd6 Recompile rules 2023-02-14 10:04:46 +01:00
Thomas Krijnen 27511d362e Use inverse unpacking in rule execution 2023-02-14 10:04:45 +01:00
Thomas Krijnen c634bffa10 Update reverse compilation to eliminate getattr() calls 2023-02-14 10:04:45 +01:00
Thomas Krijnen dffa90bd5d Better conformance wrt indeterminate with AST rewriting and dedicated type 2023-02-14 10:04:45 +01:00
Thomas Krijnen 6a47d33974 Add zone IsGroupedBy where rule test case 2023-02-14 10:04:45 +01:00
Thomas Krijnen e87524ba11 ifcopenshell.settings.unpack_non_aggregate_inverses 2023-02-14 10:04:44 +01:00
CyrilWaechter b1154ae377 Adapt to code changes introduced in ea91f43 2023-02-14 09:22:20 +01:00
Andrej730 cbc24c31a2 aud dependency fork fix
Solves #2759
2023-02-14 00:59:17 +05:00
Dion Moult 0a8a6e5825 Fix #2383. Don't default text alignment via CSS, let attributes take precedence. 2023-02-13 22:28:53 +11:00
Andrej730 e088a10aa8 New openings size wasn't converted using unit_scale #2765 2023-02-13 15:29:32 +05:00
Dion Moult 007d984baf Fix #2274. You can now purge unused types. 2023-02-13 15:20:56 +11:00
Dion Moult 7d979e5a04 Fix #2575. We are incompatible with add-ons using dictionary overrides for delete/duplicate macros. 2023-02-13 11:22:57 +11:00
CyrilWaechter 4134c709a8 Add fallback solution for invalid geometries
When an inner boundary cut an outer boundary in an IfcCurveBoundedPlane
which is invalid in IFC schema then ifcopenshell fails to create
geometry.
See #635
2023-02-12 18:29:24 +01:00
Thomas Krijnen ff5e4991ab Test files 2023-02-12 15:28:14 +01:00
Thomas Krijnen 0b4b45b7c8 Validate bounds of non-aggregate inverses 2023-02-12 15:28:14 +01:00
Massimo Fabbro 145ac1a0f4 Fix #2716 issue (#2761) 2023-02-12 14:09:33 +01:00
Dion Moult 74de7436b7 See #2762. Fix pip build. 2023-02-12 20:32:30 +11:00
Dion Moult 60fa05a913 Fix #2521. Fill areas have limited dissolve applied before rendering. 2023-02-12 18:58:49 +11:00
Dion Moult 303de61417 See #2760. Fix bug where editing pset with null'ed enumeration fails. 2023-02-12 15:12:31 +11:00
Dion Moult a9a4c6049f Fix bug where you couldn't duplicate text annotations. 2023-02-12 15:02:41 +11:00
Dion Moult 82b201a14e Fix #2352. Text styling is now possible with custom classes. 2023-02-12 15:01:47 +11:00
Dion Moult c9bd7adf8a Fix bug where you couldn't create a drawing with no linework. 2023-02-12 13:31:49 +11:00
Dion Moult 0655d77210 See #2760. Fix bug where groups UI had undefined variable. 2023-02-12 13:31:49 +11:00
Sigma Dimensions 515ec28fb3 Place text leader annotations horizontally 2023-02-12 01:07:41 +01:00
Sigma Dimensions c28c2dd077 BIM tool hotkey to update base quantities 2023-02-12 00:47:54 +01:00
Sigma Dimensions b04383bb64 Smoke test for duplicating tasks 2023-02-12 00:38:22 +01:00
Sigma Dimensions f4b93ef243 You can now duplicate tasks 2023-02-12 00:37:37 +01:00
Sigma Dimensions 05cf00bef6 Add smoke test for task product selection & minor fix 2023-02-11 23:24:52 +01:00
Andrej730 e81346c339 Door openings updated on door parameters update
Based on 6d1e9f73a
2023-02-11 19:01:56 +05:00
Sigma Dimensions ac911cefd6 minor fix for d4eb56e 2023-02-11 14:24:53 +01:00
Li1506 c4cea20849 add IfcConvert testing on sample files step to ci.yml 2023-02-11 13:25:50 +01:00
Sigma Dimensions e30992b905 minor fix for commit d3b176e 2023-02-11 13:23:23 +01:00
Sigma Dimensions 5beea8640e BBIM: rewrite Selector search filter to use core and tool functions 2023-02-11 13:19:08 +01:00
Sigma Dimensions d3b176e5eb complete costing module refactoring 2023-02-11 12:34:54 +01:00
Andrej730 0d0691a012 Door modifier - double doors type support 2023-02-11 16:09:13 +05:00
Sigma Dimensions d4eb56e360 New Search and Spatial selection Tools
+ refactor sequence modules
2023-02-11 11:23:07 +01:00
Andrej730 5cfc122b79 Door modifier - double swing doors
still no dashed lines in 2d representation
2023-02-11 14:20:04 +05:00
Sigma Dimensions fa0f484922 Inlcude audio library for BlenderBIM costing chaching sound 2023-02-11 10:05:54 +01:00
Dion Moult ceaa930aad Fix #2756. Bug where reassigning classes with a enum didn't work. 2023-02-11 11:03:35 +11:00
Dion Moult bbd2b67041 Add link to dev fund in add-on about section. 2023-02-11 09:54:43 +11:00
Sigma Dimensions 6c82e6f889 Fix assign cost value usecase 2023-02-10 21:28:49 +01:00
Thomas Krijnen 531e07767f #2754 latebound schema handling 2023-02-10 21:08:20 +01:00
Thomas Krijnen da39c903f3 Latebound schema handling: Improve error messages 2023-02-10 21:07:25 +01:00
Andrej730 f458aa9932 Door fixes
1) do not create new ifc representation if editing was canceled (similar to 25c650f4c)
2) plan and profile representations now  are optional (similar to 50d71d5ea)
2023-02-10 18:00:43 +05:00
Andrej730 9e383959c8 door black formatting 2023-02-10 16:58:36 +05:00
Andrej730 3654e49f87 Door handles 2023-02-10 16:55:13 +05:00
Dion Moult 13f5767921 Minor fix 2023-02-10 19:01:54 +11:00
Dion Moult 6d1e9f73a6 Windows now regenerate openings when window parameters updated 2023-02-10 19:01:20 +11:00
Andrej730 d9ddf67afb small fix for 50d71d5ea
The error was:
```
Error: Python: Traceback (most recent call last):
  File "\blenderbim\bim\module\model\window.py", line 337, in execute
    element = blenderbim.core.root.assign_class(
NameError: name 'blenderbim' is not defined
```
2023-02-10 12:49:33 +05:00
Dion Moult 47e6352350 Don't create temporary objs when updating filling openings. 2023-02-10 18:45:53 +11:00
Dion Moult ae564d1b61 Fix #1040. New utility for geometric shape analysis only requiring numpy. 2023-02-10 16:03:16 +11:00
Vukas Pajic d9cee04769 Fix #2755 2023-02-09 11:00:25 +01:00
Dion Moult 50d71d5ea1 Black code and make profile representation optional for windows. 2023-02-09 20:13:14 +11:00
Dion Moult 25c650f4cd Don't edit IFC when simply disabling window editing. 2023-02-09 19:33:08 +11:00
Andrej730 f0502c123e Window modifier - fixed bugs related to ae5ea3857
1) now replacing actual representation instead of the map
2) removed `should_sync_changes_first` as unnecessary representation update
2023-02-08 18:17:47 +05:00
Sigma Dimensions 693960a4ab start refactoring costing operators into core & tool 2023-02-08 13:39:46 +01:00
Vukas Pajic 5df0cf39a0 minor fix 2023-02-08 12:06:16 +01:00
Vukas Pajic 578220a1ab (IfcPatch) allow user to specify output directory for "SplitByBuildingStorey" 2023-02-08 12:04:58 +01:00
Andrej730 ae5ea3857e Fixing bug with updating window/door modifier with element types
The bug occured when you created a window/door type and some instance of it and then would try to update modifier.

There were two problems:
1) Because `remove_representation` removes all the meshes of the representation, representation has to be switched before removing the old one.

2) `switch_representation` parameter `is_global` was set to `False` and it wasn't affecting all instances of the type which led to errors during representation switch later on.
2023-02-08 15:56:57 +05:00
Dion Moult 93d0ffd459 Fix #2520. You can now duplicate drawings with or without annotations. 2023-02-08 21:16:54 +11:00
Vukas Pajic 1b85928810 allow user to specify output directory for "SplitByBuildingStorey" 2023-02-08 10:42:05 +01:00
Dion Moult f118c50d1d Fix bug where removing a drawing didn't remove its references. 2023-02-08 19:08:45 +11:00
Sigma Dimensions ac5ec72acf BBIM: improve work schedule visualisation workflow & refactor visualise_work_schedule_date operator 2023-02-08 08:25:11 +01:00
Sigma Dimensions 7fe2634d32 Improve guess schedule date range ( including tasks with input assignements) 2023-02-08 08:18:08 +01:00
Dion Moult 1279b9848f Fix regression with editing material psets 2023-02-08 17:43:50 +11:00
Dion Moult ea91f4302c Drawings now load on-demand instead of at project load. See #2520. 2023-02-08 16:53:19 +11:00
Dion Moult 88bddb9531 See #2520. You can now duplicate drawings. 2023-02-08 12:29:49 +11:00
Sigma Dimensions 4e308a6e31 fix api docstring description for nest.unassign_object usecase 2023-02-07 15:35:34 +01:00
Andrej730 4bd7fc5304 Simplified door ifc representation 2023-02-07 16:48:37 +05:00
Andrej730 0122fe4382 Fix #2742 2023-02-07 14:57:37 +05:00
Dion Moult 1730f25a5a Fix #2584. You can now edit any layered or profile material agnostic of class. 2023-02-07 19:13:22 +11:00
Dion Moult 3f8314bcf3 Minor fix. 2023-02-07 12:37:10 +11:00
Dion Moult 5879d14803 See #2737. Fix bug where convert length unit patch did not work outside Blender session. 2023-02-07 09:05:20 +11:00
Andrej730 4c1a821922 Pset_StairFlightCommon update
Added Pset_StairFlightCommon update to stair modifier
2023-02-06 18:54:25 +05:00
Andrej730 e1456533f2 Doors - clean up + black format 2023-02-06 18:53:48 +05:00
Andrej730 7522a3a2ab renamed "data" parameter in model/data.py
renamed "data" parameter in model/data.py to make it a bit more distinctive from neighbour "Data" parameter with json string
2023-02-06 18:45:48 +05:00
Andrej730 144d01a0ef Added modifiers folding to prevent clutter 2023-02-06 18:45:48 +05:00
Andrej730 f6cb6c8b21 Accepting predefined type for windows/stairs/doors from type manager
Also assigned layout name property for windows/doors
2023-02-06 18:45:48 +05:00
Andrej730 0f5264af99 Door - added to type manager
Also added predefined type for door and bit of refactoring
2023-02-06 18:44:09 +05:00
Andrej730 e57ff19e16 Door - added 2d representation 2023-02-06 18:44:09 +05:00
Andrej730 b2bdbdd8c3 Door modifier - basic setup
Also:
- refactored window modifier code - part of it is now reused building a door and can be used by other module in the future
- fixed bug with window lining intersecting frame when lining_depth < panel_depth
- simplified the way lining is built in ifc - previously it was using 4-6 extrusions, now it's 1-2 which is also better for 2D drawings
2023-02-06 18:44:09 +05:00
Andrej730 a8400ec963 geonodes prototype copyright 2023-02-06 18:44:09 +05:00
Sigma Dimensions d028b52d77 fix visualise_work_schedule_date - issue #2740 2023-02-06 12:35:42 +01:00
Sigma Dimensions 65990255e8 Fix issue #2740 2023-02-06 12:29:06 +01:00
Sigma Dimensions 3a71b03a9a Fix sequence module test bugs 2023-02-06 12:04:03 +01:00
Dion Moult 99274c2d7a Fix #1806. Changing filling types now also updates the void representation. 2023-02-06 20:28:50 +11:00
Dion Moult e40bbf2525 See #1806. Typed fillings now share the same representation for their openings. 2023-02-06 17:01:29 +11:00
Dion Moult 38c6378315 See #1806. New fillings inherit their voids from existing occurrences if possible. 2023-02-06 14:40:59 +11:00
Dion Moult c050ca1037 Fix #2736. Increase tolerance for HLR rendering to prevent "see through" objects in drawings. 2023-02-06 13:42:37 +11:00
Thomas Krijnen 9813dda778 Express rules: test case for instance comparison 2023-02-05 13:08:55 +01:00
Thomas Krijnen 6b7aeb90c7 Express rules: allow comparison of instances to instances and values 2023-02-05 13:08:55 +01:00
Dion Moult 3cb29e1344 Fix unintuitive UI where objects had to be visible for the operation to run. 2023-02-05 22:27:56 +11:00
Dion Moult 4d4961a490 Close #2622. IfcCSV now supports ODS and XLSX formats for export. 2023-02-05 18:53:16 +11:00
Dion Moult ccd7db06f8 Fix #2611. Address multiple situations in the UI where the users could create invalid material data without an indication that it is invalid. 2023-02-05 17:10:25 +11:00
Dion Moult 77234706b0 Assigning a profile set auto creates profiles if none exist to prevent invalid data. 2023-02-05 15:29:54 +11:00
Einar Raknes 9f399b34fb Rename panel from IFC Construction Type to IFC Type
As discussed here: https://community.osarch.org/discussion/1332/blenderbim-ifc-construction-type-ifcbuiltelementtype-confusion
2023-02-05 12:18:17 +11:00
Dion Moult dc77a798b5 Assigning a layer set for the first time prefills with a layer to prevent invalid data. 2023-02-04 22:14:33 +11:00
Dion Moult 207093fe99 Fix regression where changing assigned types didn't update the materials. 2023-02-04 22:12:28 +11:00
Dion Moult 42c4a68108 Fix bug where unnamed profiles clogged up the material profile settings. 2023-02-04 16:20:52 +11:00
Dion Moult a7cc58fb50 Update libraries to use Plan/Body/PLAN_VIEW instead of annotations. 2023-02-04 14:09:35 +11:00
Dion Moult 6bab10d2ac Minor fix. 2023-02-04 12:51:01 +11:00
Thomas Krijnen c4b77c2a07 Recompile rules 2023-02-03 22:22:40 +01:00
Thomas Krijnen 66d0627035 Property names test case 2023-02-03 21:18:06 +01:00
Thomas Krijnen 3f4417c9f2 Window lining test case 2023-02-03 21:17:48 +01:00
Thomas Krijnen 5526bed4c3 Express rules: allow indexing in sets 2023-02-03 21:17:08 +01:00
Thomas Krijnen 078bf8646a Express rules: enclose rhs of not in parens when needed 2023-02-03 21:10:34 +01:00
Dion Moult 6adbcdd8e1 Fix #2472. BREAKING CHANGE. Annotations are now drawn in addition to body views, not in lieu of. 2023-02-03 21:31:15 +11:00
Dion Moult b4d633a34d Fix #2735. 2023-02-03 21:28:36 +11:00
Dion Moult e1e4380fc7 Fix 3 bugs related to representations:
* Bug when switching representations when active object not selected.
 * Bug when switching to a non MODEL_VIEW body representation would load the MODEL_VIEW one instead all the time.
 * Add support for plan body representations.
2023-02-03 21:27:25 +11:00
Dion Moult 1ed7b21ab0 Fix #1496. Mirrored elements have their placements correctly synced. 2023-02-03 15:53:21 +11:00
Dion Moult 158a4c5e75 Fix bug where IFC2X3 fallback styles couldn't be assigned. 2023-02-03 13:21:23 +11:00
Dion Moult f72d48aab5 Fix bug where calendar caching meant invalid duration calculations if you edited your calendar after a date calculation. 2023-02-03 10:06:55 +11:00
Dion Moult be26a77ecb Support times specified as ISO strings too in date utils, not just datetimes. 2023-02-02 23:01:49 +11:00
Dion Moult 7c275a99a2 New utility functions for automatic local to global elevation conversions and easier unit conversions 2023-02-02 23:01:12 +11:00
Dion Moult f77d2f449d Close #2451. Implement georeferenced elevations in drawings. 2023-02-02 22:55:45 +11:00
Dion Moult 18ba805a15 Fix silly bug. 2023-02-02 21:36:13 +11:00
Dion Moult a5adf0021f Fix #2599. Unassigning materials now also removes the Blender material. 2023-02-02 18:41:07 +11:00
Dion Moult 7d944255d0 Fix bug where you could unassign a material usage and create an invalid parametric occurrence 2023-02-02 18:26:14 +11:00
Dion Moult b984a77862 IfcFM parser and writer is now easier to override for custom FM requirements 2023-02-02 14:29:48 +11:00
Dion Moult d69aa20802 Fix #2620. You can now run patches from models in memory. 2023-02-01 19:29:35 +11:00
Dion Moult 7e16cb797f See #1848. Some straggling refactoring for resource data. 2023-02-01 17:37:59 +11:00
Dion Moult b3c7971ea9 Purge all data classes from API. 2023-02-01 15:44:08 +11:00
Dion Moult a778b98fdb Fix #1848. Purge remaining references to API data classes. 2023-02-01 15:41:21 +11:00
Dion Moult 4efb80efe4 See #1848. Refactor structural data class. 2023-02-01 14:54:42 +11:00
Sigma Dimensions cfaacf8650 Ifcopenshell api usecase to unassign object nesting 2023-01-31 18:59:33 +01:00
Sigma Dimensions 60da4cb87b BBIM: Refactor activate drawing view 2023-01-31 18:42:18 +01:00
Andrej730 db6207db6c prevent flickering in profile manager thumbnails 2023-01-31 14:48:26 +05:00
Dion Moult 9b4568c881 Minor fix 2023-01-31 20:40:32 +11:00
Dion Moult cd132d4fe9 See #1848. Refactor sequence data class. 2023-01-31 20:38:03 +11:00
Andrej730 f01900bdb1 Profile manager thumnails (#2568) 2023-01-31 13:55:50 +05:00
Dion Moult 0fddd243c2 See #1848. Refactor resource data class. 2023-01-31 18:30:26 +11:00
Dion Moult 1066e5e0a7 See #1848. Refactor layer data class. 2023-01-31 17:35:54 +11:00
Dion Moult 7e0b1b9cbd See #1848. Refactor group data class. Add support for multiple subgroup relationships. 2023-01-31 15:35:14 +11:00
Dion Moult 95e924a5bc Fix #2206. Bug where removing a cost item with active data created an error. 2023-01-31 14:33:29 +11:00
Dion Moult f9cfd17303 Fix #2251. Fix #2603. Bug where annotations don't show because redeclaration of built-in glsl variables is not allowed. 2023-01-31 11:04:23 +11:00
Dion Moult 9f37f73e52 Fix #2585. Bug where geometry could randomly get corrupted. 2023-01-31 10:54:07 +11:00
Andrej730 9acd2043d7 Window/Stair types added as presets in type manager 2023-01-30 15:31:50 +05:00
Dion Moult ab6159f173 See #1848. Refactor cost data class. 2023-01-30 20:53:17 +11:00
Andrej730 f9bff64f0d ifc geonodes modifier prototype 2023-01-30 13:45:52 +05:00
Andrej730 978f362073 More consistent default and current units for stair and window modifiers 2023-01-30 12:44:11 +05:00
Dion Moult 7a185e7f7c See #1848. Refactor constraint data class. 2023-01-30 11:47:02 +11:00
Dion Moult 044cd6e97b See #1848. Refactor boundary data class. 2023-01-30 11:27:38 +11:00
Dion Moult b939370f2b See #1848. Remove reliance on data class for material module. 2023-01-30 10:20:55 +11:00
Sigma Dimensions 4af0c1c98c BBIM: Fix editing slab representation bug 2023-01-29 21:20:33 +01:00
Dion Moult 181531ada8 Fix bug where circle profiles didn't follow project units. 2023-01-29 21:30:11 +11:00
Thomas Krijnen e92d908063 Delete ifcopenshell_wrapper.py 2023-01-29 10:26:05 +01:00
Dion Moult 686f4a023d Fix #2356. Handle cases where the annotation types get changed to an incompatible object type. 2023-01-29 20:10:23 +11:00
Thomas Krijnen c0c6c23fca Update schema.py 2023-01-29 09:47:46 +01:00
Dion Moult 396fd5520f Fix #2349. Be more defensive on invalid smart annotations. 2023-01-29 15:15:00 +11:00
Dion Moult efc3a692cb Fix #2726. 2023-01-29 12:27:50 +11:00
Ryan Schultz 2902d96690 #2692 - bulk assign and unassign ifcmaterials 2023-01-29 10:05:03 +11:00
Massimo Fabbro 70f35095ff Writing qto tests (#2728)
* Update qto tool tests and fix some bugs

* complete test_run qto tests
2023-01-29 10:03:07 +11:00
Ryan Schultz 87c67abac9 #2652 Removes all IFC data, and converts the file to a simple Blender file 2023-01-29 10:00:43 +11:00
Thomas Krijnen 7df9640807 #2704 properly propagate iterator destructor 2023-01-28 14:21:52 +01:00
Thomas Krijnen 25edc91976 #2704 retain kernel pool to free cache 2023-01-28 14:21:52 +01:00
Thomas Krijnen 2174fda88d #2704 keep track of simple types by iden to free later 2023-01-28 14:21:52 +01:00
Thomas Krijnen 2fc5c6c110 #2704 newobject after decl does not work (duh) 2023-01-28 14:21:52 +01:00
Dion Moult 6a2307308c Fix bug where copying objects with profiles should only share profiles if the profile is named. 2023-01-28 21:51:00 +11:00
Dion Moult 1a05fae826 Fix #2426. bug where unrelated objects would get drawing properties by accident. 2023-01-28 14:27:47 +11:00
Andrej730 cce27d73fd Fixed ifc window bugs
1) The bug occured if you would try to remove ifc window modifier and add it back again. Now adding new window checks if window was previously added
2) Second bug occured if you import .ifc project with window and some props were not used before (for example window was without transom), then newly added props would have incorrect units.
2023-01-27 17:44:52 +05:00
Andrej730 87515f3839 Added creating window to Shift+A menu 2023-01-27 16:30:35 +05:00
Dion Moult 24f3918769 Fix #2705. Annotation draw error when splines are outside the zoom level of the screen. 2023-01-27 14:32:41 +11:00
Dion Moult f7af38c863 Fix #2712. 2023-01-27 12:09:17 +11:00
Dion Moult 6c2a4711da Fix bug where annotations were assumed to only be part of one drawing group 2023-01-27 08:38:00 +11:00
Dion Moult 41fdd77bfa Fix #2579. Bug where measurements were considered to be greasepencil strokes when drawing walls. 2023-01-27 08:32:25 +11:00
Dion Moult 9185a51234 Fix #2363. Bug when adding unbuilt schedule to sheet. 2023-01-27 08:22:10 +11:00
Ryan Schultz c5ae86d10b fix bug on Extend IfcBeam: https://community.osarch.org/discussion/1327/blenderbim-bug-on-extend-ifcbeam 2023-01-26 08:31:04 -06:00
Andrej730 74477d0051 Window modifier - removed hardcoded mm units
Related to #2724
2023-01-26 18:29:58 +05:00
Andrej730 cee0ff2074 BIM tool UI black format 2023-01-26 17:14:42 +05:00
Andrej730 85bba9d5f8 Added default blender selection behaviour to BIM Tool
Copied keymap templates from blender source and mentioned them

Related to #2568
2023-01-26 17:14:42 +05:00
Andrej730 8e696cbf02 Added description to some operators with hotkeys 2023-01-26 17:14:42 +05:00
Andrej730 c7d1f5efc0 Changed split hotkey to Shift-K (#2568)
to preserve useful default shift-s hotkey
2023-01-26 17:14:42 +05:00
Andrej730 68bbb4d7a1 Solving interface issues from #2568
1) Added type manager to N-panel. Actually made side-panel and header layout logic more similar.
2) Added interface from sidebar to properties too
2023-01-26 17:14:42 +05:00
Andrej730 b1dd20fef7 Fixed window units in bmesh (#2724) 2023-01-26 14:42:35 +05:00
Thomas Krijnen a701624d7b #2720 add gltf to CD yml 2023-01-26 09:07:53 +01:00
Thomas Krijnen f912be7875 #2720 add gltf to CD yml 2023-01-26 08:59:38 +01:00
Andrej730 04bdb12e79 Window Modifier - added mullions/transoms, removed relative panel dimensions 2023-01-26 12:30:34 +05:00
Dion Moult d69f6c02b6 See #1636. Fix drawing caching when new voids are added or parametric objects updated. 2023-01-26 12:14:45 +11:00
Dion Moult 95c3a57a4d Fix #2722. 2023-01-26 11:11:04 +11:00
Dion Moult 0b6d321d83 Fix #2721. 2023-01-26 11:09:51 +11:00
Dion Moult e0fd3fd90e See #1636. Fix bug where moving an opening didn't update the drawing cache for it's element. 2023-01-26 09:18:08 +11:00
Dion Moult 7686e35858 Fix #2697. 2023-01-25 20:52:43 +11:00
Dion Moult 8eeb8804d4 Handle corrupt geometry caches (e.g. due to crash) by creating a fresh cache. 2023-01-25 17:56:05 +11:00
Dion Moult cb9a7cf5be Fix #2712. Handle deletion of rel services buildings. 2023-01-25 17:42:39 +11:00
Andrej730 54cec30a60 Window modifier - fixed bug in bmesh 2023-01-25 11:15:01 +05:00
Andrej730 9f0ad9be3c Black formatting window modifier 2023-01-25 11:13:41 +05:00
Andrej730 a6271d0936 IFC Window modifier
Added IFC Window in object modifiers tab (works similar way to Ifc Stair)
2023-01-25 11:13:41 +05:00
Andrej730 cf6acd5dd6 ShapeBuilder - added 3d rectangles and polylines support 2023-01-25 11:10:20 +05:00
Andrej730 f8468e66e6 Fixed omitted ifc operators returns 2023-01-25 11:10:20 +05:00
Andrej730 d0cf5f9d0c Fixed ShapeBuilder bug for mirroring IfcExtrudedAreaSolid
Also fixed typo in shape_builder_examples
2023-01-25 11:10:19 +05:00
Dion Moult 2c123fdbfb Bump IfcOpenShell - mainly to fix geometry caching bug 2023-01-25 16:55:05 +11:00
Dion Moult 49b5bbd585 Fix PyPI IfcOpenShell action 2023-01-25 14:08:59 +11:00
542 changed files with 185692 additions and 248266 deletions
+3 -1
View File
@@ -29,7 +29,7 @@ jobs:
sudo apt-get install --no-install-recommends \
git cmake gcc g++ libboost-all-dev python3-all-dev swig libpcre3-dev libxml2-dev \
libocct-foundation-dev libocct-modeling-algorithms-dev libocct-modeling-data-dev libocct-ocaf-dev libocct-visualization-dev libocct-data-exchange-dev \
libhdf5-dev libcgal-dev
libhdf5-dev libcgal-dev nlohmann-json3-dev
-
name: ccache
@@ -61,6 +61,8 @@ jobs:
-DGMP_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu \
-DMPFR_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu \
-DHDF5_INCLUDE_DIR=/usr/include/hdf5/serial \
-DGLTF_SUPPORT=On \
-DJSON_INCLUDE_DIR=/usr/include \
../cmake
make -j $(nproc)
make install
+104
View File
@@ -0,0 +1,104 @@
import pathlib
import shutil
import requests
import zipfile
import os
# To test this locally, set these environment variables
REPO_OWNER = os.environ.get("REPO_OWNER", "IfcOpenShell/IfcOpenShell")
MY_WORKFLOW = os.environ.get("MY_WORKFLOW", "ci-ifcopenshell-conda-daily")
WORKFLOW_RUN_ID = os.environ.get("WORKFLOW_RUN_ID", None)
TOKEN = os.environ.get("GITHUB_TOKEN", None)
# To test this locally create a fine-grained personal access token for this repo with permissions "actions:read"
# See https://github.com/settings/tokens?type=beta
# This is a list of strings that indicate that a job has stopped abruptly.
SIGNS_OF_STOPPAGE = [
"Error: The operation was canceled.",
"fatal error C1060: compiler is out of heap space",
]
def start_request_session():
s = requests.Session()
headers = {
"Accept": "application/vnd.github+json",
"X-GitHub-Api-Version": "2022-11-28",
}
if TOKEN:
headers["Authorization"] = f"Bearer {TOKEN}"
s.headers = headers
return s
def get_ci_run(repo_name, run_id):
url = f"https://api.github.com/repos/{repo_name}/actions/runs/{run_id}"
s = start_request_session()
response = s.get(url)
return response.json()
def evaluate_log_file_for_abrupt_stop(log_file):
with open(log_file) as f:
for i, line in enumerate(f):
for sign in SIGNS_OF_STOPPAGE:
if sign in line:
print(f"Found sign of abrupt stoppage in [line {i}]: '{sign}'")
return True
return False
def get_ci_specific_run_specific_failure_details(repo_name, run_id):
url = f"https://api.github.com/repos/{repo_name}/actions/runs/{run_id}/attempts/1/logs"
s = start_request_session()
response = s.get(url)
# SAVE zip file
with open("logs.zip", "wb") as f:
f.write(response.content)
# unzip file
with zipfile.ZipFile("logs.zip", "r") as zip_ref:
zip_ref.extractall("logs")
failed_logs = []
for file in pathlib.Path("logs").iterdir():
if file.is_dir():
continue
if evaluate_log_file_for_abrupt_stop(file):
failed_logs.append(file)
return failed_logs
def restart_job(repo_name, job_id):
url = f"https://api.github.com/repos/{repo_name}/actions/runs/{job_id}/rerun-failed-jobs"
s = start_request_session()
response = s.post(url)
return response.json()
def eval_jobs():
run = get_ci_run(REPO_OWNER, WORKFLOW_RUN_ID)
if run["run_attempt"] > 1:
print("This is not the first attempt. Exiting")
return
failed_logs = get_ci_specific_run_specific_failure_details(REPO_OWNER, WORKFLOW_RUN_ID)
if len(failed_logs) == 0:
print("No runs exhibit signs of abrupt stoppage")
return
print("restarting job", WORKFLOW_RUN_ID)
r = restart_job(REPO_OWNER, WORKFLOW_RUN_ID)
print(r)
shutil.rmtree("logs")
os.remove("logs.zip")
if __name__ == "__main__":
eval_jobs()
@@ -62,6 +62,7 @@ jobs:
- uses: actions/setup-python@v2 # https://github.com/actions/setup-python
with:
architecture: 'x64' # optional x64 or x86. Defaults to x64 if not specified
python-version: '3.10'
- run: echo ${{ env.DATE }}
- name: Get current date
id: date
@@ -4,6 +4,7 @@ on:
push:
paths:
- .github/workflows/ci-ifcopenshell-conda-daily.yml
- conda/**
schedule:
# ┌───────────── minute (0 - 59)
# │ ┌───────────── hour (0 - 23)
@@ -53,11 +54,13 @@ jobs:
sudo mv -v MacOSX10.13.sdk /opt/ && \
ls /opt/
- uses: seanmiddleditch/gha-setup-ninja@master
- uses: conda-incubator/setup-miniconda@v2 # https://github.com/conda-incubator/setup-miniconda
- uses: mamba-org/provision-with-micromamba@main # https://github.com/mamba-org/provision-with-micromamba
with:
activate-environment: conda-build
python-version: ${{ matrix.pyver.distver }}
channels: conda-forge
channel-priority: strict
environment-file: conda/environment.yml
extra-specs: |
python=${{ matrix.pyver.distver }}
- name: build, test and upload ifcopenshell
if: ${{ matrix.platform.upload == 'true' }}
run: |
+6 -2
View File
@@ -8,7 +8,11 @@ on:
# │ │ │ ┌───────────── month (1 - 12 or JAN-DEC)
# │ │ │ │ ┌───────────── day of the week (0 - 6 or SUN-SAT)
# * * * * *
- cron: "49 23 1 * *" # 11min before utc midnight
- cron: "0 0 18 * *"
push:
paths:
- '.github/workflows/ci-ifcopenshell-pypi.yml'
env:
major: 0
@@ -53,7 +57,7 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2 # https://github.com/actions/setup-python
with:
python-version: '3.7.7' # Version range or exact version of a Python version to use, using SemVer's version range syntax
python-version: '3.10' # Version range or exact version of a Python version to use, using SemVer's version range syntax
architecture: 'x64' # optional x64 or x86. Defaults to x64 if not specified
- run: echo ${{ env.DATE }}
- name: Get current date
+9 -7
View File
@@ -7,7 +7,7 @@ on:
- 'test/**'
- 'conda/**'
- 'cmake/**'
- '.github/workflows/ci_py_only.yml'
- '.github/workflows/ci-py-only.yml'
pull_request:
jobs:
@@ -46,13 +46,14 @@ jobs:
- name: ccache
uses: hendrikmuhs/ccache-action@v1
with:
key: ${GITHUB_WORKFLOW}
- name: Build ifcopenshell
run: |
sudo /usr/sbin/update-ccache-symlinks
mkdir build && cd build
cmake \
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
PATH="/usr/lib/ccache:$PATH" cmake \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_PREFIX_PATH=/usr \
-DCMAKE_SYSTEM_PREFIX_PATH=/usr \
@@ -62,7 +63,7 @@ jobs:
-DPYTHON_INCLUDE_DIR:PATH=/usr/include/python3.8 \
-DPYTHON_LIBRARY:FILEPATH=/usr/lib/x86_64-linux-gnu/libpython3.8.so \
-DCOLLADA_SUPPORT=Off \
"-DSCHEMA_VERSIONS=2x3;4" \
"-DSCHEMA_VERSIONS=2x3;4;4x3_add1" \
-DBUILD_CONVERT=Off \
-DGLTF_SUPPORT=On \
-DJSON_INCLUDE_DIR=/usr/include \
@@ -79,13 +80,14 @@ jobs:
- name: Install Python dependencies
run: |
sudo /usr/bin/python -m pip install -U pip
sudo /usr/bin/python -m pip install xmlschema numpy lxml
sudo /usr/bin/python -m pip install src/bcf
sudo /usr/bin/python -m pip install xmlschema xsdata numpy lxml
sudo /usr/bin/python -m pip install src/bcf --no-deps
sudo /usr/bin/python -m pip install pytest
sudo /usr/bin/python -m pip install isodate
sudo /usr/bin/python -m pip install lark
sudo /usr/bin/python -m pip install networkx
sudo /usr/bin/python -m pip install tabulate
sudo /usr/bin/python -m pip install python-dateutil
- name: Test
run: |
@@ -0,0 +1,34 @@
name: Restart failed daily conda builds
on:
workflow_run:
workflows: [ ci-ifcopenshell-conda-daily ]
types:
- completed
env:
REPO_OWNER: IfcOpenShell/IfcOpenShell
MY_WORKFLOW: ci-ifcopenshell-conda-daily
jobs:
restart-failed-runs:
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'failure' }}
steps:
- name: checkout
uses: actions/checkout@v3
- name: Use python 3.11
uses: actions/setup-python@v2
with:
python-version: 3.11
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install requests
- name: Check for failed runs
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
WORKFLOW_RUN_ID: ${{ github.event.workflow_run.id }}
run: |
python check_repo_ci_jobs.py
working-directory: .github/workflows
+21 -3
View File
@@ -57,6 +57,8 @@ jobs:
- name: ccache
uses: hendrikmuhs/ccache-action@v1
with:
key: ${GITHUB_WORKFLOW}
- name: Build ifcopenshell
run: |
@@ -73,7 +75,7 @@ jobs:
-DPYTHON_INCLUDE_DIR:PATH=/usr/include/python3.8 \
-DPYTHON_LIBRARY:FILEPATH=/usr/lib/x86_64-linux-gnu/libpython3.8.so \
-DCOLLADA_SUPPORT=Off \
"-DSCHEMA_VERSIONS=2x3;4;4x3" \
"-DSCHEMA_VERSIONS=2x3;4;4x3_add1" \
-DGLTF_SUPPORT=On \
-DJSON_INCLUDE_DIR=/usr/include \
-DCGAL_INCLUDE_DIR=/usr/include \
@@ -86,16 +88,32 @@ jobs:
sudo make -j $(nproc)
sudo make install
- name: Run IfcConvert on Sample files
run: |
(find test/input src/blenderbim/test/files -name '*.ifc' | while read i; do \
echo $i | tee -a log; \
timeout 1m "$(which IfcConvert)" -yv "$i" "$i.obj" --validate >> log 2>&1; \
echo $i $? >> statuses; \
done) || true
echo Failed
grep -v 0$ statuses
grep -v 0$ statuses | wc -l
echo Succeeded
grep 0$ statuses
grep 0$ statuses | wc -l
- name: Install Python dependencies
run: |
sudo /usr/bin/python -m pip install -U pip
sudo /usr/bin/python -m pip install xmlschema numpy lxml
sudo /usr/bin/python -m pip install src/bcf
sudo /usr/bin/python -m pip install xmlschema xsdata numpy lxml
sudo /usr/bin/python -m pip install src/bcf --no-deps
sudo /usr/bin/python -m pip install pytest
sudo /usr/bin/python -m pip install isodate
sudo /usr/bin/python -m pip install lark
sudo /usr/bin/python -m pip install networkx
sudo /usr/bin/python -m pip install https://github.com/Andrej730/aud/archive/refs/heads/master-reduced-size.zip
sudo /usr/bin/python -m pip install tabulate
sudo /usr/bin/python -m pip install python-dateutil
- name: Test
run: |
+3
View File
@@ -84,6 +84,9 @@ src/ifcopenshell-python/test/build
# blenderbim libs
src/blenderbim/blenderbim/libs
# blenderbim test temp files
src/blenderbim/test/files/temp
# ifcopenshell swig and compiled files
src/ifcopenshell-python/ifcopenshell/_ifcopenshell_wrapper.so
src/ifcopenshell-python/ifcopenshell/ifcopenshell_wrapper.py
+1 -1
View File
@@ -27,7 +27,7 @@ RUN echo "deb http://archive.ubuntu.com/ubuntu focal-proposed main restricted" |
libboost-all-dev \
libocct-foundation-dev libocct-modeling-algorithms-dev libocct-modeling-data-dev \
libocct-ocaf-dev libocct-visualization-dev libocct-data-exchange-dev \
libhdf5-serial-dev ; \
libhdf5-serial-dev python3-pytest ; \
rm -rf /var/lib/apt/lists/* ;
COPY . /home/IfcOpenShell/
+1 -1
View File
@@ -1017,7 +1017,7 @@ endforeach(COMPONENT)
set(CPACK_DEBIAN_PACKAGE_NAME "${PROJECT_NAME}")
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "${CPACK_PACKAGE_CONTACT}")
set(CPACK_DEBIAN_PACKAGE_DEPENDS "python3, libxml2, libocct-foundation-dev, libocct-modeling-algorithms-dev, libocct-modeling-data-dev, libocct-ocaf-dev, libocct-visualization-dev, libocct-data-exchange-dev, libhdf5-serial-dev, libpython3.8 ${BOOST_DEPS}")
set(CPACK_DEBIAN_PACKAGE_DEPENDS "python3, libxml2, libocct-foundation-dev, libocct-modeling-algorithms-dev, libocct-modeling-data-dev, libocct-ocaf-dev, libocct-visualization-dev, libocct-data-exchange-dev, libhdf5-serial-dev, libpython3.8, python3-pytest ${BOOST_DEPS}")
set(CPACK_DEBIAN_PACKAGE_DESCRIPTION_SUMMARY "${CPACK_PACKAGE_DESCRIPTION_SUMMARY}")
set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "${CPACK_PACKAGE_DESCRIPTION}")
set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional")
+1 -2
View File
@@ -10,5 +10,4 @@ dependencies:
- ripgrep
- pip
- pip:
- --extra-index-url https://lief.s3-website.fr-par.scw.cloud/latest
- lief==0.13.0.dev0
- lief==0.13
+2 -4
View File
@@ -1,8 +1,6 @@
{% set name = "ifcopenshell" %}
{% set version = "0.7.0" %}
{% set occt_version = "7.6.2" %}
{% set cgal_cpp_version = "5.3" %}
{% set hdf5_version = "1.12.1" %}
package:
name: {{ name }}
@@ -38,7 +36,7 @@ requirements:
run:
- python
- boost-cpp
- occt
- {{ pin_compatible('occt', max_pin='x.x.x') }}
- libxml2
- cgal-cpp
- hdf5
+4
View File
@@ -59,6 +59,10 @@ import multiprocessing
import platform
import sysconfig
# @todo temporary for expired mpfr.org certificate on 2023-04-08
import ssl
ssl._create_default_https_context = ssl._create_unverified_context
from urllib.request import urlretrieve
+2 -22
View File
@@ -1,31 +1,11 @@
.PHONY: test
test:
.PHONY: ci
ci:
tox
.PHONY: qa
qa:
black .
isort .
pylint ./src --output-format=colorized
.PHONY: typecheck
typecheck:
mypy src/bcf
.PHONY: license
license:
copyright-header --license LGPL3 --copyright-holder "Andrea Ghensi <andrea.ghensi@gmail.com>" --copyright-year "2022" --copyright-software "IfcOpenShell" --copyright-software-description "BCF XML file handling" -a ./ -o ./
.PHONY: coverage
coverage:
coverage run --source bcf -m pytest tests
coverage html
xdg-open htmlcov/index.html
.PHONY: clean
clean:
rm -rf htmlcov
# TODO: make this based on xsd file presence
.PHONY: models
models:
+1 -1
View File
@@ -27,7 +27,7 @@ with load("/path/to/file.bcf") as bcfxml:
# Get a dictionary of topics
topics = bcfxml.topics
for topic_handler in bcfxml.topics:
for topic_guid, topic_handler in bcfxml.topics.items():
topic = topic_handler.topic
print("Topic guid is", topic.guid)
print("Topic title is", topic.title)
-7
View File
@@ -1,7 +0,0 @@
name: bcf-client
channels:
- conda-forge
dependencies:
- ifcopenshell
- xsdata
- numpy
+22 -7
View File
@@ -15,6 +15,7 @@ keywords = ["IFC", "BCF", "BIM", "eingineering"]
dependencies = [
"xsdata",
"numpy",
"ifcopenshell",
]
version = "0.0.1"
classifiers = [
@@ -58,20 +59,34 @@ exclude_lines = [
[tool.tox]
legacy_tox_ini = """
[tox]
envlist = py310
isolated_build = true
env_list = lint, type, py3{10,11}
skip_missing_interpreters = true
requires = tox-conda
[testenv]
conda_deps =
deps =
pytest
pytest-cov
coverage
conda_channels =
conda-forge
conda_env = environment.yml
commands = pytest --cov --cov-report=term tests
[testenv:lint]
description = run linters
skip_install = true
deps =
black
isort
pylint
commands =
black {posargs:.}
isort {posargs:.}
pylint {posargs:.} --output-format=colorized
[testenv:type]
description = run type checks
deps =
mypy>=0.991
commands =
- mypy {posargs:src}
"""
[tool.mypy]
-1
View File
@@ -4,4 +4,3 @@ pylint
isort
xsdata
tox==3.27.1
tox-conda
+6 -2
View File
@@ -23,8 +23,9 @@ from pathlib import Path
from typing import Optional, Union
from bcf.v2.bcfxml import BcfXml as BcfXml2
from bcf.v2.model import Version as Version2
from bcf.v3.bcfxml import BcfXml as BcfXml3
from bcf.v3.model import Version
from bcf.v3.model import Version as Version3
from bcf.xml_parser import AbstractXmlParserSerializer, XmlParserSerializer
@@ -65,5 +66,8 @@ def _get_version(filepath: Union[str, Path], xml_handler: Optional[AbstractXmlPa
"""
xml_handler = xml_handler or XmlParserSerializer()
with zipfile.ZipFile(filepath) as bcf_zip:
version = xml_handler.parse(bcf_zip.read("bcf.version"), Version)
try:
version = xml_handler.parse(bcf_zip.read("bcf.version"), Version3)
except:
version = xml_handler.parse(bcf_zip.read("bcf.version"), Version2)
return version.version_id
+55 -7
View File
@@ -1,5 +1,6 @@
"""BCF XML V2 Topic handler."""
import datetime
import tempfile
import uuid
import zipfile
from pathlib import Path
@@ -218,9 +219,56 @@ class TopicHandler:
real_path = real_path.parent if path_part == ".." else real_path.joinpath(path_part)
destination_zip.writestr(real_path.at, self.document_references[doc.referenced_document])
def add_viewpoint(self, element: entity_instance) -> None:
def extract_file(self, entity, outfile: Optional[Path] = None) -> Path:
"""Extracts an element with a file into a temporary directory
These include header files, bim snippets, document references, and
viewpoint bitmaps. External reference are not downloaded. Instead, the
URI reference is returned.
:param entity: The entity with a file reference to extract
:type entity: bcf.v2.model.HeaderFile,bcf.v2.model.BimSnippet,bcf.v2.model.TopicDocumentReference
:param outfile: If provided, save the header file to that location.
Otherwise, a temporary directory is created and the filename is
derived from the header's original filename.
:type outfile: pathlib.Path,optional
:return: The filepath of the extracted file. It may be a URL if the
header file is external.
:rtype: Path
"""
Add a viewpoint tergeting an IFC element to the topic.
if hasattr(entity, "reference"):
reference = entity.reference
else:
reference = entity.referenced_document
if not reference:
return
if getattr(entity, "is_external", False):
return entity.reference
resolved_reference = self._topic_dir
for part in Path(reference).parts:
if part == "..":
resolved_reference = resolved_reference.parent
else:
resolved_reference = resolved_reference.joinpath(part)
if not outfile:
if getattr(entity, "filename", None):
filename = entity.filename
else:
filename = resolved_reference.name
outfile = Path(tempfile.mkdtemp()) / filename
with open(outfile, "wb") as f:
f.write(resolved_reference.read_bytes())
return outfile
def add_viewpoint(self, element: entity_instance) -> None:
"""Add a viewpoint pointed at the placement of an IFC element to the topic.
Args:
element: The IFC element.
@@ -229,11 +277,11 @@ class TopicHandler:
self.add_visinfo_handler(new_viewpoint)
def add_viewpoint_from_point_and_guids(self, position: NDArray[np.float_], *guids: str) -> None:
"""
Add a viewpoint tergeting an IFC element to the topic.
"""Add a viewpoint pointing at an XYZ point in space
Args:
element: The IFC element.
position: the XYZ point in space
guids: one or more element GlobalIds.
"""
vi_handler = VisualizationInfoHandler.create_from_point_and_guids(
position, *guids, xml_handler=self._xml_handler
@@ -241,8 +289,8 @@ class TopicHandler:
self.add_visinfo_handler(vi_handler)
def add_visinfo_handler(self, new_viewpoint: VisualizationInfoHandler) -> None:
self.viewpoints[new_viewpoint.guid] = new_viewpoint
self.markup.viewpoints.append(mdl.ViewPoint(viewpoint=new_viewpoint.guid, guid=new_viewpoint.guid))
self.viewpoints[new_viewpoint.guid + ".bcfv"] = new_viewpoint
self.markup.viewpoints.append(mdl.ViewPoint(viewpoint=new_viewpoint.guid + ".bcfv", guid=new_viewpoint.guid))
def __eq__(self, other: object) -> bool | NoReturn:
return (
+8 -2
View File
@@ -5,7 +5,8 @@ from typing import Any, Iterable, Optional
import numpy as np
from ifcopenshell import entity_instance
from ifcopenshell.util import placement
import ifcopenshell.util.unit
import ifcopenshell.util.placement
from numpy.typing import NDArray
import bcf.v2.model as mdl
@@ -209,7 +210,12 @@ def build_viewpoint(element: entity_instance) -> mdl.VisualizationInfo:
Returns:
The BCF viewpoint definition.
"""
elem_placement = placement.get_local_placement(element.ObjectPlacement)
ifc_file = element.wrapped_data.file
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(ifc_file)
elem_placement = ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement)
elem_placement[0][3] *= unit_scale
elem_placement[1][3] *= unit_scale
elem_placement[2][3] *= unit_scale
return mdl.VisualizationInfo(
guid=str(uuid.uuid4()),
+4 -2
View File
@@ -187,10 +187,12 @@ class TopicHandler:
self.add_visinfo_handler(vi_handler)
def add_visinfo_handler(self, new_viewpoint: VisualizationInfoHandler) -> None:
self.viewpoints[new_viewpoint.guid] = new_viewpoint
self.viewpoints[new_viewpoint.guid + ".bcfv"] = new_viewpoint
if self.topic.viewpoints is None:
self.topic.viewpoints = mdl.TopicViewpoints()
self.topic.viewpoints.view_point.append(mdl.ViewPoint(viewpoint=new_viewpoint.guid, guid=new_viewpoint.guid))
self.topic.viewpoints.view_point.append(
mdl.ViewPoint(viewpoint=new_viewpoint.guid + ".bcfv", guid=new_viewpoint.guid)
)
def __eq__(self, other: object) -> bool | NoReturn:
return (
+1 -1
View File
@@ -15,7 +15,7 @@ def test_create_clash_set_bcf() -> None:
assert len(topic.viewpoints) == 1
guid, vi_handler = next((k, v) for k, v in topic.viewpoints.items())
v_info = vi_handler.visualization_info
assert v_info.guid == guid
assert f"{v_info.guid}.bcfv" == guid
components = v_info.components.selection.component
assert {c.ifc_guid for c in components} == {"firstId", "secondId"}
camera = v_info.perspective_camera
+1 -1
View File
@@ -15,7 +15,7 @@ def test_create_clash_set_bcf() -> None:
assert len(topic.viewpoints) == 1
guid, vi_handler = next((k, v) for k, v in topic.viewpoints.items())
v_info = vi_handler.visualization_info
assert v_info.guid == guid
assert f"{v_info.guid}.bcfv" == guid
components = v_info.components.selection.component
assert {c.ifc_guid for c in components} == {"firstId", "secondId"}
camera = v_info.perspective_camera
+48 -22
View File
@@ -1,5 +1,5 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
# Copyright (C) 2020-2023 Dion Moult <dion@thinkmoult.com>
#
# This file is part of BlenderBIM Add-on.
#
@@ -16,7 +16,8 @@
# You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
PYTHON:=python
PYTHON:=python3.10
PIP:=pip3.10
PATCH:=patch
SED:=sed -i
ifeq ($(OS),Windows_NT)
@@ -56,7 +57,7 @@ HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/1.7.5/download/linux-64/hpp
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/2.6.5/download/linux-64/eigenpy-2.6.5-py37h95e2c48_0.tar.bz2
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.74.0/download/linux-64/boost-1.74.0-py37h0379df6_3.tar.bz2
LXML_URL:=https://files.pythonhosted.org/packages/30/c0/d0526314971fc661b083ab135747dc68446a3022686da8c16d25fcf6ef07/lxml-4.6.3-cp37-cp37m-manylinux2014_x86_64.whl
SHAPELY_URL:=https://files.pythonhosted.org/packages/a2/73/07f507b539527579c026ed5f411c35e287685ce794c4dfb7edf6675cdc24/Shapely-1.8.4-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl
SHAPELY_URL:=https://files.pythonhosted.org/packages/1d/a4/931d0780f31f3ea8c4f9ef6464a2825137c5241e6707a5fb03bef760a7eb/shapely-2.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
PILLOW_URL:=https://files.pythonhosted.org/packages/ed/d5/c2e84e1e36ab8ebea033921d5886a056c77e18bab5ab1051fcc22de2e8a2/Pillow-9.2.0-cp37-cp37m-manylinux_2_28_x86_64.whl
endif
ifeq ($(PYVERSION), py39)
@@ -64,7 +65,7 @@ HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/1.7.5/download/linux-64/hpp
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/2.6.5/download/linux-64/eigenpy-2.6.5-py39h5aed9d1_0.tar.bz2
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.74.0/download/linux-64/boost-1.74.0-py39h5472131_3.tar.bz2
LXML_URL:=https://files.pythonhosted.org/packages/19/d9/a69c6aff5673554df48120565a14a50eaa41d29ae03b02faa0b023666318/lxml-4.6.3-cp39-cp39-manylinux2014_x86_64.whl
SHAPELY_URL:=https://files.pythonhosted.org/packages/08/12/5af915b2bc0470f4a652ef9978557a0fa8b705b122ccaec6626029e1674c/Shapely-1.8.4-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl
SHAPELY_URL:=https://files.pythonhosted.org/packages/2d/f2/8ec281d357e8bb7d08dc8d727f0e4c8ef3dae7d3fa75c69c8e452bb82d50/shapely-2.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
PILLOW_URL:=https://files.pythonhosted.org/packages/01/61/3ff85fb4bb596ce3d223c8fcf93c8df5c12bc8899dfb4fb3cb1c5b20dd5f/Pillow-9.2.0-cp39-cp39-manylinux_2_28_x86_64.whl
endif
ifeq ($(PYVERSION), py310)
@@ -72,7 +73,7 @@ HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/1.8.0/download/linux-64/hpp
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/2.6.11/download/linux-64/eigenpy-2.6.11-py310hf3e5c9c_0.tar.bz2
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.74.0/download/linux-64/boost-1.74.0-py310h7c3ba0c_5.tar.bz2
LXML_URL:=https://files.pythonhosted.org/packages/25/1e/19b46d8e8881fe0df2e20945d51919eeb1817836d62a90efa8506530e45c/lxml-4.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl
SHAPELY_URL:=https://files.pythonhosted.org/packages/60/20/c6321e3da92a4ad9495727c9aa8c423e47295572ccd35ece82d1ac013e28/Shapely-1.8.4-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl
SHAPELY_URL:=https://files.pythonhosted.org/packages/a8/a5/403728b5614b28083f6424dfdefec5fcf58068495fb03bb08532671c642f/shapely-2.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
PILLOW_URL:=https://files.pythonhosted.org/packages/f6/51/320986ebd6d46a0e95c2240468ced73153b691ce07617078bcdf30c609ec/Pillow-9.2.0-cp310-cp310-manylinux_2_28_x86_64.whl
endif
ASSIMP_URL:=https://anaconda.org/conda-forge/assimp/5.0.1/download/linux-64/assimp-5.0.1-hedfc422_6.tar.bz2
@@ -86,7 +87,7 @@ HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/1.7.5/download/osx-64/hpp-f
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/2.6.5/download/osx-64/eigenpy-2.6.5-py37h0695097_0.tar.bz2
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.74.0/download/osx-64/boost-1.74.0-py37hd79e0ac_3.tar.bz2
LXML_URL:=https://files.pythonhosted.org/packages/1e/3e/f0abc15d5dac50939bccc589aae336d5ead4c72e7ad1039a2e0f3630ea92/lxml-4.6.3-cp37-cp37m-macosx_10_9_x86_64.whl
SHAPELY_URL:=https://files.pythonhosted.org/packages/47/a9/7c644e71638bff2bbaa87f05ee62fdf6b4552a55e74bbf731d6210b6d436/Shapely-1.8.4-cp37-cp37m-macosx_10_9_x86_64.whl
SHAPELY_URL:=https://files.pythonhosted.org/packages/e6/7d/4923f27c340339e1c896c77cafc8ed672c8d381a025bbab6c6ddcba27e8f/shapely-2.0.1-cp37-cp37m-macosx_10_9_x86_64.whl
PILLOW_URL:=https://files.pythonhosted.org/packages/88/49/c26fc3b5b0e82bdc9d8751d6b939da29327b0d98f7c3b95a575cbfed2743/Pillow-9.2.0-cp37-cp37m-macosx_10_10_x86_64.whl
endif
ifeq ($(PYVERSION), py39)
@@ -94,7 +95,7 @@ HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/1.7.5/download/osx-64/hpp-f
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/2.6.5/download/osx-64/eigenpy-2.6.5-py39h5405915_0.tar.bz2
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.74.0/download/osx-64/boost-1.74.0-py39ha641261_3.tar.bz2
LXML_URL:=https://files.pythonhosted.org/packages/b8/74/a71f7ad72e8db54ce899efab84507b801660750cbbfa6a39e6717557d36a/lxml-4.6.3-cp39-cp39-macosx_10_9_x86_64.whl
SHAPELY_URL:=https://files.pythonhosted.org/packages/f5/f9/bf6cc34b02b3c7d3777c5e47fa57d2f69387613bbd8decdf3577a9d83a83/Shapely-1.8.4-cp39-cp39-macosx_10_9_x86_64.whl
SHAPELY_URL:=https://files.pythonhosted.org/packages/36/a4/7e542a209f862f967d7cb8e939eff155f4294a27d17e16441fb8bdd51a2c/shapely-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl
PILLOW_URL:=https://files.pythonhosted.org/packages/88/7a/ddfe28b485b623361457d4783007c1f9ba83a87f93e7fec32f64793efb6c/Pillow-9.2.0-cp39-cp39-macosx_10_10_x86_64.whl
endif
ifeq ($(PYVERSION), py310)
@@ -102,7 +103,7 @@ HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/1.8.0/download/osx-64/hpp-f
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/2.6.11/download/osx-64/eigenpy-2.6.11-py310hc03097c_0.tar.bz2
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.74.0/download/osx-64/boost-1.74.0-py310h509978a_5.tar.bz2
LXML_URL:=https://files.pythonhosted.org/packages/a1/44/17b7dac7a18807d30e2fe10c3328c152808f5464565e230bfd0e77f178c6/lxml-4.8.0-cp310-cp310-macosx_10_15_x86_64.whl
SHAPELY_URL:=https://files.pythonhosted.org/packages/f5/a8/02347c450b5507b9a7a2c15ad6a029613149468fc55ac2100ab215666159/Shapely-1.8.4-cp310-cp310-macosx_10_9_x86_64.whl
SHAPELY_URL:=https://files.pythonhosted.org/packages/1f/2a/dc3353c2431cf53e8d04bb8fba27e584410ca3435c9c85f76d71bf0c0e80/shapely-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl
PILLOW_URL:=https://files.pythonhosted.org/packages/d8/60/b13c00d403f34110e96c1b5c0afa73ce461efe3fe960c3a7e3e7fe190d82/Pillow-9.2.0-cp310-cp310-macosx_10_10_x86_64.whl
endif
ASSIMP_URL:=https://anaconda.org/conda-forge/assimp/5.0.1/download/osx-64/assimp-5.0.1-h1224e73_6.tar.bz2
@@ -117,7 +118,7 @@ HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/1.7.5/download/osx-64/hpp-f
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/2.6.5/download/osx-64/eigenpy-2.6.5-py37h0695097_0.tar.bz2
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.74.0/download/osx-64/boost-1.74.0-py37hd79e0ac_3.tar.bz2
LXML_URL:=https://anaconda.org/conda-forge/lxml/4.9.1/download/osx-64/lxml-4.9.1-py37h994c40b_0.tar.bz2
SHAPELY_URL:=https://files.pythonhosted.org/packages/47/a9/7c644e71638bff2bbaa87f05ee62fdf6b4552a55e74bbf731d6210b6d436/Shapely-1.8.4-cp37-cp37m-macosx_10_9_x86_64.whl
SHAPELY_URL:=https://files.pythonhosted.org/packages/e6/7d/4923f27c340339e1c896c77cafc8ed672c8d381a025bbab6c6ddcba27e8f/shapely-2.0.1-cp37-cp37m-macosx_10_9_x86_64.whl
PILLOW_URL:=https://files.pythonhosted.org/packages/aa/bc/21097cd891dd2fa02f2b3d767e02e883e026482e59d29975d1bc30024aa3/Pillow-9.2.0-cp39-cp39-macosx_11_0_arm64.whl
endif
ifeq ($(PYVERSION), py39)
@@ -125,7 +126,7 @@ HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/1.7.5/download/osx-arm64/hp
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/2.6.7/download/osx-arm64/eigenpy-2.6.7-py39ha69f3c1_0.tar.bz2
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.78.0/download/osx-arm64/boost-1.78.0-py39h2550fe3_0.tar.bz2
LXML_URL:=https://anaconda.org/conda-forge/lxml/4.9.1/download/osx-arm64/lxml-4.9.1-py39h9eb174b_0.tar.bz2
SHAPELY_URL:=https://files.pythonhosted.org/packages/8e/60/8186995a96c5a27c9b79c015bbdd89c6d9a6df9988f86f3f1027ecd4a56a/Shapely-1.8.4-cp39-cp39-macosx_11_0_arm64.whl
SHAPELY_URL:=https://files.pythonhosted.org/packages/ea/aa/45fbd031edf3149cb767d8b9f9db45d5faf0324d743c6b8fb0298cc022d0/shapely-2.0.1-cp39-cp39-macosx_11_0_arm64.whl
PILLOW_URL:=https://files.pythonhosted.org/packages/aa/bc/21097cd891dd2fa02f2b3d767e02e883e026482e59d29975d1bc30024aa3/Pillow-9.2.0-cp39-cp39-macosx_11_0_arm64.whl
endif
ifeq ($(PYVERSION), py310)
@@ -133,7 +134,7 @@ HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/1.8.0/download/osx-arm64/hp
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/2.6.9/download/osx-arm64/eigenpy-2.6.9-py310habfc766_1.tar.bz2
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.78.0/download/osx-arm64/boost-1.78.0-py310h4566fd1_0.tar.bz2
LXML_URL:=https://anaconda.org/conda-forge/lxml/4.9.1/download/osx-arm64/lxml-4.9.1-py310h02f21da_0.tar.bz2
SHAPELY_URL:=https://files.pythonhosted.org/packages/ba/d0/75cd36c81a1aa26dd6d37cf7ae1c79b0bc2d379e0f32c359bcb00b14e5c3/Shapely-1.8.4-cp310-cp310-macosx_11_0_arm64.whl
SHAPELY_URL:=https://files.pythonhosted.org/packages/ec/41/d59208743e737184e1b403e95a937aebb022b8459e99efbcd5208fc8be46/shapely-2.0.1-cp310-cp310-macosx_11_0_arm64.whl
PILLOW_URL:=https://files.pythonhosted.org/packages/0c/5f/117b653cad585f3aedfe0de996c292e67d4b020ed77f652e5a6c8c24f908/Pillow-9.2.0-cp310-cp310-macosx_11_0_arm64.whl
endif
ASSIMP_URL:=https://anaconda.org/conda-forge/assimp/5.0.1/download/osx-arm64/assimp-5.0.1-h0f81e16_7.tar.bz2
@@ -147,7 +148,7 @@ HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/1.7.5/download/win-64/hpp-f
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/2.6.5/download/win-64/eigenpy-2.6.5-py37h2c32e34_0.tar.bz2
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.74.0/download/win-64/boost-1.74.0-py37h3b38789_3.tar.bz2
LXML_URL:=https://files.pythonhosted.org/packages/9e/5e/171ee9d40a600f565fe691ec5bf7596247ec62cfb2edc00c91afe8ea837b/lxml-4.6.3-cp37-cp37m-win_amd64.whl
SHAPELY_URL:=https://files.pythonhosted.org/packages/ca/4d/85b912d7947261e6b3218b01946bcd7c2efb2bbafaf517c6fe1dc3bb8399/Shapely-1.8.4-cp37-cp37m-win_amd64.whl
SHAPELY_URL:=https://files.pythonhosted.org/packages/e2/87/b8b8d8d57b429b01aa56b7d723075c09f33c988b8091bb6f790c83436909/shapely-2.0.1-cp37-cp37m-win_amd64.whl
PILLOW_URL:=https://files.pythonhosted.org/packages/69/f5/9e802159d78b2eaf26bf1f8b94648605993f5ca7247ac8870f065063fc40/Pillow-9.2.0-cp37-cp37m-win_amd64.whl
endif
ifeq ($(PYVERSION), py39)
@@ -155,7 +156,7 @@ HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/1.7.5/download/win-64/hpp-f
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/2.6.5/download/win-64/eigenpy-2.6.5-py39h3ce40e6_0.tar.bz2
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.74.0/download/win-64/boost-1.74.0-py39hefe7e4c_3.tar.bz2
LXML_URL:=https://files.pythonhosted.org/packages/72/d4/426ecb8849c47c3e370c87aa0ac05d85768df917ffea27fcd6686a5e6495/lxml-4.6.3-cp39-cp39-win_amd64.whl
SHAPELY_URL:=https://files.pythonhosted.org/packages/72/c3/0d52812db25ca71203f6203891277e6ee4f0244b21a36c062123ece15f36/Shapely-1.8.4-cp39-cp39-win_amd64.whl
SHAPELY_URL:=https://files.pythonhosted.org/packages/a7/ae/eab645c4075093584b7a65ab71cb8ff4563a015bd935c9b55dba14b2c1eb/shapely-2.0.1-cp39-cp39-win_amd64.whl
PILLOW_URL:=https://files.pythonhosted.org/packages/19/3f/b4d4bcf05dbcbe07f2e9613a8f4180c297395e73a91d8ad22c32c6624f8c/Pillow-9.2.0-cp39-cp39-win_amd64.whl
endif
ifeq ($(PYVERSION), py310)
@@ -163,7 +164,7 @@ HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/1.8.0/download/win-64/hpp-f
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/2.6.11/download/win-64/eigenpy-2.6.11-py310hbd43d28_0.tar.bz2
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.74.0/download/win-64/boost-1.74.0-py310hc781a3c_5.tar.bz2
LXML_URL:=https://files.pythonhosted.org/packages/f6/71/65c80a4caa1617a4c6e8fe1500cebb179db96232e2f623bfe6a1f4294e39/lxml-4.8.0-cp310-cp310-win_amd64.whl
SHAPELY_URL:=https://files.pythonhosted.org/packages/24/b5/46d13799804b5fe9ede2eca8b19023410e29b78baca0bff727671a1df8c1/Shapely-1.8.4-cp310-cp310-win_amd64.whl
SHAPELY_URL:=https://files.pythonhosted.org/packages/81/8a/7ac076a86b2632f1872284c5e60ed5f2fc26094875a85b35d9fa17b52504/shapely-2.0.1-cp310-cp310-win_amd64.whl
PILLOW_URL:=https://files.pythonhosted.org/packages/02/55/67a3c17b9e7d972ed8c246f104da99ca4f3ea42fba566697e479011b84b6/Pillow-9.2.0-cp310-cp310-win_amd64.whl
endif
ASSIMP_URL:=https://anaconda.org/conda-forge/assimp/5.0.1/download/win-64/assimp-5.0.1-hc2aa0de_6.tar.bz2
@@ -189,7 +190,7 @@ endif
cp -r blenderbim/* dist/blenderbim/
# Provides IfcOpenShell Python functionality
cd dist/working && wget https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-$(PYNUMBER)-v0.7.0-6180d73-$(PLATFORM)64.zip
cd dist/working && wget https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-$(PYNUMBER)-v0.7.0-476ab50-$(PLATFORM)64.zip
cd dist/working && unzip ifcopenshell-python*
cp -r dist/working/ifcopenshell dist/blenderbim/libs/site/packages/
@@ -207,14 +208,16 @@ endif
cp -r dist/working/IfcOpenShell-0.7.0/src/ifcopenshell-python/ifcopenshell/api/* dist/blenderbim/libs/site/packages/ifcopenshell/api/
cp dist/working/IfcOpenShell-0.7.0/src/ifcopenshell-python/ifcopenshell/entity_instance.py dist/blenderbim/libs/site/packages/ifcopenshell/
cp dist/working/IfcOpenShell-0.7.0/src/ifcopenshell-python/ifcopenshell/file.py dist/blenderbim/libs/site/packages/ifcopenshell/
cp dist/working/IfcOpenShell-0.7.0/src/ifcopenshell-python/ifcopenshell/validate.py dist/blenderbim/libs/site/packages/ifcopenshell/
cp dist/working/IfcOpenShell-0.7.0/src/ifcopenshell-python/ifcopenshell/express/rule_executor.py dist/blenderbim/libs/site/packages/ifcopenshell/express/
# Provides bcf functionality
cp -r dist/working/IfcOpenShell-0.7.0/src/bcf/src/bcf dist/blenderbim/libs/site/packages/
# Provides IFCClash functionality
cp -r dist/working/IfcOpenShell-0.7.0/src/ifcclash/ifcclash dist/blenderbim/libs/site/packages/
# Provides BIMTester functionality
cd dist/working && $(PYTHON) -m venv env
cd dist/working && . env/bin/activate && pip install pybabel
cd dist/working && . env/bin/activate && pip install babel
cd dist/working && . env/bin/activate && $(PIP) install pybabel
cd dist/working && . env/bin/activate && $(PIP) install babel
cd dist/working && . env/bin/activate && ./env/bin/pybabel compile -d ./IfcOpenShell-0.7.0/src/ifcbimtester/bimtester/locale/
cp -r dist/working/IfcOpenShell-0.7.0/src/ifcbimtester/bimtester dist/blenderbim/libs/site/packages/
# Provides IFCTester functionality
@@ -233,9 +236,15 @@ endif
cp -r dist/working/IfcOpenShell-0.7.0/src/ifc5d/ifc5d dist/blenderbim/libs/site/packages/
# Provides IFCCityJSON functionality
cp -r dist/working/IfcOpenShell-0.7.0/src/ifccityjson dist/blenderbim/libs/site/packages/
# Provides IfcGit functionality
cd dist/working && . env/bin/activate && $(PIP) install GitPython --target=./site-packages
cp -r dist/working/site-packages/smmap dist/blenderbim/libs/site/packages/
cp -r dist/working/site-packages/gitdb dist/blenderbim/libs/site/packages/
cp -r dist/working/site-packages/git dist/blenderbim/libs/site/packages/
rm -rf dist/working
# Provides Mustache templating in construction documentation
# TODO: remove this dependency, it seems overkill and we seem to get by with str replaces
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/3f/e7/8750ba6c6101d6aa5ceeb20c013adf2c6f3554a12c71d75654b468404bfa/pystache-0.6.0.tar.gz
cd dist/working && tar -xzvf pystache*
@@ -277,6 +286,13 @@ endif
cp -r dist/working/decorator-5.0.9/src/decorator.py dist/blenderbim/libs/site/packages/
rm -rf dist/working
# Provides audio playback for costing
mkdir -p dist/working
git clone https://github.com/Andrej730/aud.git --branch master-reduced-size --depth 1 dist/working/aud
cd dist/working/aud && git checkout 529af208fbcb707c387bfb681d2605c0b6112de7
cp -r dist/working/aud/aud dist/blenderbim/libs/site/packages/
rm -rf dist/working
# Provides jsgantt-improved supports for web-based construction sequencing gantt charts
mkdir dist/working
cd dist/working && wget https://raw.githubusercontent.com/jsGanttImproved/jsgantt-improved/master/dist/jsgantt.js
@@ -360,10 +376,10 @@ endif
cd dist/working && cp *.whl shapely.zip && unzip shapely.zip
cp -r dist/working/shapely dist/blenderbim/libs/site/packages/
ifeq ($(PLATFORM), win)
cp -r dist/working/Shapely.libs dist/blenderbim/libs/site/packages/
cp -r dist/working/shapely.libs dist/blenderbim/libs/site/packages/
endif
ifeq ($(PLATFORM), linux)
cp -r dist/working/Shapely.libs dist/blenderbim/libs/site/packages/
cp -r dist/working/shapely.libs dist/blenderbim/libs/site/packages/
endif
rm -rf dist/working
@@ -385,11 +401,13 @@ endif
cp -r dist/working/tqdm-4.50.0/tqdm dist/blenderbim/libs/site/packages/
rm -rf dist/working
# Required by IFCCityJSON
mkdir dist/working
cd dist/working && $(PYTHON) -m venv env
cd dist/working && mkdir site-packages
cd dist/working && . env/bin/activate && pip install cjio --target=./site-packages
# Required by IFCCityJSON
cd dist/working && . env/bin/activate && $(PIP) install cjio --target=./site-packages
# Provides express rule validation for ifcopenshell.validate
cd dist/working && . env/bin/activate && $(PIP) install pytest --target=./site-packages
cd dist/working/site-packages/ && rm -r *dist-info*
cp -r dist/working/site-packages/* dist/blenderbim/libs/site/packages/
rm -rf dist/working
@@ -535,6 +553,7 @@ endif
rm -rf dist/working
# Required by IFCCOBie for XLSX support
# TODO: see if we can replace this with openpyxl which does both read/write
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/0c/bc/82d6783f83f65f56d8b77d052773c4a2f952fa86385f0cd54e1e006658d7/XlsxWriter-1.2.9.tar.gz
cd dist/working && tar -xzvf XlsxWriter*
@@ -606,7 +625,7 @@ endif
cd dist/working/brickschema-0.5.1/ && cp -r brickschema ../../blenderbim/libs/site/packages/
# For now lets bundle the latest nightly schema
cd dist/working && wget https://github.com/BrickSchema/Brick/releases/download/nightly/Brick.ttl
cd dist/working && cp Brick.ttl ../../blenderbim/bim/schema/Brick.ttl
cd dist/working && cp Brick.ttl ../blenderbim/bim/schema/Brick.ttl
rm -rf dist/working
# Required by brickschema
@@ -638,6 +657,13 @@ endif
cd dist/working/ && cp -r pkg_resources ../blenderbim/libs/site/packages/
rm -rf dist/working
# Required for hipped roof generation
mkdir dist/working
cd dist/working && wget https://github.com/prochitecture/bpypolyskel/archive/refs/heads/master.zip
cd dist/working && unzip master.zip
cp -r dist/working/bpypolyskel-master/bpypolyskel dist/blenderbim/libs/site/packages/
rm -rf dist/working
cd dist/blenderbim && $(SED) "s/999999/$(VERSION)/" __init__.py
cd dist && zip -r blenderbim-$(VERSION)-$(PYVERSION)-$(PLATFORM).zip ./*
rm -rf dist/blenderbim
+2 -1
View File
@@ -34,7 +34,8 @@ bl_info = {
if sys.modules.get("bpy", None):
# Process *.pth in /libs/site/packages to setup globally importable modules
# This is 3 levels deep as required by the static RPATH of ../../ from dependencies taken from Anaconda
site.addsitedir(os.path.join(os.path.dirname(os.path.realpath(__file__)), "libs", "site", "packages"))
# site.addsitedir(os.path.join(os.path.dirname(os.path.realpath(__file__)), "libs", "site", "packages"))
sys.path.insert(0, os.path.join(os.path.dirname(os.path.realpath(__file__)), "libs", "site", "packages"))
import blenderbim.bim
+9 -3
View File
@@ -17,6 +17,7 @@
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import bpy
import blenderbim
import importlib
from . import handler, ui, prop, operator, helper
@@ -70,6 +71,7 @@ modules = {
"covetool": None,
"augin": None,
"debug": None,
"ifcgit": None,
# Uncomment this line to enable loading of the demo module. Happy hacking!
# The name "demo" must correlate to a folder name in `bim/module/`.
# "demo": None,
@@ -152,7 +154,7 @@ def register():
bpy.app.handlers.redo_post.append(handler.redo_post)
bpy.app.handlers.load_post.append(handler.setDefaultProperties)
bpy.app.handlers.load_post.append(handler.loadIfcStore)
bpy.app.handlers.save_pre.append(handler.ensureIfcExported)
bpy.app.handlers.save_post.append(handler.ensureIfcExported)
bpy.types.Scene.BIMProperties = bpy.props.PointerProperty(type=prop.BIMProperties)
bpy.types.Object.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties)
bpy.types.Material.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties)
@@ -171,10 +173,14 @@ def register():
def unregister():
for cls in reversed(classes):
bpy.utils.unregister_class(cls)
if getattr(cls, "is_registered", None) is None:
bpy.utils.unregister_class(cls)
elif cls.is_registered:
bpy.utils.unregister_class(cls)
bpy.app.handlers.load_post.remove(handler.setDefaultProperties)
bpy.app.handlers.load_post.remove(handler.loadIfcStore)
bpy.app.handlers.save_pre.remove(handler.ensureIfcExported)
bpy.app.handlers.save_post.remove(handler.ensureIfcExported)
del bpy.types.Scene.BIMProperties
del bpy.types.Object.BIMObjectProperties
del bpy.types.Material.BIMObjectProperties
@@ -19,11 +19,13 @@
*/
* { stroke-linecap: round; stroke-linejoin: round; }
text { fill: black; stroke: none; }
text, tspan { /* 2.5mm */ fill: black; stroke: none; font-family: 'OpenGost Type B TT', 'DejaVu Sans Condensed', 'Liberation Sans', 'Arial Narrow', 'Arial'; font-size: 4.13px; }
.cut { fill: black; stroke: black; stroke-linecap: 'round'; stroke-width: 0.35; fill-rule: evenodd; }
.projection { fill: white; stroke: black; stroke-linecap: 'round'; stroke-width: 0.25; }
.surface { stroke: none; fill: #fff; fill-rule: evenodd; }
.annotation { fill: none; stroke: black; stroke-linecap: 'round'; stroke-width: 0.25; }
.IfcAnnotation { fill: none; stroke: black; stroke-linecap: 'round'; stroke-width: 0.25; }
.IfcGeographicElement { fill: none; stroke: black; stroke-linecap: 'round'; stroke-width: 1; }
.PredefinedType-LINEWORK { stroke: black; stroke-width: 0.25; }
.PredefinedType-BACKGROUND { stroke: black; stroke-width: 0.18; }
.PredefinedType-GRID { marker-start: url(#grid-marker); marker-end: url(#grid-marker); }
@@ -32,18 +34,27 @@ text { fill: black; stroke: none; }
.PredefinedType-PLANLEVEL { marker-end: url(#plan-level-marker); }
.PredefinedType-DIMENSION { marker-start: url(#dimension-marker-start); marker-end: url(#dimension-marker-end); }
.PredefinedType-ANGLE { marker-start: url(#angle-marker-start); marker-end: url(#angle-marker-end); }
.PredefinedType-RADIUS { marker-end: url(#radius-marker-end); }
.PredefinedType-RADIUS { marker-end: url(#fall-marker-end); }
.PredefinedType-FALL { marker-end: url(#radius-marker-end); }
.PredefinedType-SLOPEANGLE { marker-end: url(#radius-marker-end); }
.PredefinedType-SLOPEPERCENT { marker-end: url(#radius-marker-end); }
.PredefinedType-SLOPEFRACTION { marker-end: url(#radius-marker-end); }
.PredefinedType-DIAMETER { marker-start: url(#diameter-marker-start); marker-end: url(#diameter-marker-end); }
.PredefinedType-HIDDENLINE { stroke-dasharray: 3, 2; }
.PredefinedType-STAIRARROW { marker-start: url(#stair-marker-start); marker-end: url(#stair-marker-end); }
.PredefinedType-BOUNDARY { fill: none; stroke: red; stroke-width: 1; stroke-dasharray: 12, 4, 3, 4, 3, 4; }
.PredefinedType-BATTING { marker-start: url(#batting); stroke: none; }
.PredefinedType-SEALANT { fill: url(#crosshatch1); stroke-width: 0.25; }
.PredefinedType-FILLAREA { fill: white; stroke: none; }
.PredefinedType-FILLAREA { fill: white; stroke: black; }
.PredefinedType-BREAKLINE { fill: none; stroke: black; stroke-width: 0.25; marker-mid: url(#breakline-marker); }
.PredefinedType-TEXT { fill: black; stroke: none; }
path.PredefinedType-TEXTLEADER { marker-end: url(#leader-marker); }
text.PredefinedType-TEXTLEADER { fill: black; stroke: none; }
text.PredefinedType-TEXTLEADER, tspan.PredefinedType-TEXTLEADER { fill: black; stroke: none; }
text.title, tspan.title { /* 7mm */ font-size: 11.55px; }
text.header, tspan.header { /* 5mm */ font-size: 8.25px; }
text.large, tspan.large { /* 3.5mm */ font-size: 5.78px; }
text.regular, tspan.regular { /* 2.5mm */ font-size: 4.13px; }
text.small, tspan.small { /* 1.8mm */ font-size: 2.97px; }
text.GRID, tspan.GRID { /* 5mm */ font-size: 8.25px; }
.material-blank { fill: white; }
.material-diagonal1 { fill: url(#diagonal1); }
.material-diagonal2 { fill: url(#diagonal2); }
@@ -1,9 +1,7 @@
<?xml version="1.0" encoding="utf-8" ?>
<svg baseProfile="full" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink">
<marker id="batting" markerHeight="10" markerWidth="4" orient="auto" refX="0" refY="5" markerUnits="userSpaceOnUse">
<!-- Tip: https://svg-path-visualizer.netlify.app/ -->
<path style="fill: none; stroke:black; stroke-width:0.18" d="M 0 2 A 2 2 0 0 1 4 2 L 2 8 M 0 2 L 2 8 A 2 2 0 0 0 4 10 M 2 8 A 2 2 0 0 1 0 10" />
</marker>
<!-- Tip: https://www.svgviewer.dev/ -->
<marker id="eng-dimension-marker-start" markerHeight="14" markerWidth="11" orient="auto" refX="1" refY="7">
<g>
<path d="M 11 3.5 L 11 10.5 L 1 7" class="annotation" style="fill:black;" />
@@ -21,6 +19,12 @@
<path d="M 0 3.5 L 0 10.5 L 10 7" class="annotation" style="fill:black;" />
</g>
</marker>
<!-- same as for the radius -->
<marker id="fall-marker-end" markerHeight="14" markerWidth="11" orient="auto" refX="10" refY="7">
<g>
<path d="M 0 3.5 L 0 10.5 L 10 7" class="annotation" style="fill:black;" />
</g>
</marker>
<marker id="angle-marker-start" markerHeight="14" markerWidth="11" orient="auto" refX="1" refY="7">
<g>
<path d="M 11 3.5 L 11 10.5 L 1 7" class="annotation" style="fill:black;" />
@@ -92,4 +96,12 @@
<path d="M 7.5 10 L 10 0 L 15 10" class="annotation" style="stroke-width:1; fill: white;" />
</g>
</marker>
<filter x="0" y="0" width="1" height="1" id="fill-background">
<feFlood flood-color="white" result="bg" />
<feMerge>
<feMergeNode in="bg"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
</svg>

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

@@ -17,16 +17,49 @@
<line x1="0" y1="0" x2="0" y2="3" style="stroke:black; stroke-width:0.25" />
</pattern>
<pattern id="square1" width="1" height="1" patternUnits="userSpaceOnUse">
<line x1="0" y1="0" x2="0" y2="1" style="stroke:black; stroke-width:0.25" />
<line x1="0" y1="0" x2="1" y2="0" style="stroke:black; stroke-width:0.25" />
<path style="stroke: black; stroke-width: 0.25; fill: none;" d="M 0 0 1 0 1 1" />
</pattern>
<pattern id="square2" width="2" height="2" patternUnits="userSpaceOnUse">
<line x1="0" y1="0" x2="0" y2="2" style="stroke:black; stroke-width:0.25" />
<line x1="0" y1="0" x2="2" y2="0" style="stroke:black; stroke-width:0.25" />
<path style="stroke: black; stroke-width: 0.25; fill: none;" d="M 0 0 2 0 2 2" />
</pattern>
<pattern id="square3" width="3" height="3" patternUnits="userSpaceOnUse">
<line x1="0" y1="0" x2="0" y2="3" style="stroke:black; stroke-width:0.25" />
<line x1="0" y1="0" x2="3" y2="0" style="stroke:black; stroke-width:0.25" />
<path style="stroke: black; stroke-width: 0.25; fill: none;" d="M 0 0 3 0 3 3" />
</pattern>
<pattern id="recth1" width="2" height="1" patternUnits="userSpaceOnUse">
<path style="stroke: black; stroke-width: 0.25; fill: none;" d="M 0 0 2 0 2 1" />
</pattern>
<pattern id="recth2" width="4" height="2" patternUnits="userSpaceOnUse">
<path style="stroke: black; stroke-width: 0.25; fill: none;" d="M 0 0 4 0 4 2" />
</pattern>
<pattern id="recth3" width="6" height="3" patternUnits="userSpaceOnUse">
<path style="stroke: black; stroke-width: 0.25; fill: none;" d="M 0 0 6 0 6 3" />
</pattern>
<pattern id="rectv1" width="1" height="2" patternUnits="userSpaceOnUse">
<path style="stroke: black; stroke-width: 0.25; fill: none;" d="M 0 0 1 0 1 2" />
</pattern>
<pattern id="rectv2" width="2" height="4" patternUnits="userSpaceOnUse">
<path style="stroke: black; stroke-width: 0.25; fill: none;" d="M 0 0 2 0 2 4" />
</pattern>
<pattern id="rectv3" width="3" height="6" patternUnits="userSpaceOnUse">
<path style="stroke: black; stroke-width: 0.25; fill: none;" d="M 0 0 3 0 3 6" />
</pattern>
<pattern id="tile1" width="2" height="2" patternUnits="userSpaceOnUse">
<path style="stroke: black; stroke-width: 0.15; fill: none;" d="M 0 0 2 0 2 1 0 1 M 1 1 1 2" />
</pattern>
<pattern id="tile2" width="4" height="4" patternUnits="userSpaceOnUse">
<path style="stroke: black; stroke-width: 0.15; fill: none;" d="M 0 0 4 0 4 2 0 2 M 2 2 2 4" />
</pattern>
<pattern id="tile3" width="6" height="6" patternUnits="userSpaceOnUse">
<path style="stroke: black; stroke-width: 0.15; fill: none;" d="M 0 0 6 0 6 3 0 3 M 3 3 3 6" />
</pattern>
<pattern id="board1" width="4" height="2" patternUnits="userSpaceOnUse">
<path style="stroke: black; stroke-width: 0.15; fill: none;" d="M 0 0 4 0 4 1 0 1 M 2 1 2 2" />
</pattern>
<pattern id="board2" width="8" height="4" patternUnits="userSpaceOnUse">
<path style="stroke: black; stroke-width: 0.15; fill: none;" d="M 0 0 8 0 8 2 0 2 M 4 2 4 4" />
</pattern>
<pattern id="board3" width="12" height="6" patternUnits="userSpaceOnUse">
<path style="stroke: black; stroke-width: 0.15; fill: none;" d="M 0 0 12 0 12 3 0 3 M 6 3 6 6" />
</pattern>
<pattern id="crosshatch1" width="1" height="1" patternTransform="rotate(45 0 0)" patternUnits="userSpaceOnUse">
<line x1="0" y1="0" x2="0" y2="1" style="stroke:black; stroke-width:0.25" />

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 69 KiB

@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8" ?>
<svg baseProfile="full" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="rectangle-tag">
<rect x="-6" y="-2.5" width="12" height="5" fill="white" stroke="black" style="stroke-width: 0.25;" />
</g>
<g id="triangle-tag">
<path style="fill: white; stroke: black; stroke-width: 0.25;" d="M 3.1409425e-7,-7.1069207 5.7650623,2.8784603 l -11.5301246,-6e-7 z" />
</g>
<g id="hexagon-tag">
<path style="fill: white; stroke: black; stroke-width: 0.25;" d="m 0.0161133,-6.0583333 5.2339915,3.8366023 -2.031433,6.1633977 c 0,0 -6.4894866,-0.027414 -6.4894866,-0.027414 l -1.97929,-6.1803395 5.2662181,-3.7922467" />
</g>
<g id="capsule-tag">
<rect x="-6" y="-2.5" width="12" height="5" rx="2.5" ry="2.5" fill="white" stroke="black" style="stroke-width: 0.25;" />
</g>
<g id="circle-tag">
<circle r="5" fill="white" stroke="black" style="stroke-width: 0.25;" />
</g>
<g id="door-tag">
<circle r="5" fill="white" stroke="black" style="stroke-width: 0.25;" />
<text y="-1.75" class="regular" text-anchor="middle" dominant-baseline="middle" data-type="text-template"></text>
<line x1="-5" x2="5" style="stroke: black; stroke-width: 0.25;" />
<text y="2.5" class="regular" text-anchor="middle" dominant-baseline="middle" data-type="text-template"></text>
</g>
<g id="space-tag">
<text y="-5" class="large" text-anchor="middle" dominant-baseline="middle" data-type="text-template"></text>
<rect x="-6" y="-2.5" width="12" height="5" fill="white" stroke="black" style="stroke-width: 0.25;" />
<text class="regular" text-anchor="middle" dominant-baseline="middle" data-type="text-template"></text>
<text y="5" class="regular" text-anchor="middle" dominant-baseline="middle" data-type="text-template"></text>
</g>
<g id="elevation-arrow">
<path style="fill: black;" d="M -7.07106 0 0 -7.07106 7.07106 0" />
</g>
<g id="elevation-tag">
<circle r="5" fill="white" stroke="black" style="stroke-width: 0.25;" />
<line x1="-5" y1="0" x2="5" y2="0" style="stroke: black; stroke-width: 0.25;" />
</g>
<g id="section-arrow">
<path style="fill: black;" d="M -7.07106 0 0 -7.07106 7.07106 0" />
</g>
<g id="section-tag">
<circle r="5" fill="white" stroke="black" style="stroke-width: 0.25;" />
<line x1="-5" y1="0" x2="5" y2="0" style="stroke: black; stroke-width: 0.25;" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

@@ -1,67 +1,53 @@
<link href="jsgantt.css" rel="stylesheet" type="text/css"/>
<link href="../../module/sequence/gantt/main.css" rel="stylesheet" type="text/css"/>
<script src="jsgantt.js" type="text/javascript"></script>
<style>
.gtaskcellwkend,
.gtaskcellcurrent,
.gminorheadingwkend {
background-color: #e1e1e1;
}
.gitemhighlight td {
background-color: #ffdaaa;
}
.gtaskblue {
background: #4281A4;
}
.gtaskred {
background: #C1666B;
}
.gtaskgreen {
background: #48A9A6;
}
.gtaskyellow {
background: #D4B483;
}
.gmainleft {
overflow: visible;
flex: 0 1 auto;
}
</style>
<a href="#print" id="print">Print Mode</a>
<div style="position:relative" class="gantt" id="GanttChartDIV"></div>
<script type="text/javascript">
var g = new JSGantt.GanttChart(document.getElementById('GanttChartDIV'), 'day');
g.setOptions({
vCaptionType: 'Caption', // Set to Show Caption : None,Caption,Resource,Duration,Complete,
vQuarterColWidth: 36,
vDateTaskDisplayFormat: 'day dd month yyyy', // Shown in tool tip box
vDayMajorDateDisplayFormat: 'mon yyyy - Week ww',// Set format to dates in the "Major" header of the "Day" view
vWeekMinorDateDisplayFormat: 'dd mon', // Set format to display dates in the "Minor" header of the "Week" view
vLang: 'en',
vShowTaskInfoLink: 1, // Show link in tool tip (0/1)
vShowEndWeekDate: 0, // Show/Hide the date for the last day of the week in header for daily
vUseSingleCell: 10000, // Set the threshold cell per table row (Helps performance for large data.
vFormatArr: ['Day', 'Week', 'Month', 'Quarter'], // Even with setUseSingleCell using Hour format on such a large chart can cause issues in some browsers,
vShowRes: false, // Disable the resource column.
vShowComp: false, // Disable the completion column.
vShowDur: false, // Disable the duration column, because jsgantt doesn't calculate durations the way we want.
vAdditionalHeaders: {ifcduration: {title: 'Duration'}},
vUseToolTip: false, // Disable tooltips.
vTotalHeight: 900,
});
document.getElementById('print').addEventListener('click', function() {
g.setTotalHeight("");
g.Draw();
});
var json_data = `
{{{json_data}}}
`;
JSGantt.parseJSONString(json_data, g);
g.Draw();
<script type="text/javascript" src="../../module/sequence/gantt/index.js"></script>
<script>
var json_data = `{{{json_data}}}`;
</script>
<div class="no-print">
<h4>Choose a language:
<select id="lang" class="sweet_button" onchange="set_language(event)">
<option value='cn'>Chinese (cn)</option>
<option value='cs'>Czech (cs)</option>
<option value='nl'>Dutch (Standard)</option>
<option value='en' selected>English (en)</option>
<option value='fr'>French (fr)</option>
<option value='de'>German (de)</option>
<option value='hu'>Hungarian (hu)</option>
<option value='id'>Indonesian (id)</option>
<option value='it'>Italian (it)</option>
<option value='ja'>Japanese (ja)</option>
<option value='pt'>Portuguese (pt)</option>
<option value='ru'>Russian (ru)</option>
<option value='es'>Spanish (es)</option>
<option value='sv'>Swedish (sv)</option>
<option value='tr'>Turkish (tr)</option>
</select>
<h4>
<div id="print_options">
<select class="sweet_button" id="print_page_size">
<option value="210,297">A4 Portrait</option>
<option value="297,210">A4 Landscape</option>
<option value="297,420">A3 Portrait</option>
<option value="420,297">A3 Landscape</option>
<option value="420,594">A2 Portrait</option>
<option value="594,420">A2 Landscape</option>
<option value="594,841">A1 Portrait</option>
<option value="841,594">A1 Landscape</option>
<option value="841,1189">A0 Portrait</option>
<option value="1189,841">A0 Landscape</option>
</select>
</div>
<br>
</div>
<div id="schedule-data"></div>
<div style="position:relative" class="gantt" id="GanttChartDIV"></div>
<script>
var name = `{{{name}}}`;
setupPage(name);
create_gantt_chart(json_data)
</script>
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
@@ -1,13 +1,13 @@
ISO-10303-21;
HEADER;
FILE_DESCRIPTION(('ViewDefinition[DesignTransferView]'),'2;1');
FILE_NAME('/dev/null','2022-10-14T18:00:26+11:00',(),(),'IfcOpenShell v0.7.0-dc67287d','IfcOpenShell v0.7.0-dc67287d','Nobody');
FILE_NAME('/dev/null','2023-02-04T13:08:05+11:00',(),(),'IfcOpenShell v0.7.0-07ee62eb9','IfcOpenShell v0.7.0-07ee62eb9','Nobody');
FILE_SCHEMA(('IFC4'));
ENDSEC;
DATA;
#1=IFCPROJECT('0yOx2IKFL4HAbf$IjzE0Kp',$,'BlenderBIM Demo',$,$,$,$,(#12,#16),#7);
#2=IFCPROJECTLIBRARY('2jUXWJsnH2pxWl9Bl3Ljhd',$,'BlenderBIM Demo Library',$,$,$,$,$,$);
#3=IFCRELDECLARES('0Rn4fWoen7xB9hskGGEfEy',$,$,$,#1,(#2));
#1=IFCPROJECT('3ubSuEuavEPfXTf4v0gmbA',$,'BlenderBIM Demo',$,$,$,$,(#12,#16),#7);
#2=IFCPROJECTLIBRARY('1Qq43hq0v3B9Ko7EGW0hJ5',$,'BlenderBIM Demo Library',$,$,$,$,$,$);
#3=IFCRELDECLARES('0hU2lTJfX70AKkhyURrbam',$,$,$,#1,(#2));
#4=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.);
#5=IFCSIUNIT(*,.AREAUNIT.,$,.SQUARE_METRE.);
#6=IFCSIUNIT(*,.VOLUMEUNIT.,$,.CUBIC_METRE.);
@@ -17,89 +17,89 @@ DATA;
#10=IFCDIRECTION((1.,0.,0.));
#11=IFCAXIS2PLACEMENT3D(#8,#9,#10);
#12=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-05,#11,$);
#13=IFCCARTESIANPOINT((0.,0.,0.));
#14=IFCDIRECTION((1.,0.,0.));
#13=IFCCARTESIANPOINT((0.,0.));
#14=IFCDIRECTION((1.,0.));
#15=IFCAXIS2PLACEMENT2D(#13,#14);
#16=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Plan',2,1.E-05,#15,$);
#17=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Body','Model',*,*,*,*,#12,$,.MODEL_VIEW.,$);
#18=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Annotation','Plan',*,*,*,*,#16,$,.PLAN_VIEW.,$);
#18=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Body','Plan',*,*,*,*,#16,$,.PLAN_VIEW.,$);
#19=IFCMATERIAL('Unknown',$,$);
#20=IFCWALLTYPE('0D2QFqmA18fvA6TUteIRXc',$,'WAL50',$,$,$,$,$,$,.NOTDEFINED.);
#20=IFCWALLTYPE('3yGmcLQT96CPWJXcV3vNPJ',$,'WAL50',$,$,$,$,$,$,.NOTDEFINED.);
#21=IFCMATERIALLAYERSET((#23),$,$);
#22=IFCRELASSOCIATESMATERIAL('0WbN7alqv8ix3w4MOdyl8F',$,$,$,(#20),#21);
#22=IFCRELASSOCIATESMATERIAL('0JMzROKjj7i8EKtMLZ2nXy',$,$,$,(#20),#21);
#23=IFCMATERIALLAYER(#19,0.05,$,$,$,$,$);
#24=IFCRELDECLARES('13kodoVGr08Bn7WfGWndV_',$,$,$,#2,(#37,#41,#76,#47,#58,#91,#25,#225,#71,#159,#29,#62,#95,#20,#53,#86,#33,#66,#81));
#25=IFCWALLTYPE('0qRxb2OqjDEfFI9i7b6qAw',$,'WAL100',$,$,$,$,$,$,.NOTDEFINED.);
#24=IFCRELDECLARES('1TTgyh7ynBQ8XpgpGSU6ao',$,$,$,#2,(#41,#76,#47,#58,#91,#25,#159,#225,#71,#29,#62,#95,#20,#53,#86,#33,#66,#81,#37));
#25=IFCWALLTYPE('20jY5HfLH4LBwMrDEoTdML',$,'WAL100',$,$,$,$,$,$,.NOTDEFINED.);
#26=IFCMATERIALLAYERSET((#28),$,$);
#27=IFCRELASSOCIATESMATERIAL('3BDm6HH553QhwCW7FPmjrR',$,$,$,(#25),#26);
#27=IFCRELASSOCIATESMATERIAL('2sICOUhur5WgrNUOi1pvhV',$,$,$,(#25),#26);
#28=IFCMATERIALLAYER(#19,0.1,$,$,$,$,$);
#29=IFCWALLTYPE('1KMENjmzj7cf_am9SYEaXW',$,'WAL200',$,$,$,$,$,$,.NOTDEFINED.);
#29=IFCWALLTYPE('0xhGqQenH5Ov9L_CiLywqN',$,'WAL200',$,$,$,$,$,$,.NOTDEFINED.);
#30=IFCMATERIALLAYERSET((#32),$,$);
#31=IFCRELASSOCIATESMATERIAL('1$36L06DrDYx1sZ9tBQ_WE',$,$,$,(#29),#30);
#31=IFCRELASSOCIATESMATERIAL('2pOhWSsWT4bQwtduxS8HNJ',$,$,$,(#29),#30);
#32=IFCMATERIALLAYER(#19,0.2,$,$,$,$,$);
#33=IFCWALLTYPE('2y5Y19etz3rhrt8P45fuGA',$,'WAL300',$,$,$,$,$,$,.NOTDEFINED.);
#33=IFCWALLTYPE('1HrJFcNVH4XQJIRrEmUD6Z',$,'WAL300',$,$,$,$,$,$,.NOTDEFINED.);
#34=IFCMATERIALLAYERSET((#36),$,$);
#35=IFCRELASSOCIATESMATERIAL('1WSONmheTD9xhiluqtfktr',$,$,$,(#33),#34);
#35=IFCRELASSOCIATESMATERIAL('0cAK6yBnXAnO$5LYcU3QQl',$,$,$,(#33),#34);
#36=IFCMATERIALLAYER(#19,0.3,$,$,$,$,$);
#37=IFCCOVERINGTYPE('1pAuiYopL5hxCATiIPV8QG',$,'COV10',$,$,$,$,$,$,.NOTDEFINED.);
#37=IFCCOVERINGTYPE('11MVutxgn20fTEibOhUiS8',$,'COV10',$,$,$,$,$,$,.NOTDEFINED.);
#38=IFCMATERIALLAYERSET((#40),$,$);
#39=IFCRELASSOCIATESMATERIAL('0ysRf97LT8fw28mDwt2vop',$,$,$,(#37),#38);
#39=IFCRELASSOCIATESMATERIAL('1$dvrpTAn6oPSVqcMtsvmV',$,$,$,(#37),#38);
#40=IFCMATERIALLAYER(#19,0.01,$,$,$,$,$);
#41=IFCCOVERINGTYPE('00LbT8PQf3XwImYkj_i_$4',$,'COV20',$,$,(#45),$,$,$,.NOTDEFINED.);
#41=IFCCOVERINGTYPE('1DWvr7poP3ggxFE7vxOot2',$,'COV20',$,$,(#45),$,$,$,.NOTDEFINED.);
#42=IFCMATERIALLAYERSET((#44),$,$);
#43=IFCRELASSOCIATESMATERIAL('0Kvr3quG19xw6$528t4NQi',$,$,$,(#41),#42);
#43=IFCRELASSOCIATESMATERIAL('2uf2C_54501xFgLlk_jqMG',$,$,$,(#41),#42);
#44=IFCMATERIALLAYER(#19,0.02,$,$,$,$,$);
#45=IFCPROPERTYSET('1IZqsXecj8NQZcNAIAY48P',$,'EPset_Parametric',$,(#46));
#45=IFCPROPERTYSET('0fSGHPSBH9U8H_ph5xpH65',$,'EPset_Parametric',$,(#46));
#46=IFCPROPERTYSINGLEVALUE('LayerSetDirection',$,IFCLABEL('AXIS2'),$);
#47=IFCCOVERINGTYPE('10YNsLMi9409CbEVFmDkJt',$,'COV30',$,$,(#51),$,$,$,.NOTDEFINED.);
#47=IFCCOVERINGTYPE('1ayqDfj8XDxB$G7ZFyRagM',$,'COV30',$,$,(#51),$,$,$,.NOTDEFINED.);
#48=IFCMATERIALLAYERSET((#50),$,$);
#49=IFCRELASSOCIATESMATERIAL('2jZVWXllT9ugYrdBaIBAnt',$,$,$,(#47),#48);
#49=IFCRELASSOCIATESMATERIAL('01vVE$oHTA3BLmh2imRGuu',$,$,$,(#47),#48);
#50=IFCMATERIALLAYER(#19,0.03,$,$,$,$,$);
#51=IFCPROPERTYSET('2M2_M1eSXFYR8vFE96Ecy1',$,'EPset_Parametric',$,(#52));
#51=IFCPROPERTYSET('3v7cOUGgT0qxrxlQkzwSIR',$,'EPset_Parametric',$,(#52));
#52=IFCPROPERTYSINGLEVALUE('LayerSetDirection',$,IFCLABEL('AXIS3'),$);
#53=IFCRAMPTYPE('09P6PVRiT44vIjup23Olfq',$,'RAM200',$,$,$,$,$,$,.NOTDEFINED.);
#53=IFCRAMPTYPE('0VEv7VZVnDzxHXC$QE2Crm',$,'RAM200',$,$,$,$,$,$,.NOTDEFINED.);
#54=IFCMATERIALLAYERSET((#56),$,$);
#55=IFCRELASSOCIATESMATERIAL('2Ghy1OMS1FLAOmdzrFp4rp',$,$,$,(#53),#54);
#55=IFCRELASSOCIATESMATERIAL('0ToQjR2aLAdgGPuqu6UH1o',$,$,$,(#53),#54);
#56=IFCMATERIALLAYER(#19,0.2,$,$,$,$,$);
#57=IFCCIRCLEPROFILEDEF(.AREA.,$,$,0.3);
#58=IFCPILETYPE('0X0OrCsUnDUPVeAu90N_K$',$,'P1',$,$,$,$,$,$,.NOTDEFINED.);
#58=IFCPILETYPE('26kmCVC0H4lOhCGn6cCLSQ',$,'P1',$,$,$,$,$,$,.NOTDEFINED.);
#59=IFCMATERIALPROFILESET($,$,(#61),$);
#60=IFCRELASSOCIATESMATERIAL('1FiRvXL$nBkuT8B3FfiGT9',$,$,$,(#58),#59);
#60=IFCRELASSOCIATESMATERIAL('2Ev3ucMBPDtwntRLG_4cBU',$,$,$,(#58),#59);
#61=IFCMATERIALPROFILE($,$,#19,#57,$,$);
#62=IFCSLABTYPE('1helZ086v1o8JD3Ow1eWAy',$,'FLR150',$,$,$,$,$,$,.NOTDEFINED.);
#62=IFCSLABTYPE('3cLRr85lHDAPjHswaO7574',$,'FLR150',$,$,$,$,$,$,.NOTDEFINED.);
#63=IFCMATERIALLAYERSET((#65),$,$);
#64=IFCRELASSOCIATESMATERIAL('3BbM02xKH6h97dBaMJp$Rr',$,$,$,(#62),#63);
#64=IFCRELASSOCIATESMATERIAL('07afdGZLHCFej$qXPfCLn9',$,$,$,(#62),#63);
#65=IFCMATERIALLAYER(#19,0.2,$,$,$,$,$);
#66=IFCSLABTYPE('1YAagoi$9D_hPiWtssbkcG',$,'FLR250',$,$,$,$,$,$,.NOTDEFINED.);
#66=IFCSLABTYPE('2WiuelwGf3Ah3gnVgDGEAn',$,'FLR250',$,$,$,$,$,$,.NOTDEFINED.);
#67=IFCMATERIALLAYERSET((#69),$,$);
#68=IFCRELASSOCIATESMATERIAL('0m1oulHjT1u889pxJG$XG_',$,$,$,(#66),#67);
#68=IFCRELASSOCIATESMATERIAL('03XoY4bYD4ZwZgvg3JqIIK',$,$,$,(#66),#67);
#69=IFCMATERIALLAYER(#19,0.3,$,$,$,$,$);
#70=IFCRECTANGLEPROFILEDEF(.AREA.,$,$,0.5,0.6);
#71=IFCCOLUMNTYPE('1lzPzyyFfFYRgjqMHnKZLx',$,'C1',$,$,$,$,$,$,.NOTDEFINED.);
#70=IFCRECTANGLEPROFILEDEF(.AREA.,'500x600',$,0.5,0.6);
#71=IFCCOLUMNTYPE('187MdL9O13_OzlwsOaT0zY',$,'C1',$,$,$,$,$,$,.NOTDEFINED.);
#72=IFCMATERIALPROFILESET($,$,(#74),$);
#73=IFCRELASSOCIATESMATERIAL('26fuP2VpfEnhlUsk0K1i4y',$,$,$,(#71),#72);
#73=IFCRELASSOCIATESMATERIAL('1CMVTn08P2Tu8afsZHQiUk',$,$,$,(#71),#72);
#74=IFCMATERIALPROFILE($,$,#19,#70,$,$);
#75=IFCCIRCLEHOLLOWPROFILEDEF(.AREA.,$,$,0.25,0.005);
#76=IFCCOLUMNTYPE('1qqu_K2Az3yvZSOapZmUrZ',$,'C2',$,$,$,$,$,$,.NOTDEFINED.);
#75=IFCCIRCLEHOLLOWPROFILEDEF(.AREA.,'500.0x5.0 CHS',$,0.25,0.005);
#76=IFCCOLUMNTYPE('3e6tIcskjDOuj1_UAWcT87',$,'C2',$,$,$,$,$,$,.NOTDEFINED.);
#77=IFCMATERIALPROFILESET($,$,(#79),$);
#78=IFCRELASSOCIATESMATERIAL('2YQz9JCGXAogjkmN3RZeKI',$,$,$,(#76),#77);
#78=IFCRELASSOCIATESMATERIAL('0ahT30pYb7zQ968gWY9q1A',$,$,$,(#76),#77);
#79=IFCMATERIALPROFILE($,$,#19,#75,$,$);
#80=IFCRECTANGLEHOLLOWPROFILEDEF(.AREA.,$,$,0.075,0.15,0.005,0.005,0.005);
#81=IFCCOLUMNTYPE('1rnXRgQ3X6pfk5O9j4HCLm',$,'C3',$,$,$,$,$,$,.NOTDEFINED.);
#80=IFCRECTANGLEHOLLOWPROFILEDEF(.AREA.,'150x75x2.0 RHS',$,0.075,0.15,0.002,0.005,0.005);
#81=IFCCOLUMNTYPE('0ZxXYg0BTFg8sbqXr50qPW',$,'C3',$,$,$,$,$,$,.NOTDEFINED.);
#82=IFCMATERIALPROFILESET($,$,(#84),$);
#83=IFCRELASSOCIATESMATERIAL('2CQLZ1qlzA9uN2eRrpg99c',$,$,$,(#81),#82);
#83=IFCRELASSOCIATESMATERIAL('0ul_xSpIH2MxsZcv$ZFXG7',$,$,$,(#81),#82);
#84=IFCMATERIALPROFILE($,$,#19,#80,$,$);
#85=IFCISHAPEPROFILEDEF(.AREA.,'DEMO-I',$,0.1,0.2,0.005,0.01,0.005,$,$);
#86=IFCBEAMTYPE('1xjrF860f2H9zb04Ui98Rt',$,'B1',$,$,$,$,$,$,.NOTDEFINED.);
#86=IFCBEAMTYPE('06Td_2IhD5OgyKW34Ikv$Z',$,'B1',$,$,$,$,$,$,.NOTDEFINED.);
#87=IFCMATERIALPROFILESET($,$,(#89),$);
#88=IFCRELASSOCIATESMATERIAL('03i9c81690fx2ZTBBXcqkL',$,$,$,(#86),#87);
#88=IFCRELASSOCIATESMATERIAL('2cT5kDtU11jv2sFtIKVfJa',$,$,$,(#86),#87);
#89=IFCMATERIALPROFILE($,$,#19,#85,$,$);
#90=IFCCSHAPEPROFILEDEF(.AREA.,'DEMO-C',$,0.2,0.1,0.0015,0.03,0.005);
#91=IFCBEAMTYPE('3mAc_7AGP06w4CzYOlYVL2',$,'B2',$,$,$,$,$,$,.NOTDEFINED.);
#91=IFCBEAMTYPE('1JyktsoBP8DgMbJiECPZ6d',$,'B2',$,$,$,$,$,$,.NOTDEFINED.);
#92=IFCMATERIALPROFILESET($,$,(#94),$);
#93=IFCRELASSOCIATESMATERIAL('2xvXgaSvL7KPR25fXz0fSE',$,$,$,(#91),#92);
#93=IFCRELASSOCIATESMATERIAL('2$_YKuP914sBZmflM_8l1s',$,$,$,(#91),#92);
#94=IFCMATERIALPROFILE($,$,#19,#90,$,$);
#95=IFCWINDOWTYPE('1kxjrhA4P2dwwueZ0_nSuu',$,'WT01',$,$,$,(#136,#158),$,$,.NOTDEFINED.,.NOTDEFINED.,$,$);
#95=IFCWINDOWTYPE('0g10V5zVP8c96j3mPV0Xvn',$,'WT01',$,$,$,(#136,#158),$,$,.NOTDEFINED.,.NOTDEFINED.,$,$);
#96=IFCINDEXEDPOLYGONALFACE((13,17,18,14));
#97=IFCINDEXEDPOLYGONALFACE((5,6,3,4));
#98=IFCINDEXEDPOLYGONALFACE((7,8,2,1));
@@ -157,13 +157,13 @@ DATA;
#150=IFCINDEXEDPOLYCURVE(#137,(IFCLINEINDEX((5,11))),$);
#151=IFCINDEXEDPOLYCURVE(#137,(IFCLINEINDEX((6,12))),$);
#152=IFCGEOMETRICCURVESET((#138,#139,#140,#141,#142,#143,#144,#145,#146,#147,#148,#149,#150,#151));
#153=IFCSHAPEREPRESENTATION(#18,'Annotation','Annotation2D',(#152));
#153=IFCSHAPEREPRESENTATION(#18,'Body','Annotation2D',(#152));
#154=IFCCARTESIANPOINT((0.,0.,0.));
#155=IFCDIRECTION((1.,0.,0.));
#156=IFCDIRECTION((0.,0.,1.));
#157=IFCAXIS2PLACEMENT3D(#154,#156,#155);
#158=IFCREPRESENTATIONMAP(#157,#153);
#159=IFCDOORTYPE('0s370BWH51r8Xg03pJ5OBE',$,'DT01',$,$,$,(#204,#224),$,$,.NOTDEFINED.,.NOTDEFINED.,$,$);
#159=IFCDOORTYPE('0nu4v3oPPFmQIz2RA1Vp_t',$,'DT01',$,$,$,(#204,#224),$,$,.NOTDEFINED.,.NOTDEFINED.,$,$);
#160=IFCINDEXEDPOLYGONALFACE((17,16,15,14));
#161=IFCINDEXEDPOLYGONALFACE((2,3,29,28));
#162=IFCINDEXEDPOLYGONALFACE((27,28,29,30,32,31));
@@ -223,13 +223,13 @@ DATA;
#216=IFCINDEXEDPOLYCURVE(#205,(IFCLINEINDEX((11,15))),$);
#217=IFCINDEXEDPOLYCURVE(#205,(IFCLINEINDEX((17,18)),IFCLINEINDEX((18,19)),IFCLINEINDEX((19,20)),IFCLINEINDEX((20,21)),IFCLINEINDEX((21,22)),IFCLINEINDEX((22,23)),IFCLINEINDEX((23,24)),IFCLINEINDEX((24,25)),IFCLINEINDEX((25,26)),IFCLINEINDEX((26,27)),IFCLINEINDEX((27,28)),IFCLINEINDEX((28,29))),$);
#218=IFCGEOMETRICCURVESET((#206,#207,#208,#209,#210,#211,#212,#213,#214,#215,#216,#217));
#219=IFCSHAPEREPRESENTATION(#18,'Annotation','Annotation2D',(#218));
#219=IFCSHAPEREPRESENTATION(#18,'Body','Annotation2D',(#218));
#220=IFCCARTESIANPOINT((0.,0.,0.));
#221=IFCDIRECTION((1.,0.,0.));
#222=IFCDIRECTION((0.,0.,1.));
#223=IFCAXIS2PLACEMENT3D(#220,#222,#221);
#224=IFCREPRESENTATIONMAP(#223,#219);
#225=IFCFURNITURETYPE('227RrM$yD30RGzlTgbBH3c',$,'BUN01',$,$,$,(#946,#988),$,$,.NOTDEFINED.,.NOTDEFINED.);
#225=IFCFURNITURETYPE('2YuVxsKJL9URJtwIhHY2GN',$,'BUN01',$,$,$,(#946,#988),$,$,.NOTDEFINED.,.NOTDEFINED.);
#226=IFCINDEXEDPOLYGONALFACE((187,278,44));
#227=IFCINDEXEDPOLYGONALFACE((21,52,60));
#228=IFCINDEXEDPOLYGONALFACE((91,100,31));
@@ -987,7 +987,7 @@ DATA;
#980=IFCINDEXEDPOLYCURVE(#947,(IFCLINEINDEX((60,61)),IFCLINEINDEX((61,62)),IFCLINEINDEX((62,63))),$);
#981=IFCINDEXEDPOLYCURVE(#947,(IFCLINEINDEX((64,31))),$);
#982=IFCGEOMETRICCURVESET((#948,#949,#950,#951,#952,#953,#954,#955,#956,#957,#958,#959,#960,#961,#962,#963,#964,#965,#966,#967,#968,#969,#970,#971,#972,#973,#974,#975,#976,#977,#978,#979,#980,#981));
#983=IFCSHAPEREPRESENTATION(#18,'Annotation','Annotation2D',(#982));
#983=IFCSHAPEREPRESENTATION(#18,'Body','Annotation2D',(#982));
#984=IFCCARTESIANPOINT((0.,0.,0.));
#985=IFCDIRECTION((1.,0.,0.));
#986=IFCDIRECTION((0.,0.,1.));
File diff suppressed because one or more lines are too long
@@ -1,13 +0,0 @@
ISO-10303-21;
HEADER;
FILE_DESCRIPTION((),'2;1');
FILE_NAME('EPset_Annotation.ifc','2020-01-01T00:00:00',(),(),'EPset_Annotation','EPset_Annotation',$);
FILE_SCHEMA(('IFC4'));
ENDSEC;
DATA;
#1=IFCPROPERTYSETTEMPLATE('3VuPUwdCD2Qx3XDDRs0R1N',$,'EPset_Annotation','',.PSET_OCCURRENCEDRIVEN.,'IfcAnnotation',(#2,#3));
#2=IFCSIMPLEPROPERTYTEMPLATE('2P7JN79n96Q9pElZ83LKe4',$,'ZIndex','',.P_SINGLEVALUE.,'IfcInteger',$,$,$,$,$,.READWRITE.);
#3=IFCSIMPLEPROPERTYTEMPLATE('1Wpx_r2xj1_9w5JpI0QRJy',$,'Symbol','',.P_ENUMERATEDVALUE.,'IfcLabel',$,#4,$,$,$,.READWRITE.);
#4=IFCPROPERTYENUMERATION('Symbol',(IFCLABEL('rectangle-tag'), IFCLABEL('door-tag')),$);
ENDSEC;
END-ISO-10303-21;
@@ -5,20 +5,24 @@ FILE_NAME('EPset_Drawing.ifc','2020-01-01T00:00:00',(),(),'EPset_Drawing','EPset
FILE_SCHEMA(('IFC4'));
ENDSEC;
DATA;
#1=IFCPROPERTYSETTEMPLATE('2JhNIvqZrFnAgxfhK0XVQX',$,'EPset_Drawing','',.PSET_OCCURRENCEDRIVEN.,'IfcAnnotation',(#2,#3,#4,#5,#6,#7,#8,#9,#10,#11,#12,#13,#14,#15));
#1=IFCPROPERTYSETTEMPLATE('2JhNIvqZrFnAgxfhK0XVQX',$,'EPset_Drawing','',.PSET_OCCURRENCEDRIVEN.,'IfcAnnotation',(#2,#3,#4,#5,#6,#7,#8,#9,#10,#11,#12,#13,#14,#15,#16,#17,#18,#19));
#2=IFCSIMPLEPROPERTYTEMPLATE('23JavTMk98ZxXhrUEnjAcf',$,'TargetView','',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#3=IFCSIMPLEPROPERTYTEMPLATE('1yVWUt5H9DAOuu0OaMMLpe',$,'Scale','',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#4=IFCSIMPLEPROPERTYTEMPLATE('3gsuPBtU93b8f0gg1pjkq6',$,'HumanScale','',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#3=IFCSIMPLEPROPERTYTEMPLATE('1yVWUt5H9DAOuu0OaMMLpe',$,'Scale','The scale of this drawing represented as a numerator and denominator, such as 1/100',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#4=IFCSIMPLEPROPERTYTEMPLATE('3gsuPBtU93b8f0gg1pjkq6',$,'HumanScale','The scale of this drawing in human readable format, such as 1:100',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#5=IFCSIMPLEPROPERTYTEMPLATE('0AK5C2UpL4$eaac2LszAx$',$,'HasUnderlay','',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
#6=IFCSIMPLEPROPERTYTEMPLATE('2j2ZEZR8X5tONm7kli5hM6',$,'HasLinework','',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
#7=IFCSIMPLEPROPERTYTEMPLATE('1ttChRysH9UuEX2FeMj5Hu',$,'HasAnnotation','',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
#8=IFCSIMPLEPROPERTYTEMPLATE('2NPPxuABv1huDTVh32TFgw',$,'GlobalReferencing','',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
#9=IFCSIMPLEPROPERTYTEMPLATE('10hT_1zrzEbRRKMXYAWvtD',$,'Metadata','',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
#10=IFCSIMPLEPROPERTYTEMPLATE('3Z0BXPSG5CWgtI33ioV7aj',$,'Include','',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
#11=IFCSIMPLEPROPERTYTEMPLATE('1RVts_g3PAw98PJA2yL3bO',$,'Exclude','',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
#10=IFCSIMPLEPROPERTYTEMPLATE('3Z0BXPSG5CWgtI33ioV7aj',$,'Include','Selector expression to include ifc elements in the drawing',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
#11=IFCSIMPLEPROPERTYTEMPLATE('1RVts_g3PAw98PJA2yL3bO',$,'Exclude','Selector expression to exclude ifc elements in the drawing',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
#12=IFCSIMPLEPROPERTYTEMPLATE('0c1$8NpYDEaBiJrj16jHIo',$,'Stylesheet','',.P_SINGLEVALUE.,'IfcURIReference',$,$,$,$,$,.READWRITE.);
#13=IFCSIMPLEPROPERTYTEMPLATE('3mRF52q81FQB$h4oTh7M45',$,'Markers','',.P_SINGLEVALUE.,'IfcURIReference',$,$,$,$,$,.READWRITE.);
#14=IFCSIMPLEPROPERTYTEMPLATE('1rhr_0N3LDtuORcEJP0KXM',$,'Symbols','',.P_SINGLEVALUE.,'IfcURIReference',$,$,$,$,$,.READWRITE.);
#15=IFCSIMPLEPROPERTYTEMPLATE('2sHDBuW7P4TROy$hL2w7ct',$,'Patterns','',.P_SINGLEVALUE.,'IfcURIReference',$,$,$,$,$,.READWRITE.);
#16=IFCSIMPLEPROPERTYTEMPLATE('1$xfo9EVb26QLqmPll2_RK',$,'MetricPrecision','',.P_SINGLEVALUE.,'IfcReal',$,$,$,$,$,.READWRITE.);
#17=IFCSIMPLEPROPERTYTEMPLATE('38uAtrp9nD_901NO42zd$7',$,'ImperialPrecision','',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#18=IFCSIMPLEPROPERTYTEMPLATE('1MX0uffTL6TOvtvEJpxFmk',$,'DecimalPlaces','',.P_SINGLEVALUE.,'IfcInteger',$,$,$,$,$,.READWRITE.);
#19=IFCSIMPLEPROPERTYTEMPLATE('0joEq0Rd10cxweEh0NeHT6',$,'JoinCriteria','Comma separated selection keys which determine what cut objects are to be joined.',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
ENDSEC;
END-ISO-10303-21;
@@ -0,0 +1,34 @@
ISO-10303-21;
HEADER;
FILE_DESCRIPTION((),'2;1');
FILE_NAME('Psets_BBIM_Annotation.ifc','2020-01-01T00:00:00',(),(),'Psets_BBIM_Annotation','Psets_BBIM_Annotation',$);
FILE_SCHEMA(('IFC4'));
ENDSEC;
DATA;
#1=IFCPROPERTYSETTEMPLATE('3VuPUwdCD2Qx3XDDRs0R1N',$,'EPset_Annotation','',.PSET_OCCURRENCEDRIVEN.,'IfcAnnotation,IfcTypeProduct',(#2,#3,#4));
#2=IFCSIMPLEPROPERTYTEMPLATE('2P7JN79n96Q9pElZ83LKe4',$,'ZIndex','',.P_SINGLEVALUE.,'IfcInteger',$,$,$,$,$,.READWRITE.);
#3=IFCSIMPLEPROPERTYTEMPLATE('1Wpx_r2xj1_9w5JpI0QRJy',$,'Symbol','',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#4=IFCSIMPLEPROPERTYTEMPLATE('3q0oxMUKP47vZ4jnyG$dDb',$,'Classes','Classes separarated by spaces that end up in classes for this element in svg. Can be used to specify the text font size: small - 1.8mm; regular - 2.5mm; large - 3.5mm; header - 5mm; title - 7mm. By default regular size is used.',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#5=IFCPROPERTYSETTEMPLATE('0iKwujnQL9IevVQato8f7Z',$,'BBIM_Batting','',.PSET_OCCURRENCEDRIVEN.,'IfcAnnotation,IfcTypeProduct',(#6,#7));
#6=IFCSIMPLEPROPERTYTEMPLATE('0t2LEesGT1QRQtrIZUAR8L',$,'Thickness','Batting thickness',.P_SINGLEVALUE.,'IfcPositiveLengthMeasure',$,$,$,$,$,.READWRITE.);
#7=IFCSIMPLEPROPERTYTEMPLATE('082PndS6v2kBOiJoSboMnh',$,'Reverse pattern direction','Reverse batting pattern (swap starting and ending points)',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
#8=IFCPROPERTYSETTEMPLATE('1Dx2EiZnP67xotInXpwz90',$,'BBIM_Section','',.PSET_OCCURRENCEDRIVEN.,'IfcAnnotation,IfcTypeProduct',(#9,#10,#11,#12,#13));
#9=IFCSIMPLEPROPERTYTEMPLATE('2a_9s8spHDc9dZHtgg71XL',$,'ShowStartArrow','Display start arrow.',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
#10=IFCSIMPLEPROPERTYTEMPLATE('3i6SH_GbT7zhKea$wVE56A',$,'StartArrowSymbol','Custom symbol for the start of the section marker arrow. Need to make sure it''s present in "symbols.svg".',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#11=IFCSIMPLEPROPERTYTEMPLATE('1DtsPn5a9FG8$zXHDDMavY',$,'ShowEndArrow','Display end arrow.',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
#12=IFCSIMPLEPROPERTYTEMPLATE('2$6U0mLI9AiPRWeBabdY3u',$,'EndArrowSymbol','Custom symbol for the end of the section marker arrow. Need to make sure it''s present in "symbols.svg".',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#13=IFCSIMPLEPROPERTYTEMPLATE('1naFqntIL7igCY7hCaE7kq',$,'HasConnectedSectionLine','Connect or disconnect section markers with line (by default = True).',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
#14=IFCPROPERTYSETTEMPLATE('3V8oZ8YRD3_O7uR5vcUleS',$,'BBIM_Documentation','',.PSET_OCCURRENCEDRIVEN.,'IfcProject',(#15,#16,#17,#18,#19,#20,#21,#22));
#15=IFCSIMPLEPROPERTYTEMPLATE('0ulAhgk3v9qfGlDILauR6J',$,'SheetsDir','Default sheets directory',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#16=IFCSIMPLEPROPERTYTEMPLATE('2yvlVKiQXASucfH40deCvu',$,'LayoutsDir','Default layouts directory',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#17=IFCSIMPLEPROPERTYTEMPLATE('2kXZqXicL3jRwsOnLM_0ho',$,'TitleblocksDir','Default titleblocks directory',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#18=IFCSIMPLEPROPERTYTEMPLATE('1lmOfo9Bf9beMBJkCZKf5Y',$,'DrawingsDir','Default drawings directory',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#19=IFCSIMPLEPROPERTYTEMPLATE('30cOKpb4D9CADKM04$N9X3',$,'StylesheetPath','Default stylesheet CSS',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#20=IFCSIMPLEPROPERTYTEMPLATE('23tejOrxj859R_eCRp1AFP',$,'MarkersPath','Default markers SVG',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#21=IFCSIMPLEPROPERTYTEMPLATE('1UDakJ5_f7kBhggNSW4$h5',$,'SymbolsPath','Default symbols SVG',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#22=IFCSIMPLEPROPERTYTEMPLATE('0d53LEtgLDQxnv__NfgH7i',$,'PatternsPath','Default patterns SVG',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#23=IFCPROPERTYSETTEMPLATE('0I9merLinF5Ap$aZwaclgm',$,'BBIM_Dimension','',.PSET_OCCURRENCEDRIVEN.,'IfcAnnotation,IfcTypeProduct',(#24,#25));
#24=IFCSIMPLEPROPERTYTEMPLATE('1rL2AbQsXD8RbpoWH5pYOV',$,'ShowDescriptionOnly','Hide the measurement values and show only annotation description',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
#25=IFCSIMPLEPROPERTYTEMPLATE('0SVyOfB0rC2xNfdRYf3XvY',$,'SuppressZeroInches','Suppress 0 inch values in dimension annotation text (for example: 12'' - 0" -> 12'')',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
ENDSEC;
END-ISO-10303-21;
@@ -1,24 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<svg baseProfile="full" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="rectangle-tag">
<rect x="-6" y="-2.5" width="12" height="5" fill="white" stroke="black" style="stroke-width: 0.25;" />
</g>
<g id="door-tag">
<circle cy="1.75" r="5" fill="white" stroke="black" style="stroke-width: 0.25;" />
<line x1="-5" y1="1.75" x2="5" y2="1.75" style="stroke: black; stroke-width: 0.25;" />
</g>
<g id="elevation-arrow">
<path style="fill: black;" d="M -7.07106 0 0 -7.07106 7.07106 0" />
</g>
<g id="elevation-tag">
<circle r="5" fill="white" stroke="black" style="stroke-width: 0.25;" />
<line x1="-5" y1="0" x2="5" y2="0" style="stroke: black; stroke-width: 0.25;" />
</g>
<g id="section-arrow">
<path style="fill: black;" d="M -7.07106 0 0 -7.07106 7.07106 0" />
</g>
<g id="section-tag">
<circle r="5" fill="white" stroke="black" style="stroke-width: 0.25;" />
<line x1="-5" y1="0" x2="5" y2="0" style="stroke: black; stroke-width: 0.25;" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

@@ -1,15 +1,15 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 841 594"
id="root"
version="1.1"
height="594mm"
width="841mm">
width="841mm"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<defs
id="defs901" />
<metadata
@@ -20,7 +20,6 @@
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
@@ -44,146 +43,10 @@
width="3027.1016"
id="rect116616"
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:1.88976377;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<text
id="text116526"
y="909.15491"
x="113.70713"
style="font-style:normal;font-weight:normal;font-size:15.1536px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.94488188;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15.1536px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.94488188;stroke-miterlimit:4;stroke-dasharray:none"
y="909.15491"
x="113.70713"
id="tspan116524">REV. NO.</tspan></text>
<path
id="path116530"
d="M 75.590564,895.24802 H 3102.9921"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.94488188;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path116532"
d="M 151.6811,895.24802 V 1046.9291"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.94488188;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.94488188;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 454.04331,895.24802 V 1046.9291"
id="path116534" />
<path
id="path116536"
d="M 529.63386,895.24802 V 1046.4302"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.94488188;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.94488188;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 605.22441,895.24802 V 1046.9291"
id="path116538" />
<path
id="path116540"
d="M 75.590564,914.14565 H 605.22441"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.94488188;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.94488188;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 75.590564,933.04329 H 605.22441"
id="path116542" />
<path
id="path116544"
d="M 75.590564,951.94093 H 605.22441"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.94488188;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.94488188;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 75.590564,970.83857 H 605.22441"
id="path116546" />
<path
id="path116548"
d="M 75.590564,989.73621 H 605.22441"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.94488188;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.94488188;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 75.590564,1008.6338 H 605.22441"
id="path116550" />
<path
id="path116552"
d="M 75.590564,1027.5315 H 605.22441"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.94488188;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path116576"
d="M 2800.5,895.24802 V 1046.9291"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.94488188;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path116578"
d="m 2800.5,1008.6338 h 302.4921"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.94488188;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path116598"
d="m 3027.4027,1008.6338 v 38.2953"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.94488188;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:15.1536px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.94488188;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
x="302.86591"
y="909.15491"
id="text1395"><tspan
id="tspan1393"
x="302.86591"
y="909.15491"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15.1536px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.94488188;stroke-miterlimit:4;stroke-dasharray:none">DESCRIPTION</tspan></text>
<text
id="text1399"
y="909.15491"
x="491.97513"
style="font-style:normal;font-weight:normal;font-size:15.1536px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.94488188;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15.1536px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.94488188;stroke-miterlimit:4;stroke-dasharray:none"
y="909.15491"
x="491.97513"
id="tspan1397">AUTHOR</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:16.0591px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.94488188;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
x="568.05585"
y="909.15485"
id="text1403"><tspan
id="tspan1401"
x="568.05585"
y="909.15485"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16.0591px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.94488188;stroke-miterlimit:4;stroke-dasharray:none">DATE</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:11.2271px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.94488188;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
x="2839.5081"
y="1020.4988"
id="text1421"><tspan
id="tspan1419"
x="2839.5081"
y="1020.4988"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.2271px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.94488188;stroke-miterlimit:4;stroke-dasharray:none">DRAWING NUMBER</tspan></text>
<text
id="text1429"
y="1020.4988"
x="3038.5586"
style="font-style:normal;font-weight:normal;font-size:11.2271px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.94488188;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.2271px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.94488188;stroke-miterlimit:4;stroke-dasharray:none"
y="1020.4988"
x="3038.5586"
id="tspan1427">REV</tspan></text>
<text
id="text2529"
y="1036.1166"
x="3030.9653"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13.3918px;line-height:125%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.999999px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
style="stroke-width:0.999999px"
y="1036.1166"
x="3030.9653"
id="tspan2527">{{Revision}}</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13.3918px;line-height:125%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.999999px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="2804.7021"
y="1036.1166"
id="text2533"><tspan
id="tspan2531"
x="2804.7021"
y="1036.1166"
style="stroke-width:0.999999px">{{Identification}} - {{Name}}</tspan></text>
</g>
<g
transform="matrix(0.26458333,0,0,0.26458334,-3.180917e-5,297.00003)"
@@ -205,4 +68,260 @@
transform="matrix(0.26458333,0,0,0.26458334,-3.180917e-5,297.00003)"
id="g90677"
style="stroke-width:3.77953" />
<g
id="g38171"
transform="translate(411.014,286.96036)">
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 329.96563,246.86771 V 286.75"
id="path116576" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 329.96563,276.8677 H 410"
id="path116578" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 390.00031,276.8677 V 287"
id="path116598" />
<text
id="text1421"
y="280.00699"
x="340.28653"
style="font-style:normal;font-weight:normal;font-size:2.9705px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.9705px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25;stroke-miterlimit:4;stroke-dasharray:none"
y="280.00699"
x="340.28653"
id="tspan1419">DRAWING NUMBER</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:3.05925px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25747;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
x="393.00568"
y="280.0592"
id="text1429"><tspan
id="tspan1427"
x="393.00568"
y="280.0592"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.05925px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25747;stroke-miterlimit:4;stroke-dasharray:none">REV</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.54325px;line-height:125%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
x="390.94287"
y="284.13919"
id="text2529"><tspan
id="tspan2527"
x="390.94287"
y="284.13919"
style="stroke-width:0.264583px">{{Revision}}</tspan></text>
<text
id="text2533"
y="284.13919"
x="331.07742"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.54325px;line-height:125%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.265;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
xml:space="preserve"><tspan
style="stroke-width:0.265;stroke-dasharray:none"
y="284.13919"
x="331.07742"
id="tspan2531">{{Identification}}</tspan></text>
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 329.96563,266.868 H 410"
id="path36736" />
<text
id="text36740"
y="270.05899"
x="339.00919"
style="font-style:normal;font-weight:normal;font-size:3.05925px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25747;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.05925px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25747;stroke-miterlimit:4;stroke-dasharray:none"
y="270.05899"
x="339.00919"
id="tspan36738">DRAWING TITLE</tspan></text>
<text
id="text36744"
y="275.05634"
x="330.99438"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.96055px;line-height:125%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.371;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
xml:space="preserve"><tspan
style="stroke-width:0.371;stroke-dasharray:none"
y="275.05634"
x="330.99438"
id="tspan36742">{{Name}}</tspan></text>
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 390.00031,246.86771 V 266.993"
id="path36746" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:2.88677px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.169851;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
x="397.04108"
y="249.72177"
id="text36677"><tspan
id="tspan36675"
x="397.04108"
y="249.72177"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.88677px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.169851;stroke-miterlimit:4;stroke-dasharray:none">GRID NORTH</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:2.88677px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.169851;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
x="338.77008"
y="249.72177"
id="text36752"><tspan
id="tspan36750"
x="335.76562"
y="249.72177"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.88677px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.169851;stroke-miterlimit:4;stroke-dasharray:none">COMPANY</tspan></text>
<g
id="g37148"
transform="translate(399.87519,258.25776)">
<g
id="g37140"
data-type="grid-north">
<path
id="path11652"
style="fill:none;stroke:#000000;stroke-width:0.25;stroke-linecap:round;stroke-dasharray:none"
d="M 0,0 V 7.12566 Z" />
<path
style="fill:none;stroke:#000000;stroke-width:0.25;stroke-linecap:round;stroke-dasharray:none"
d="M 7.3079,3e-5 H -7.30798"
id="path36547" />
<circle
style="fill:none;stroke:#000000;stroke-width:0.25;stroke-linecap:round;stroke-dasharray:none"
id="path36601"
cx="-3.8305665e-05"
cy="4.15625e-05"
r="5" />
<path
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.25;stroke-linecap:round;stroke-dasharray:none"
d="m -5.00004,4e-5 5,-5 5,5 H 3.94162 L -4e-5,-3.94162 -3.9417,4e-5 Z"
id="path36671" />
<path
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:round;stroke-dasharray:none"
d="M -4e-5,-7.12545 V 3e-5"
id="path36673" />
</g>
</g>
</g>
<g
id="g38492"
transform="translate(9.7896584,287.21036)">
<text
id="text116526"
y="250.54721"
x="20.085007"
style="font-style:normal;font-weight:normal;font-size:4.00939px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.00939px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25;stroke-miterlimit:4;stroke-dasharray:none"
y="250.54721"
x="20.085007"
id="tspan116524">REV. NO.</tspan></text>
<path
id="path116532"
d="M 30.132288,246.86771 V 287"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 110.13229,246.86771 V 287"
id="path116534" />
<path
id="path116536"
d="M 130.13229,246.86771 V 286.868"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 150.13229,246.86771 V 287"
id="path116538" />
<path
id="path116540"
d="M 10,251.86771 H 170.132"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 10,256.86771 H 170.132"
id="path116542" />
<path
id="path116544"
d="M 10,261.86771 H 170.132"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 10,266.86771 H 170.132"
id="path116546" />
<path
id="path116548"
d="M 10,271.86771 H 170.132"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 10,276.8677 H 170.132"
id="path116550" />
<path
id="path116552"
d="M 10,281.86772 H 170.132"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:4.00939px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
x="70.13327"
y="250.54721"
id="text1395"><tspan
id="tspan1393"
x="70.13327"
y="250.54721"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.00939px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25;stroke-miterlimit:4;stroke-dasharray:none">DESCRIPTION</tspan></text>
<text
id="text1399"
y="250.54721"
x="120.16841"
style="font-style:normal;font-weight:normal;font-size:4.00939px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.00939px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25;stroke-miterlimit:4;stroke-dasharray:none"
y="250.54721"
x="120.16841"
id="tspan1397">AUTHOR</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:4.24897px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
x="140.29811"
y="250.54721"
id="text1403"><tspan
id="tspan1401"
x="140.29811"
y="250.54721"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.24897px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25;stroke-miterlimit:4;stroke-dasharray:none">ISSUED</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:4.24897px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
x="180.14232"
y="250.54721"
id="text36130"><tspan
id="tspan36128"
x="175.72011"
y="250.54721"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.24897px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25;stroke-miterlimit:4;stroke-dasharray:none">NOTES</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:4.24897px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
x="194.37886"
y="255.5477"
id="text36768"><tspan
id="tspan36766"
x="189.95665"
y="255.5477"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.24897px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25;stroke-miterlimit:4;stroke-dasharray:none">DO NOT SCALE DRAWINGS</tspan></text>
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 170.132,246.86771 V 287"
id="path36836" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:4.24897px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
x="160.29811"
y="250.54721"
id="text36842"><tspan
id="tspan36840"
x="160.29811"
y="250.54721"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.24897px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25;stroke-miterlimit:4;stroke-dasharray:none">DATE</tspan></text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 20 KiB

@@ -1,15 +1,15 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
width="594mm"
height="420mm"
version="1.1"
id="root"
viewBox="0 0 594 420">
viewBox="0 0 594 420"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<defs
id="defs901" />
<metadata
@@ -20,7 +20,6 @@
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
@@ -40,146 +39,10 @@
width="553.5"
id="rect116616"
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.499999;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<text
id="text116526"
y="363.54721"
x="30.085009"
style="font-style:normal;font-weight:normal;font-size:4.00939px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.00939px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25;stroke-miterlimit:4;stroke-dasharray:none"
y="363.54721"
x="30.085009"
id="tspan116524">REV. NO.</tspan></text>
<path
id="path116530"
d="m 20.000001,359.86771 553.749999,0"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path116532"
d="M 40.132289,359.86771 V 400"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 120.13229,359.86771 V 400"
id="path116534" />
<path
id="path116536"
d="M 140.13229,359.86771 V 399.868"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 160.13229,359.86771 V 400"
id="path116538" />
<path
id="path116540"
d="M 20.000001,364.86771 H 160.13229"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25000001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 20.000001,369.86771 H 160.13229"
id="path116542" />
<path
id="path116544"
d="M 20.000001,374.86771 H 160.13229"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 20.000001,379.86771 H 160.13229"
id="path116546" />
<path
id="path116548"
d="M 20.000001,384.86771 H 160.13229"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 20.000001,389.8677 H 160.13229"
id="path116550" />
<path
id="path116552"
d="M 20.000001,394.86772 H 160.13229"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:4.00939px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
x="80.13327"
y="363.54721"
id="text1395"><tspan
id="tspan1393"
x="80.13327"
y="363.54721"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.00939px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25;stroke-miterlimit:4;stroke-dasharray:none">DESCRIPTION</tspan></text>
<text
id="text1399"
y="363.54721"
x="130.16841"
style="font-style:normal;font-weight:normal;font-size:4.00939px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.00939px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25;stroke-miterlimit:4;stroke-dasharray:none"
y="363.54721"
x="130.16841"
id="tspan1397">AUTHOR</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:4.24897px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
x="150.29811"
y="363.54721"
id="text1403"><tspan
id="tspan1401"
x="150.29811"
y="363.54721"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.24897px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25;stroke-miterlimit:4;stroke-dasharray:none">DATE</tspan></text>
<path
id="path116576"
d="M 493.96563,359.86771 V 400"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path116578"
d="M 493.96563,389.8677 H 574"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path116598"
d="M 554.00031,389.8677 V 400"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:2.9705px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
x="504.28653"
y="393.00699"
id="text1421"><tspan
id="tspan1419"
x="504.28653"
y="393.00699"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.9705px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25;stroke-miterlimit:4;stroke-dasharray:none">DRAWING NUMBER</tspan></text>
<text
id="text1429"
y="393.00699"
x="556.9519"
style="font-style:normal;font-weight:normal;font-size:2.9705px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.9705px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25;stroke-miterlimit:4;stroke-dasharray:none"
y="393.00699"
x="556.9519"
id="tspan1427">REV</tspan></text>
<text
id="text2529"
y="397.13919"
x="554.94287"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.54325px;line-height:125%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
xml:space="preserve"><tspan
style="stroke-width:0.264583px"
y="397.13919"
x="554.94287"
id="tspan2527">{{Revision}}</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.54325px;line-height:125%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
x="495.07742"
y="397.13919"
id="text2533"><tspan
id="tspan2531"
x="495.07742"
y="397.13919"
style="stroke-width:0.264583px">{{Identification}} - {{Name}}</tspan></text>
<g
style="stroke-width:3.77953"
id="g90669"
@@ -200,4 +63,260 @@
style="stroke-width:3.77953"
id="g90677"
transform="matrix(0.26458333,0,0,0.26458334,-3.180917e-5,297.00003)" />
<g
id="g38171"
transform="translate(163.75769,113)">
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 329.96563,246.86771 V 286.75"
id="path116576" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 329.96563,276.8677 H 410"
id="path116578" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 390.00031,276.8677 V 287"
id="path116598" />
<text
id="text1421"
y="280.00699"
x="340.28653"
style="font-style:normal;font-weight:normal;font-size:2.9705px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.9705px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25;stroke-miterlimit:4;stroke-dasharray:none"
y="280.00699"
x="340.28653"
id="tspan1419">DRAWING NUMBER</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:3.05925px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25747;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
x="393.00568"
y="280.0592"
id="text1429"><tspan
id="tspan1427"
x="393.00568"
y="280.0592"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.05925px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25747;stroke-miterlimit:4;stroke-dasharray:none">REV</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.54325px;line-height:125%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
x="390.94287"
y="284.13919"
id="text2529"><tspan
id="tspan2527"
x="390.94287"
y="284.13919"
style="stroke-width:0.264583px">{{Revision}}</tspan></text>
<text
id="text2533"
y="284.13919"
x="331.07742"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.54325px;line-height:125%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.265;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
xml:space="preserve"><tspan
style="stroke-width:0.265;stroke-dasharray:none"
y="284.13919"
x="331.07742"
id="tspan2531">{{Identification}}</tspan></text>
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 329.96563,266.868 H 410"
id="path36736" />
<text
id="text36740"
y="270.05899"
x="339.00919"
style="font-style:normal;font-weight:normal;font-size:3.05925px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25747;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.05925px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25747;stroke-miterlimit:4;stroke-dasharray:none"
y="270.05899"
x="339.00919"
id="tspan36738">DRAWING TITLE</tspan></text>
<text
id="text36744"
y="275.05634"
x="330.99438"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.96055px;line-height:125%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.371;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
xml:space="preserve"><tspan
style="stroke-width:0.371;stroke-dasharray:none"
y="275.05634"
x="330.99438"
id="tspan36742">{{Name}}</tspan></text>
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 390.00031,246.86771 V 266.993"
id="path36746" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:2.88677px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.169851;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
x="397.04108"
y="249.72177"
id="text36677"><tspan
id="tspan36675"
x="397.04108"
y="249.72177"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.88677px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.169851;stroke-miterlimit:4;stroke-dasharray:none">GRID NORTH</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:2.88677px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.169851;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
x="338.77008"
y="249.72177"
id="text36752"><tspan
id="tspan36750"
x="335.76562"
y="249.72177"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.88677px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.169851;stroke-miterlimit:4;stroke-dasharray:none">COMPANY</tspan></text>
<g
id="g37148"
transform="translate(399.87519,258.25776)">
<g
id="g37140"
data-type="grid-north">
<path
id="path11652"
style="fill:none;stroke:#000000;stroke-width:0.25;stroke-linecap:round;stroke-dasharray:none"
d="M 0,0 V 7.12566 Z" />
<path
style="fill:none;stroke:#000000;stroke-width:0.25;stroke-linecap:round;stroke-dasharray:none"
d="M 7.3079,3e-5 H -7.30798"
id="path36547" />
<circle
style="fill:none;stroke:#000000;stroke-width:0.25;stroke-linecap:round;stroke-dasharray:none"
id="path36601"
cx="-3.8305665e-05"
cy="4.15625e-05"
r="5" />
<path
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.25;stroke-linecap:round;stroke-dasharray:none"
d="m -5.00004,4e-5 5,-5 5,5 H 3.94162 L -4e-5,-3.94162 -3.9417,4e-5 Z"
id="path36671" />
<path
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:round;stroke-dasharray:none"
d="M -4e-5,-7.12545 V 3e-5"
id="path36673" />
</g>
</g>
</g>
<g
id="g38492"
transform="translate(10.000001,113)">
<text
id="text116526"
y="250.54721"
x="20.085007"
style="font-style:normal;font-weight:normal;font-size:4.00939px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.00939px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25;stroke-miterlimit:4;stroke-dasharray:none"
y="250.54721"
x="20.085007"
id="tspan116524">REV. NO.</tspan></text>
<path
id="path116532"
d="M 30.132288,246.86771 V 287"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 110.13229,246.86771 V 287"
id="path116534" />
<path
id="path116536"
d="M 130.13229,246.86771 V 286.868"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 150.13229,246.86771 V 287"
id="path116538" />
<path
id="path116540"
d="M 10,251.86771 H 170.132"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 10,256.86771 H 170.132"
id="path116542" />
<path
id="path116544"
d="M 10,261.86771 H 170.132"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 10,266.86771 H 170.132"
id="path116546" />
<path
id="path116548"
d="M 10,271.86771 H 170.132"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 10,276.8677 H 170.132"
id="path116550" />
<path
id="path116552"
d="M 10,281.86772 H 170.132"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:4.00939px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
x="70.13327"
y="250.54721"
id="text1395"><tspan
id="tspan1393"
x="70.13327"
y="250.54721"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.00939px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25;stroke-miterlimit:4;stroke-dasharray:none">DESCRIPTION</tspan></text>
<text
id="text1399"
y="250.54721"
x="120.16841"
style="font-style:normal;font-weight:normal;font-size:4.00939px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.00939px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25;stroke-miterlimit:4;stroke-dasharray:none"
y="250.54721"
x="120.16841"
id="tspan1397">AUTHOR</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:4.24897px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
x="140.29811"
y="250.54721"
id="text1403"><tspan
id="tspan1401"
x="140.29811"
y="250.54721"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.24897px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25;stroke-miterlimit:4;stroke-dasharray:none">ISSUED</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:4.24897px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
x="180.14232"
y="250.54721"
id="text36130"><tspan
id="tspan36128"
x="175.72011"
y="250.54721"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.24897px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25;stroke-miterlimit:4;stroke-dasharray:none">NOTES</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:4.24897px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
x="194.37886"
y="255.5477"
id="text36768"><tspan
id="tspan36766"
x="189.95665"
y="255.5477"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.24897px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25;stroke-miterlimit:4;stroke-dasharray:none">DO NOT SCALE DRAWINGS</tspan></text>
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 170.132,246.86771 V 287"
id="path36836" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:4.24897px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
x="160.29811"
y="250.54721"
id="text36842"><tspan
id="tspan36840"
x="160.29811"
y="250.54721"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.24897px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25;stroke-miterlimit:4;stroke-dasharray:none">DATE</tspan></text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 20 KiB

@@ -1,15 +1,15 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
width="420mm"
height="297mm"
version="1.1"
id="root"
viewBox="0 0 420 297">
viewBox="0 0 420 297"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<defs
id="defs901" />
<metadata
@@ -20,7 +20,6 @@
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
@@ -40,150 +39,10 @@
width="399.5"
id="rect116616"
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.49999897;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<text
id="text116526"
y="250.54721"
x="20.085007"
style="font-style:normal;font-weight:normal;font-size:4.00939px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.00939px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25;stroke-miterlimit:4;stroke-dasharray:none"
y="250.54721"
x="20.085007"
id="tspan116524">REV. NO.</tspan></text>
<path
id="path116530"
d="m 10,246.86771 399.75,0"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path116532"
d="M 30.132288,246.86771 V 287"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 110.13229,246.86771 V 287"
id="path116534" />
<path
id="path116536"
d="M 130.13229,246.86771 V 286.868"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 150.13229,246.86771 V 287"
id="path116538" />
<path
id="path116540"
d="M 10,251.86771 H 150.13229"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 10,256.86771 H 150.13229"
id="path116542" />
<path
id="path116544"
d="M 10,261.86771 H 150.13229"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 10,266.86771 H 150.13229"
id="path116546" />
<path
id="path116548"
d="M 10,271.86771 H 150.13229"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 10,276.8677 H 150.13229"
id="path116550" />
<path
id="path116552"
d="M 10,281.86772 H 150.13229"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:4.00939px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
x="70.13327"
y="250.54721"
id="text1395"><tspan
id="tspan1393"
x="70.13327"
y="250.54721"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.00939px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25;stroke-miterlimit:4;stroke-dasharray:none">DESCRIPTION</tspan></text>
<text
id="text1399"
y="250.54721"
x="120.16841"
style="font-style:normal;font-weight:normal;font-size:4.00939px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.00939px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25;stroke-miterlimit:4;stroke-dasharray:none"
y="250.54721"
x="120.16841"
id="tspan1397">AUTHOR</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:4.24897px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
x="140.29811"
y="250.54721"
id="text1403"><tspan
id="tspan1401"
x="140.29811"
y="250.54721"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.24897px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25;stroke-miterlimit:4;stroke-dasharray:none">DATE</tspan></text>
<g
transform="translate(-154)"
id="g3735">
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 483.96563,246.86771 V 287"
id="path116576" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 483.96563,276.8677 H 564"
id="path116578" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 544.00031,276.8677 V 287"
id="path116598" />
<text
id="text1421"
y="280.00699"
x="494.28653"
style="font-style:normal;font-weight:normal;font-size:2.9705px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.9705px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25;stroke-miterlimit:4;stroke-dasharray:none"
y="280.00699"
x="494.28653"
id="tspan1419">DRAWING NUMBER</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:2.9705px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
x="546.9519"
y="280.00699"
id="text1429"><tspan
id="tspan1427"
x="546.9519"
y="280.00699"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.9705px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25;stroke-miterlimit:4;stroke-dasharray:none">REV</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.54325px;line-height:125%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
x="544.94287"
y="284.13919"
id="text2529"><tspan
id="tspan2527"
x="544.94287"
y="284.13919"
style="stroke-width:0.264583px">{{Revision}}</tspan></text>
<text
id="text2533"
y="284.13919"
x="485.07742"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.54325px;line-height:125%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
xml:space="preserve"><tspan
style="stroke-width:0.264583px"
y="284.13919"
x="485.07742"
id="tspan2531">{{Identification}} - {{Name}}</tspan></text>
</g>
<g
style="stroke-width:3.77953"
id="g90669"
@@ -204,4 +63,271 @@
style="stroke-width:3.77953"
id="g90677"
transform="matrix(0.26458333,0,0,0.26458334,-3.180917e-5,297.00003)" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:4.24897px;line-height:100%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
x="162.3047"
y="256.66803"
id="text36756" />
<text
xml:space="preserve"
style="font-size:20.4013px;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT, Normal';text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.264999;stroke-linecap:round;stroke-dasharray:none"
x="161.2028"
y="266.93387"
id="text36760" />
<g
id="g38492">
<text
id="text116526"
y="250.54721"
x="20.085007"
style="font-style:normal;font-weight:normal;font-size:4.00939px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.00939px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25;stroke-miterlimit:4;stroke-dasharray:none"
y="250.54721"
x="20.085007"
id="tspan116524">REV. NO.</tspan></text>
<path
id="path116532"
d="M 30.132288,246.86771 V 287"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 110.13229,246.86771 V 287"
id="path116534" />
<path
id="path116536"
d="M 130.13229,246.86771 V 286.868"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 150.13229,246.86771 V 287"
id="path116538" />
<path
id="path116540"
d="m 10,251.86771 160.132,0"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 10,256.86771 160.132,0"
id="path116542" />
<path
id="path116544"
d="m 10,261.86771 160.132,0"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 10,266.86771 160.132,0"
id="path116546" />
<path
id="path116548"
d="m 10,271.86771 160.132,0"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 10,276.8677 160.132,0"
id="path116550" />
<path
id="path116552"
d="m 10,281.86772 160.132,0"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:4.00939px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
x="70.13327"
y="250.54721"
id="text1395"><tspan
id="tspan1393"
x="70.13327"
y="250.54721"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.00939px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25;stroke-miterlimit:4;stroke-dasharray:none">DESCRIPTION</tspan></text>
<text
id="text1399"
y="250.54721"
x="120.16841"
style="font-style:normal;font-weight:normal;font-size:4.00939px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.00939px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25;stroke-miterlimit:4;stroke-dasharray:none"
y="250.54721"
x="120.16841"
id="tspan1397">AUTHOR</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:4.24897px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
x="140.29811"
y="250.54721"
id="text1403"><tspan
id="tspan1401"
x="140.29811"
y="250.54721"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.24897px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25;stroke-miterlimit:4;stroke-dasharray:none">ISSUED</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:4.24897px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
x="180.14232"
y="250.54721"
id="text36130"><tspan
id="tspan36128"
x="175.72011"
y="250.54721"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.24897px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25;stroke-miterlimit:4;stroke-dasharray:none">NOTES</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:4.24897px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
x="194.37886"
y="255.5477"
id="text36768"><tspan
id="tspan36766"
x="189.95665"
y="255.5477"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.24897px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25;stroke-miterlimit:4;stroke-dasharray:none">DO NOT SCALE DRAWINGS</tspan></text>
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 170.132,246.86771 V 287"
id="path36836" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:4.24897px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
x="160.29811"
y="250.54721"
id="text36842"><tspan
id="tspan36840"
x="160.29811"
y="250.54721"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.24897px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25;stroke-miterlimit:4;stroke-dasharray:none">DATE</tspan></text>
</g>
<g
id="g38171"
transform="translate(-0.25000095)">
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 329.96563,246.86771 V 286.75"
id="path116576" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 329.96563,276.8677 H 410"
id="path116578" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 390.00031,276.8677 V 287"
id="path116598" />
<text
id="text1421"
y="280.00699"
x="340.28653"
style="font-style:normal;font-weight:normal;font-size:2.9705px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.9705px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25;stroke-miterlimit:4;stroke-dasharray:none"
y="280.00699"
x="340.28653"
id="tspan1419">DRAWING NUMBER</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:3.05925px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25747;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
x="393.00568"
y="280.0592"
id="text1429"><tspan
id="tspan1427"
x="393.00568"
y="280.0592"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.05925px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25747;stroke-miterlimit:4;stroke-dasharray:none">REV</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.54325px;line-height:125%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
x="390.94287"
y="284.13919"
id="text2529"><tspan
id="tspan2527"
x="390.94287"
y="284.13919"
style="stroke-width:0.264583px">{{Revision}}</tspan></text>
<text
id="text2533"
y="284.13919"
x="331.07742"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.54325px;line-height:125%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.265;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
xml:space="preserve"><tspan
style="stroke-width:0.265;stroke-dasharray:none"
y="284.13919"
x="331.07742"
id="tspan2531">{{Identification}}</tspan></text>
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 329.96563,266.868 H 410"
id="path36736" />
<text
id="text36740"
y="270.05899"
x="339.00919"
style="font-style:normal;font-weight:normal;font-size:3.05925px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25747;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.05925px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25747;stroke-miterlimit:4;stroke-dasharray:none"
y="270.05899"
x="339.00919"
id="tspan36738">DRAWING TITLE</tspan></text>
<text
id="text36744"
y="275.05634"
x="330.99438"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.96055px;line-height:125%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.371;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
xml:space="preserve"><tspan
style="stroke-width:0.371;stroke-dasharray:none"
y="275.05634"
x="330.99438"
id="tspan36742">{{Name}}</tspan></text>
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 390.00031,246.86771 V 266.993"
id="path36746" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:2.88677px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.169851;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
x="397.04108"
y="249.72177"
id="text36677"><tspan
id="tspan36675"
x="397.04108"
y="249.72177"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.88677px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.169851;stroke-miterlimit:4;stroke-dasharray:none">GRID NORTH</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:2.88677px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.169851;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
x="338.77008"
y="249.72177"
id="text36752"><tspan
id="tspan36750"
x="335.76562"
y="249.72177"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.88677px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.169851;stroke-miterlimit:4;stroke-dasharray:none">COMPANY</tspan></text>
<g
id="g37148"
transform="translate(399.87519,258.25776)">
<g
id="g37140"
data-type="grid-north">
<path
id="path11652"
style="fill:none;stroke:#000000;stroke-width:0.25;stroke-linecap:round;stroke-dasharray:none"
d="M 0,0 V 7.12566 Z" />
<path
style="fill:none;stroke:#000000;stroke-width:0.25;stroke-linecap:round;stroke-dasharray:none"
d="M 7.3079,3e-5 H -7.30798"
id="path36547" />
<circle
style="fill:none;stroke:#000000;stroke-width:0.25;stroke-linecap:round;stroke-dasharray:none"
id="path36601"
cx="-3.8305665e-05"
cy="4.15625e-05"
r="5" />
<path
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.25;stroke-linecap:round;stroke-dasharray:none"
d="m -5.00004,4e-5 5,-5 5,5 H 3.94162 L -4e-5,-3.94162 -3.9417,4e-5 Z"
id="path36671" />
<path
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:round;stroke-dasharray:none"
d="M -4e-5,-7.12545 V 3e-5"
id="path36673" />
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 20 KiB

+3 -16
View File
@@ -45,7 +45,6 @@ class IfcExporter:
self.set_header()
IfcStore.update_cache()
if bpy.context.scene.BIMProjectProperties.is_authoring:
self.sync_deletions()
self.sync_all_objects()
self.sync_edited_objects()
extension = self.ifc_export_settings.output_file.split(".")[-1].lower()
@@ -87,19 +86,6 @@ class IfcExporter:
self.get_application_name(), self.get_application_version()
)
def sync_deletions(self):
results = []
for ifc_definition_id in IfcStore.deleted_ids:
try:
product = self.file.by_id(ifc_definition_id)
if hasattr(product, "GlobalId"):
results.append(product.GlobalId)
except:
continue
ifcopenshell.api.run("root.remove_product", self.file, **{"product": product})
IfcStore.deleted_ids.clear()
return results
def sync_all_objects(self):
results = []
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(self.file)
@@ -126,7 +112,8 @@ class IfcExporter:
continue
try:
if isinstance(obj, bpy.types.Material):
blenderbim.core.style.update_style_colours(tool.Ifc, tool.Style, obj=obj)
if tool.Ifc.has_changed_shading(obj):
blenderbim.core.style.update_style_colours(tool.Ifc, tool.Style, obj=obj)
else:
element = tool.Ifc.get_entity(obj)
if element:
@@ -149,10 +136,10 @@ class IfcExporter:
return checksum != str([s.id() for s in tool.Geometry.get_styles(obj) if s])
def sync_object_placement(self, obj):
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
if not tool.Ifc.is_moved(obj):
return
blender_matrix = np.array(obj.matrix_world)
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
if (obj.scale - Vector((1.0, 1.0, 1.0))).length > 1e-4:
bpy.ops.bim.update_representation(obj=obj.name)
return element
+16 -16
View File
@@ -27,8 +27,6 @@ from bpy.app.handlers import persistent
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.module.owner.prop import get_user_person, get_user_organisation
from blenderbim.bim.module.model.data import AuthoringData
from ifcopenshell.api.material.data import Data as MaterialData
from ifcopenshell.api.type.data import Data as TypeData
global_subscription_owner = object()
@@ -49,9 +47,9 @@ def mode_callback(obj, data):
):
continue
if obj.data.BIMMeshProperties.ifc_definition_id:
IfcStore.edited_objs.add(obj)
tool.Ifc.edit(obj)
elif IfcStore.get_file().by_id(obj.BIMObjectProperties.ifc_definition_id).is_a("IfcGridAxis"):
IfcStore.edited_objs.add(obj)
tool.Ifc.edit(obj)
def name_callback(obj, data):
@@ -67,7 +65,6 @@ def name_callback(obj, data):
if isinstance(obj, bpy.types.Material):
if obj.BIMObjectProperties.ifc_definition_id:
IfcStore.get_file().by_id(obj.BIMObjectProperties.ifc_definition_id).Name = obj.name
MaterialData.load_materials()
if obj.BIMMaterialProperties.ifc_style_id:
IfcStore.get_file().by_id(obj.BIMMaterialProperties.ifc_style_id).Name = obj.name
refresh_ui_data()
@@ -91,31 +88,33 @@ def name_callback(obj, data):
break
if grid_collection:
grid_collection.name = obj.name
if element.is_a("IfcTypeProduct"):
TypeData.purge()
element.Name = "/".join(obj.name.split("/")[1:])
refresh_ui_data()
def color_callback(obj, data):
if obj.BIMMaterialProperties.ifc_style_id:
IfcStore.edited_objs.add(obj)
tool.Ifc.edit(obj)
def active_object_callback():
refresh_ui_data()
def subscribe_to(object, data_path, callback):
def active_material_index_callback(obj, data):
refresh_ui_data()
def subscribe_to(obj, data_path, callback):
try:
subscribe_to = object.path_resolve(data_path, False)
subscribe_to = obj.path_resolve(data_path, False)
except:
return
bpy.msgbus.subscribe_rna(
key=subscribe_to,
owner=object,
owner=obj,
args=(
object,
obj,
data_path,
),
notify=callback,
@@ -236,6 +235,11 @@ def setDefaultProperties(scene):
ifcopenshell.api.owner.settings.get_application = get_application
# TODO: Move to drawing module
if len(bpy.context.scene.DocProperties.drawing_styles) == 0:
drawing_style = bpy.context.scene.DocProperties.drawing_styles.add()
drawing_style.name = "Blender Default"
drawing_style.render_type = "DEFAULT"
bpy.ops.bim.save_drawing_style(index="0")
drawing_style = bpy.context.scene.DocProperties.drawing_styles.add()
drawing_style.name = "Technical"
drawing_style.render_type = "VIEWPORT"
@@ -298,8 +302,4 @@ def setDefaultProperties(scene):
RasterStyleProperty.OVERLAY_SHOW_RELATIONSHIP_LINES.value: False,
}
)
drawing_style = bpy.context.scene.DocProperties.drawing_styles.add()
drawing_style.name = "Blender Default"
drawing_style.render_type = "DEFAULT"
bpy.ops.bim.save_drawing_style(index="2")
AuthoringData.type_thumbnails = {}
+34 -4
View File
@@ -31,9 +31,15 @@ import blenderbim.tool as tool
from blenderbim.bim.ifc import IfcStore
def draw_attributes(props, layout, copy_operator=None):
def draw_attributes(props, layout, copy_operator=None, popup_active_attribute=None):
"""you can set attribute active in popup with `active_attribute`
meaning you will be able to type into attribute's field without having to click
on it first
"""
for attribute in props:
row = layout.row(align=True)
if attribute == popup_active_attribute:
row.activate_init = True
draw_attribute(attribute, row, copy_operator)
@@ -64,14 +70,14 @@ def draw_attribute(attribute, layout, copy_operator=None):
text=attribute.name,
)
if attribute.is_uri:
op = layout.operator("bim.select_uri_attribute", text="", icon="FILE_FOLDER")
op.data_path = attribute.path_from_id("string_value")
if attribute.is_optional:
layout.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
if copy_operator:
op = layout.operator(f"{copy_operator}", text="", icon="COPYDOWN")
op.name = attribute.name
if attribute.is_uri:
op = layout.operator("bim.select_uri_attribute", text="", icon="FILE_FOLDER")
op.data_path = attribute.path_from_id("string_value")
def import_attributes(ifc_class, props, data, callback=None):
@@ -243,6 +249,30 @@ def close_operator_panel(event):
bpy.app.timers.register(move_back, first_interval=0.01)
def convert_property_group_from_si(property_group, skip_props=()):
"""Method converts property group values from si to current ifc project units
based on default values of the properties.
List of properties to skip can be supplied in `skip_props`."""
conversion_k = 1.0 / ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
skip_props = ("rna_type", "name") + skip_props
for prop_name in property_group.bl_rna.properties.keys():
if prop_name in skip_props:
continue
prop_bl_rna = property_group.bl_rna.properties[prop_name]
if prop_bl_rna.array_length > 0:
prop_value = prop_bl_rna.default_array
else:
prop_value = prop_bl_rna.default
if type(prop_value) is float:
prop_value = prop_value * conversion_k
elif type(prop_value) is bpy.types.bpy_prop_array:
prop_value = [el * conversion_k for el in prop_value]
setattr(property_group, prop_name, prop_value)
class IfcHeaderExtractor:
def __init__(self, filepath: str):
self.filepath = filepath
+13 -21
View File
@@ -24,6 +24,7 @@ import zipfile
import tempfile
import ifcopenshell
import blenderbim.bim.handler
import blenderbim.tool as tool
from pathlib import Path
@@ -35,7 +36,6 @@ class IfcStore:
cache_path = None
id_map = {}
guid_map = {}
deleted_ids = set()
edited_objs = set()
pset_template_path = ""
pset_template_file = None
@@ -51,6 +51,7 @@ class IfcStore:
history = []
future = []
schema_identifiers = ["IFC4", "IFC2X3", "IFC4X3"]
session_files = {}
@staticmethod
def purge():
@@ -61,7 +62,6 @@ class IfcStore:
IfcStore.cache_path = None
IfcStore.id_map = {}
IfcStore.guid_map = {}
IfcStore.deleted_ids = set()
IfcStore.edited_objs = set()
IfcStore.pset_template_path = ""
IfcStore.pset_template_file = None
@@ -71,6 +71,7 @@ class IfcStore:
IfcStore.history = []
IfcStore.future = []
IfcStore.schema_identifiers = ["IFC4", "IFC2X3", "IFC4X3"]
IfcStore.session_files = {}
@staticmethod
def get_file():
@@ -96,7 +97,14 @@ class IfcStore:
try:
IfcStore.cache = ifcopenshell.geom.serializers.hdf5(IfcStore.cache_path, cache_settings)
except:
return
if os.path.exists(IfcStore.cache_path):
os.remove(IfcStore.cache_path)
try:
IfcStore.cache = ifcopenshell.geom.serializers.hdf5(IfcStore.cache_path, cache_settings)
except:
return
else:
return
return IfcStore.cache
@staticmethod
@@ -268,23 +276,6 @@ class IfcStore:
data = {"id": element.id(), "obj": obj.name}
IfcStore.commit_link_element(data)
@staticmethod
def delete_element(element):
IfcStore.deleted_ids.add(element.id())
if IfcStore.history:
data = {"id": element.id()}
IfcStore.history[-1]["operations"].append(
{"rollback": IfcStore.rollback_delete_element, "commit": IfcStore.commit_delete_element, "data": data}
)
@staticmethod
def rollback_delete_element(data):
IfcStore.deleted_ids.remove(data["id"])
@staticmethod
def commit_delete_element(data):
IfcStore.deleted_ids.add(data["id"])
@staticmethod
def link_element(element, obj):
existing_obj = IfcStore.id_map.get(element.id(), None)
@@ -311,6 +302,7 @@ class IfcStore:
blenderbim.bim.handler.subscribe_to(obj, "diffuse_color", blenderbim.bim.handler.color_callback)
elif isinstance(obj, bpy.types.Object):
blenderbim.bim.handler.subscribe_to(obj, "mode", blenderbim.bim.handler.mode_callback)
blenderbim.bim.handler.subscribe_to(obj, "active_material_index", blenderbim.bim.handler.active_material_index_callback)
for listener in IfcStore.element_listeners:
listener(element, obj)
@@ -361,7 +353,7 @@ class IfcStore:
def unlink_element(element=None, obj=None):
if element is None:
try:
element = IfcStore.get_file().by_id(obj.BIMObjectProperties.ifc_definition_id)
element = tool.Ifc.get_entity(obj)
except:
pass
+92 -181
View File
@@ -60,7 +60,6 @@ class MaterialCreator:
def create(self, element, obj, mesh):
self.mesh = mesh
self.obj = obj
if (hasattr(element, "Representation") and not element.Representation) or (
hasattr(element, "RepresentationMaps") and not element.RepresentationMaps
):
@@ -140,11 +139,11 @@ class MaterialCreator:
def assign_material_slots_to_faces(self):
if "ios_materials" not in self.mesh or not self.mesh["ios_materials"]:
return
if len(self.obj.material_slots) == 1:
if len(self.mesh.materials) == 1:
return
material_to_slot = {}
for i, material in enumerate(self.mesh["ios_materials"]):
slot_index = self.obj.material_slots.find(self.styles[material].name)
slot_index = self.mesh.materials.find(self.styles[material].name)
material_to_slot[i] = slot_index
if len(self.mesh.polygons) == len(self.mesh["ios_material_ids"]):
@@ -179,6 +178,11 @@ class IfcImporter:
self.settings.set_deflection_tolerance(self.ifc_import_settings.deflection_tolerance)
self.settings.set_angular_tolerance(self.ifc_import_settings.angular_tolerance)
self.settings.set(self.settings.STRICT_TOLERANCE, True)
self.settings_curve = ifcopenshell.geom.settings()
self.settings_curve.set_deflection_tolerance(self.ifc_import_settings.deflection_tolerance)
self.settings_curve.set_angular_tolerance(self.ifc_import_settings.angular_tolerance)
self.settings_curve.set(self.settings_curve.STRICT_TOLERANCE, True)
self.settings_curve.set(self.settings_curve.INCLUDE_CURVES, True)
self.settings_native = ifcopenshell.geom.settings()
self.settings_native.set(self.settings_native.INCLUDE_CURVES, True)
self.settings_2d = ifcopenshell.geom.settings()
@@ -236,14 +240,14 @@ class IfcImporter:
self.profile_code("Create materials")
self.create_styles()
self.profile_code("Create styles")
self.create_annotations()
self.profile_code("Create annotation")
self.parse_native_elements()
self.profile_code("Parsing native elements")
self.create_native_elements()
self.profile_code("Create native elements")
self.create_elements()
self.profile_code("Create elements")
self.create_generic_elements(self.annotations)
self.profile_code("Create annotations")
self.create_grids()
self.profile_code("Create grids")
self.create_spatial_elements()
@@ -304,12 +308,12 @@ class IfcImporter:
)
if self.body_contexts:
self.settings.set_context_ids(self.body_contexts)
# Annotation is to accommodate broken Revit files
# Annotation ContextType is to accommodate broken Revit files
# See https://github.com/Autodesk/revit-ifc/issues/187
self.plan_contexts = [
c.id()
for c in self.file.by_type("IfcGeometricRepresentationContext")
if c.ContextType in ["Plan", "Annotation"]
if c.ContextType in ["Plan", "Annotation"] or c.ContextIdentifier == "Annotation"
]
if self.plan_contexts:
self.settings_2d.set_context_ids(self.plan_contexts)
@@ -323,10 +327,13 @@ class IfcImporter:
if isinstance(self.elements, set):
self.elements = list(self.elements)
# TODO: enable filtering for annotations
self.annotations = set(self.file.by_type("IfcAnnotation"))
self.annotations = set([a for a in self.file.by_type("IfcAnnotation") if not a.HasAssignments])
else:
self.elements = self.file.by_type("IfcElement")
self.annotations = set(self.file.by_type("IfcAnnotation"))
if self.file.schema in ("IFC2X3", "IFC4"):
self.elements = self.file.by_type("IfcElement") + self.file.by_type("IfcProxy")
else:
self.elements = self.file.by_type("IfcElement")
self.annotations = set([a for a in self.file.by_type("IfcAnnotation") if not a.HasAssignments])
self.elements = [e for e in self.elements if not e.is_a("IfcFeatureElement")]
if self.ifc_import_settings.is_coordinating:
@@ -337,14 +344,7 @@ class IfcImporter:
if self.ifc_import_settings.has_filter or offset or offset_limit < len(self.elements):
self.element_types = set([ifcopenshell.util.element.get_type(e) for e in self.elements])
else:
if self.file.schema in ("IFC2X3", "IFC4"):
self.element_types = set(
self.file.by_type("IfcElementType")
+ self.file.by_type("IfcDoorStyle")
+ self.file.by_type("IfcWindowStyle")
)
else:
self.element_types = set(self.file.by_type("IfcElementType"))
self.element_types = set(self.file.by_type("IfcTypeProduct"))
if self.ifc_import_settings.has_filter and self.ifc_import_settings.should_filter_spatial_elements:
self.spatial_elements = self.get_spatial_elements_filtered_by_elements(self.elements)
@@ -586,7 +586,7 @@ class IfcImporter:
if bpy.context.preferences.addons["blenderbim"].preferences.lock_grids_on_import:
grid_obj.lock_location = (True, True, True)
grid_obj.lock_rotation = (True, True, True)
collection = bpy.data.collections.new(self.get_name(grid))
collection = bpy.data.collections.new(tool.Loader.get_name(grid))
u_axes = bpy.data.collections.new("UAxes")
collection.children.link(u_axes)
v_axes = bpy.data.collections.new("VAxes")
@@ -618,37 +618,33 @@ class IfcImporter:
def create_element_type(self, element):
self.ifc_import_settings.logger.info("Creating object %s", element)
representation_map = self.get_type_product_body_representation_map(element)
representation = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
if not representation:
representation = ifcopenshell.util.representation.get_representation(element, "Plan", "Annotation")
if not representation:
representation = ifcopenshell.util.representation.get_representation(element, "Model", "Annotation")
mesh = None
if representation_map:
representation = representation_map.MappedRepresentation
if representation:
mesh_name = "{}/{}".format(representation.ContextOfItems.id(), representation.id())
mesh = self.meshes.get(mesh_name)
if mesh is None:
shape = None
try:
shape = ifcopenshell.geom.create_shape(self.settings, representation_map.MappedRepresentation)
mesh = self.create_mesh(element, shape)
self.link_mesh(shape, mesh)
self.meshes[mesh_name] = mesh
shape = ifcopenshell.geom.create_shape(self.settings, representation)
except:
self.ifc_import_settings.logger.error("Failed to generate shape for %s", element)
obj = bpy.data.objects.new(self.get_name(element), mesh)
try:
shape = ifcopenshell.geom.create_shape(self.settings_2d, representation)
except:
self.ifc_import_settings.logger.error("Failed to generate shape for %s", element)
if shape:
mesh = self.create_mesh(element, shape)
tool.Loader.link_mesh(shape, mesh)
self.meshes[mesh_name] = mesh
obj = bpy.data.objects.new(tool.Loader.get_name(element), mesh)
self.link_element(element, obj)
self.material_creator.create(element, obj, mesh)
self.type_products[element.GlobalId] = obj
def get_type_product_body_representation_map(self, element):
if not element.RepresentationMaps:
return
for representation_map in element.RepresentationMaps:
context = representation_map.MappedRepresentation.ContextOfItems
if (
context.ContextType == "Model"
and context.ContextIdentifier == "Body"
and context.TargetView == "MODEL_VIEW"
):
return representation_map
def create_native_elements(self):
progress = 0
checkpoint = time.time()
@@ -687,36 +683,36 @@ class IfcImporter:
def create_spatial_elements(self):
self.create_generic_elements(self.spatial_elements)
def create_annotations(self):
self.create_generic_elements(self.annotations)
def create_elements(self):
self.create_generic_elements(self.elements)
def create_generic_elements(self, elements):
# Based on my experience in viewing BIM models, representations are prioritised as follows:
# 1. 3D Body, 2. 2D Plans, 3. Point clouds, 4. No representation
# If an element has a representation that doesn't follow 1, 2, or 3, it will not show by default.
# 1. 3D Body, 2. 2D Body, 3. 2D Plans / annotations, 4. Point clouds, 5. No representation
# If an element has a representation that doesn't follow 1, 2, 3, or 4, it will not show by default.
# The user can load them later if they want to view them.
products = self.create_products(elements)
elements -= products
products = self.create_curve_products(elements)
products = self.create_products(elements, settings=self.settings_curve)
elements -= products
products = self.create_products(elements, settings=self.settings_2d)
elements -= products
products = self.create_pointclouds(elements)
elements -= products
for element in elements:
self.create_product(element)
def create_products(self, products):
def create_products(self, products, settings=None):
if settings is None:
settings = self.settings
results = set()
if not products:
return results
if self.ifc_import_settings.should_use_cpu_multiprocessing:
iterator = ifcopenshell.geom.iterator(
self.settings, self.file, multiprocessing.cpu_count(), include=products
)
iterator = ifcopenshell.geom.iterator(settings, self.file, multiprocessing.cpu_count(), include=products)
else:
iterator = ifcopenshell.geom.iterator(self.settings, self.file, include=products)
iterator = ifcopenshell.geom.iterator(settings, self.file, include=products)
if self.ifc_import_settings.should_cache:
cache = IfcStore.get_cache()
if cache:
@@ -783,10 +779,10 @@ class IfcImporter:
self.structural_collection.children.link(self.structural_connection_collection)
self.project["blender"].children.link(self.structural_collection)
self.create_curve_products(self.file.by_type("IfcStructuralCurveMember"))
self.create_curve_products(self.file.by_type("IfcStructuralCurveConnection"))
self.create_curve_products(self.file.by_type("IfcStructuralSurfaceMember"))
self.create_curve_products(self.file.by_type("IfcStructuralSurfaceConnection"))
self.create_products(self.file.by_type("IfcStructuralCurveMember"), settings=self.settings_2d)
self.create_products(self.file.by_type("IfcStructuralCurveConnection"), settings=self.settings_2d)
self.create_products(self.file.by_type("IfcStructuralSurfaceMember"), settings=self.settings_2d)
self.create_products(self.file.by_type("IfcStructuralSurfaceConnection"), settings=self.settings_2d)
self.create_structural_point_connections()
def create_structural_point_connections(self):
@@ -874,40 +870,6 @@ class IfcImporter:
self.link_element(product, obj)
return product
def create_curve_products(self, products):
results = set()
if not products:
return results
if self.ifc_import_settings.should_use_cpu_multiprocessing:
iterator = ifcopenshell.geom.iterator(
self.settings_2d, self.file, multiprocessing.cpu_count(), include=products
)
else:
iterator = ifcopenshell.geom.iterator(self.settings_2d, self.file, include=products)
if self.ifc_import_settings.should_cache:
cache = IfcStore.get_cache()
if cache:
iterator.set_cache(cache)
valid_file = iterator.initialize()
if not valid_file:
return results
checkpoint = time.time()
total = 0
while True:
total += 1
if total % 250 == 0:
print("{} elements processed in {:.2f}s ...".format(total, time.time() - checkpoint))
checkpoint = time.time()
shape = iterator.get()
if shape:
product = self.file.by_id(shape.id)
self.create_product(product, shape)
results.add(product)
if not iterator.next():
break
print("Done creating geometry")
return results
def create_product(self, element, shape=None, mesh=None):
if element is None:
return
@@ -921,23 +883,20 @@ class IfcImporter:
if mesh:
pass
elif element.is_a("IfcAnnotation") and element.ObjectType == "DRAWING":
mesh = self.create_camera(element, shape)
self.link_mesh(shape, mesh)
elif element.is_a("IfcAnnotation") and self.is_curve_annotation(element) and shape:
mesh = self.create_curve(element, shape)
self.link_mesh(shape, mesh)
tool.Loader.link_mesh(shape, mesh)
elif shape:
mesh_name = self.get_mesh_name(shape.geometry)
mesh_name = tool.Loader.get_mesh_name(shape.geometry)
mesh = self.meshes.get(mesh_name)
if mesh is None:
mesh = self.create_mesh(element, shape)
self.link_mesh(shape, mesh)
tool.Loader.link_mesh(shape, mesh)
self.meshes[mesh_name] = mesh
else:
mesh = None
obj = bpy.data.objects.new(self.get_name(element), mesh)
obj = bpy.data.objects.new(tool.Loader.get_name(element), mesh)
self.link_element(element, obj)
if shape:
@@ -990,9 +949,37 @@ class IfcImporter:
self.convert_representation(representation)
mesh = bpy.data.meshes.new("Native")
props = bpy.context.scene.BIMGeoreferenceProperties
mat = ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement)
if props.has_blender_offset and self.is_point_far_away(self.mesh_data["co"][0:3], is_meters=False):
offset_point = np.linalg.inv(mat) @ np.array(
(
float(props.blender_eastings),
float(props.blender_northings),
float(props.blender_orthogonal_height),
0.0,
)
)
verts = [None] * len(self.mesh_data["co"])
for i in range(0, len(self.mesh_data["co"]), 3):
verts[i], verts[i + 1], verts[i + 2] = ifcopenshell.util.geolocation.enh2xyz(
self.mesh_data["co"][i] * self.unit_scale,
self.mesh_data["co"][i + 1] * self.unit_scale,
self.mesh_data["co"][i + 2] * self.unit_scale,
offset_point[0] * self.unit_scale,
offset_point[1] * self.unit_scale,
offset_point[2] * self.unit_scale,
float(props.blender_x_axis_abscissa),
float(props.blender_x_axis_ordinate),
)
mesh["has_cartesian_point_offset"] = True
else:
verts = [c * self.unit_scale for c in self.mesh_data["co"]]
mesh["has_cartesian_point_offset"] = False
mesh.vertices.add(self.mesh_data["total_verts"])
mesh.vertices.foreach_set("co", [c * self.unit_scale for c in self.mesh_data["co"]])
# mesh.vertices.foreach_set("co", self.mesh_data["co"])
mesh.vertices.foreach_set("co", verts)
mesh.loops.add(len(self.mesh_data["vertex_index"]))
mesh.loops.foreach_set("vertex_index", self.mesh_data["vertex_index"])
mesh.polygons.add(self.mesh_data["total_polygons"])
@@ -1385,12 +1372,6 @@ class IfcImporter:
if not view_collection:
view_collection = bpy.data.collections.new("Views")
self.project["blender"].children.link(view_collection)
for element in self.file.by_type("IfcAnnotation"):
if element.ObjectType == "DRAWING":
group = [r for r in element.HasAssignments if r.is_a("IfcRelAssignsToGroup")][0].RelatingGroup
collection = bpy.data.collections.new("IfcGroup/" + group.Name)
self.collections[group.GlobalId] = collection
view_collection.children.link(collection)
def create_spatial_decomposition_collection(self, parent, related_objects):
for element in related_objects:
@@ -1404,7 +1385,7 @@ class IfcImporter:
collection = obj.users_collection[0]
self.collections[element.GlobalId] = collection
if not is_existing:
collection = bpy.data.collections.new(self.get_name(element))
collection = bpy.data.collections.new(tool.Loader.get_name(element))
self.collections[element.GlobalId] = collection
parent.children.link(collection)
if element.IsDecomposedBy:
@@ -1432,7 +1413,7 @@ class IfcImporter:
aggregates = {}
for rel_aggregate in rel_aggregates:
element = rel_aggregate.RelatingObject
collection = bpy.data.collections.new(self.get_name(element))
collection = bpy.data.collections.new(tool.Loader.get_name(element))
aggregates[element.GlobalId] = {"element": element, "collection": collection}
self.collections[element.GlobalId] = collection
@@ -1508,6 +1489,8 @@ class IfcImporter:
if rendering_style and texture_style:
self.create_surface_style_with_textures(blender_material, rendering_style, texture_style)
tool.Style.record_shading(blender_material)
def create_surface_style_shading(self, blender_material, surface_style):
alpha = 1.0
# Transparency was added in IFC4
@@ -1663,9 +1646,6 @@ class IfcImporter:
else:
blender_material.node_tree.links.new(coord.outputs["UV"], node.inputs["Vector"])
def get_name(self, element):
return "{}/{}".format(element.is_a(), element.Name)
def place_objects_in_collections(self):
for ifc_definition_id, obj in self.added_data.items():
if isinstance(obj, bpy.types.Object):
@@ -1736,7 +1716,7 @@ class IfcImporter:
def get_drawing_group(self, element):
for rel in element.HasAssignments or []:
if rel.is_a("IfcRelAssignsToGroup"):
if rel.is_a("IfcRelAssignsToGroup") and rel.RelatingGroup.ObjectType == "DRAWING":
return rel.RelatingGroup
def get_element_matrix(self, element):
@@ -1813,71 +1793,13 @@ class IfcImporter:
):
return representation.Items[0].MappingTarget
def get_mesh_name(self, geometry):
representation_id = geometry.id
if "-" in representation_id:
representation_id = int(re.sub(r"\D", "", representation_id.split("-")[0]))
else:
representation_id = int(re.sub(r"\D", "", representation_id))
representation = self.file.by_id(representation_id)
context_id = representation.ContextOfItems.id() if hasattr(representation, "ContextOfItems") else 0
return "{}/{}".format(context_id, representation_id)
def create_camera(self, element, shape):
if hasattr(shape, "geometry"):
geometry = shape.geometry
else:
geometry = shape
v = geometry.verts
x = [v[i] for i in range(0, len(v), 3)]
y = [v[i + 1] for i in range(0, len(v), 3)]
z = [v[i + 2] for i in range(0, len(v), 3)]
width = max(x) - min(x)
height = max(y) - min(y)
depth = max(z) - min(z)
camera = bpy.data.cameras.new(self.get_mesh_name(geometry))
camera.type = "ORTHO"
camera.ortho_scale = width if width > height else height
camera.clip_end = depth
if width > height:
camera.BIMCameraProperties.raster_x = 1000
camera.BIMCameraProperties.raster_y = round(1000 * (height / width))
else:
camera.BIMCameraProperties.raster_x = round(1000 * (width / height))
camera.BIMCameraProperties.raster_y = 1000
psets = ifcopenshell.util.element.get_psets(element)
pset = psets.get("EPset_Drawing")
if pset:
if "TargetView" in pset:
camera.BIMCameraProperties.target_view = pset["TargetView"]
if "Scale" in pset:
valid_scales = [
i[0] for i in get_diagram_scales(None, bpy.context) if pset["Scale"] == i[0].split("|")[-1]
]
if valid_scales:
camera.BIMCameraProperties.diagram_scale = valid_scales[0]
else:
camera.BIMCameraProperties.diagram_scale = "CUSTOM"
camera.BIMCameraProperties.custom_diagram_scale = pset["Scale"]
if "HasUnderlay" in pset:
camera.BIMCameraProperties.has_underlay = pset["HasUnderlay"]
if "HasLinework" in pset:
camera.BIMCameraProperties.has_linework = pset["HasLinework"]
if "HasAnnotation" in pset:
camera.BIMCameraProperties.has_annotation = pset["HasAnnotation"]
return camera
def create_curve(self, element, shape):
if hasattr(shape, "geometry"):
geometry = shape.geometry
else:
geometry = shape
curve = bpy.data.curves.new(self.get_mesh_name(geometry), type="CURVE")
curve = bpy.data.curves.new(tool.Loader.get_mesh_name(geometry), type="CURVE")
curve.dimensions = "3D"
curve.resolution_u = 2
@@ -1903,7 +1825,7 @@ class IfcImporter:
else:
geometry = shape
mesh = bpy.data.meshes.new(self.get_mesh_name(geometry))
mesh = bpy.data.meshes.new(tool.Loader.get_mesh_name(geometry))
props = bpy.context.scene.BIMGeoreferenceProperties
if (
@@ -2018,17 +1940,6 @@ class IfcImporter:
self.added_data[element.id()] = obj
IfcStore.link_element(element, obj)
def link_mesh(self, shape, mesh):
if hasattr(shape, "geometry"):
geometry = shape.geometry
else:
geometry = shape
if "-" in geometry.id:
mesh.BIMMeshProperties.ifc_definition_id = int(geometry.id.split("-")[0])
else:
# TODO: See #2002
mesh.BIMMeshProperties.ifc_definition_id = int(geometry.id.replace(",", ""))
def set_matrix_world(self, obj, matrix_world):
obj.matrix_world = matrix_world
tool.Geometry.record_object_position(obj)
@@ -34,7 +34,6 @@ class AggregateData:
cls.data = {
"has_relating_object": cls.has_relating_object(),
"relating_object_label": cls.get_relating_object_label(),
"relating_object_id": cls.get_relating_object_id(),
"has_related_objects": cls.has_related_objects(),
"related_objects_amount": cls.get_related_objects_amount(),
"ifc_class": cls.ifc_class(),
@@ -55,12 +54,6 @@ class AggregateData:
if aggregate:
return f"{aggregate.is_a()}/{aggregate.Name or ''}"
@classmethod
def get_relating_object_id(cls) -> int:
aggregate = cls.get_relating_object()
if aggregate:
return aggregate.id()
@classmethod
def get_related_objects(cls):
return ifcopenshell.util.element.get_parts(tool.Ifc.get_entity(bpy.context.active_object))
@@ -58,16 +58,22 @@ class BIM_OT_unassign_object(bpy.types.Operator, Operator):
bl_idname = "bim.unassign_object"
bl_label = "Unassign Object"
bl_options = {"REGISTER", "UNDO"}
relating_object: bpy.props.IntProperty()
related_object: bpy.props.IntProperty()
def _execute(self, context):
core.unassign_object(
tool.Ifc,
tool.Collector,
relating_obj=tool.Ifc.get_object(tool.Ifc.get().by_id(self.relating_object)),
related_obj=tool.Ifc.get_object(tool.Ifc.get().by_id(self.related_object)),
)
for obj in bpy.context.selected_objects:
element = tool.Ifc.get_entity(obj)
if not element:
continue
aggregate = ifcopenshell.util.element.get_aggregate(element)
if not aggregate:
continue
core.unassign_object(
tool.Ifc,
tool.Aggregate,
tool.Collector,
relating_obj=tool.Ifc.get_object(aggregate),
related_obj=tool.Ifc.get_object(element),
)
class BIM_OT_enable_editing_aggregate(bpy.types.Operator, Operator):
@@ -92,52 +98,61 @@ class BIM_OT_disable_editing_aggregate(bpy.types.Operator, Operator):
core.disable_editing_aggregate(tool.Aggregate, obj=context.active_object)
class BIM_OT_add_aggregate(bpy.types.Operator):
class BIM_OT_add_aggregate(bpy.types.Operator, tool.Ifc.Operator):
"""Add aggregate to IFC element"""
bl_idname = "bim.add_aggregate"
bl_label = "Add Aggregate"
bl_options = {"REGISTER", "UNDO"}
obj: bpy.props.StringProperty()
ifc_class: bpy.props.StringProperty(name="IFC Class", default="IfcElementAssembly")
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def invoke(self, context, event):
return context.window_manager.invoke_props_dialog(self)
def draw(self, context):
row = self.layout
row.prop(self, "ifc_class")
def _execute(self, context):
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
element = tool.Ifc.get_entity(obj)
if not element:
return {"FINISHED"}
try:
ifc_class = tool.Ifc.schema().declaration_by_name(self.ifc_class).name()
except:
return
aggregate = self.create_aggregate(context, ifc_class)
aggregate = self.create_aggregate(context)
tool.Collector.sync(obj)
current_aggregate = ifcopenshell.util.element.get_aggregate(element)
current_container = ifcopenshell.util.element.get_container(element)
if current_aggregate:
core.assign_object(
tool.Ifc,
tool.Aggregate,
tool.Collector,
relating_obj=tool.Ifc.get_object(current_aggregate),
related_obj=aggregate,
)
elif current_container:
blenderbim.core.spatial.assign_container(
tool.Ifc,
tool.Collector,
tool.Spatial,
structure_obj=tool.Ifc.get_object(current_container),
element_obj=aggregate,
)
core.assign_object(tool.Ifc, tool.Aggregate, tool.Collector, relating_obj=aggregate, related_obj=obj)
return {"FINISHED"}
for obj in context.selected_objects:
element = tool.Ifc.get_entity(obj)
if not element:
continue
def create_aggregate(self, context):
aggregate_collection = bpy.data.collections.new("IfcElementAssembly/Assembly")
tool.Collector.sync(obj)
current_aggregate = ifcopenshell.util.element.get_aggregate(element)
current_container = ifcopenshell.util.element.get_container(element)
if current_aggregate:
core.assign_object(
tool.Ifc,
tool.Aggregate,
tool.Collector,
relating_obj=tool.Ifc.get_object(current_aggregate),
related_obj=aggregate,
)
elif current_container:
blenderbim.core.spatial.assign_container(
tool.Ifc,
tool.Collector,
tool.Spatial,
structure_obj=tool.Ifc.get_object(current_container),
element_obj=aggregate,
)
core.assign_object(tool.Ifc, tool.Aggregate, tool.Collector, relating_obj=aggregate, related_obj=obj)
def create_aggregate(self, context, ifc_class):
aggregate_collection = bpy.data.collections.new(f"{ifc_class}/Assembly")
context.scene.collection.children.link(aggregate_collection)
aggregate = bpy.data.objects.new("Assembly", None)
aggregate_collection.objects.link(aggregate)
bpy.ops.bim.assign_class(obj=aggregate.name, ifc_class="IfcElementAssembly")
bpy.ops.bim.assign_class(obj=aggregate.name, ifc_class=ifc_class)
return aggregate
@@ -67,8 +67,6 @@ class BIM_PT_aggregate(Panel):
row.operator("bim.enable_editing_aggregate", icon="GREASEPENCIL", text="")
row.operator("bim.add_aggregate", icon="ADD", text="")
op = row.operator("bim.unassign_object", icon="X", text="")
op.relating_object = AggregateData.data["relating_object_id"]
op.related_object = context.active_object.BIMObjectProperties.ifc_definition_id
else:
row.label(text="No Aggregate", icon="TRIA_UP")
row.operator("bim.enable_editing_aggregate", icon="GREASEPENCIL", text="")
@@ -59,9 +59,13 @@ class MaterialAttributesData:
@classmethod
def load(cls):
cls.data = {"attributes": cls.attributes()}
cls.data = {"ifc_definition_id": cls.ifc_definition_id(), "attributes": cls.attributes()}
cls.is_loaded = True
@classmethod
def ifc_definition_id(cls):
return bpy.context.active_object.active_material.BIMObjectProperties.ifc_definition_id
@classmethod
def attributes(cls):
results = []
@@ -25,7 +25,6 @@ import blenderbim.bim.handler
import blenderbim.tool as tool
import blenderbim.core.attribute as core
from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.attribute.data import Data
class Operator:
@@ -51,9 +50,20 @@ class EnableEditingAttributes(bpy.types.Operator):
oprops = obj.BIMObjectProperties
props = obj.BIMAttributeProperties
props.attributes.clear()
if oprops.ifc_definition_id not in Data.products:
Data.load(IfcStore.get_file(), oprops.ifc_definition_id)
blenderbim.bim.helper.import_attributes2(tool.Ifc.get().by_id(oprops.ifc_definition_id), props.attributes)
def callback(name, prop, data):
if name in ("RefLatitude", "RefLongitude"):
new = props.attributes.add()
new.name = name
new.is_null = data[name] is None
new.is_optional = True
new.data_type = "string"
new.ifc_class = data["type"]
new.string_value = "" if new.is_null else json.dumps(data[name])
blenderbim.bim.helper.import_attributes2(
tool.Ifc.get().by_id(oprops.ifc_definition_id), props.attributes, callback=callback
)
props.is_editing_attributes = True
return {"FINISHED"}
@@ -88,33 +98,22 @@ class EditAttributes(bpy.types.Operator, Operator):
obj = bpy.data.objects.get(self.obj)
elif self.obj_type == "Material":
obj = bpy.data.materials.get(self.obj)
oprops = obj.BIMObjectProperties
props = obj.BIMAttributeProperties
attributes = {}
for attribute in Data.products[oprops.ifc_definition_id]:
blender_attribute = props.attributes.get(attribute["name"])
if not blender_attribute:
continue
if attribute["is_optional"] and blender_attribute.is_null:
attributes[attribute["name"]] = None
elif attribute["type"] == "string":
attributes[attribute["name"]] = blender_attribute.string_value
elif attribute["type"] == "list":
values = blender_attribute.string_value[1:-1].split(", ")
if attribute["list_type"] == "float":
values = [float(v) for v in values]
elif attribute["list_type"] == "integer":
values = [int(v) for v in values]
attributes[attribute["name"]] = values
elif attribute["type"] == "integer":
attributes[attribute["name"]] = blender_attribute.int_value
elif attribute["type"] == "float":
attributes[attribute["name"]] = blender_attribute.float_value
elif attribute["type"] == "enum":
attributes[attribute["name"]] = blender_attribute.enum_value
product = self.file.by_id(oprops.ifc_definition_id)
ifcopenshell.api.run("attribute.edit_attributes", self.file, **{"product": product, "attributes": attributes})
Data.load(IfcStore.get_file(), oprops.ifc_definition_id)
product = tool.Ifc.get_entity(obj)
def callback(attributes, prop):
if prop.name in ("RefLatitude", "RefLongitude"):
if prop.is_null:
attributes[prop.name] = None
else:
try:
attributes[prop.name] = json.loads(prop.string_value)
except:
attributes[prop.name] = None
return True
attributes = blenderbim.bim.helper.export_attributes(props.attributes, callback=callback)
ifcopenshell.api.run("attribute.edit_attributes", self.file, product=product, attributes=attributes)
bpy.ops.bim.disable_editing_attributes(obj=obj.name, obj_type=self.obj_type)
return {"FINISHED"}
@@ -143,5 +142,5 @@ class CopyAttributeToSelection(bpy.types.Operator, Operator):
def _execute(self, context):
value = context.active_object.BIMAttributeProperties.attributes.get(self.name).get_value()
for obj in context.selected_objects:
for obj in tool.Blender.get_selected_objects():
core.copy_attribute_to_selection(tool.Ifc, name=self.name, value=value, obj=obj)
@@ -94,4 +94,10 @@ class BIM_PT_material_attributes(Panel):
def draw(self, context):
if not MaterialAttributesData.is_loaded:
MaterialAttributesData.load()
elif (
context.active_object.active_material.BIMObjectProperties.ifc_definition_id
!= MaterialAttributesData.data["ifc_definition_id"]
):
MaterialAttributesData.load()
draw_ui(context, self.layout, "Material", MaterialAttributesData.data["attributes"])
@@ -20,43 +20,44 @@ import bpy
from . import ui, prop, operator
classes = (
operator.NewBcfProject,
operator.LoadBcfProject,
operator.LoadBcfTopics,
operator.LoadBcfTopic,
operator.LoadBcfComments,
operator.EditBcfProjectName,
operator.EditBcfAuthor,
operator.EditBcfTopicName,
operator.EditBcfTopic,
operator.SaveBcfProject,
operator.AddBcfTopic,
operator.AddBcfHeaderFile,
operator.AddBcfBimSnippet,
operator.AddBcfReferenceLink,
operator.AddBcfDocumentReference,
operator.AddBcfLabel,
operator.AddBcfRelatedTopic,
operator.ViewBcfTopic,
operator.RemoveBcfTopic,
operator.RemoveBcfComment,
operator.RemoveBcfBimSnippet,
operator.RemoveBcfReferenceLink,
operator.RemoveBcfDocumentReference,
operator.RemoveBcfRelatedTopic,
operator.EditBcfReferenceLinks,
operator.EditBcfLabels,
operator.RemoveBcfLabel,
operator.EditBcfComment,
operator.AddBcfComment,
operator.ActivateBcfViewpoint,
operator.AddBcfBimSnippet,
operator.AddBcfComment,
operator.AddBcfDocumentReference,
operator.AddBcfHeaderFile,
operator.AddBcfLabel,
operator.AddBcfReferenceLink,
operator.AddBcfRelatedTopic,
operator.AddBcfTopic,
operator.AddBcfViewpoint,
operator.RemoveBcfViewpoint,
operator.RemoveBcfFile,
operator.EditBcfAuthor,
operator.EditBcfComment,
operator.EditBcfLabels,
operator.EditBcfProjectName,
operator.EditBcfReferenceLinks,
operator.EditBcfTopic,
operator.EditBcfTopicName,
operator.ExtractBcfFile,
operator.LoadBcfComments,
operator.LoadBcfProject,
operator.LoadBcfTopic,
operator.LoadBcfTopics,
operator.NewBcfProject,
operator.OpenBcfReferenceLink,
operator.SelectBcfHeaderFile,
operator.RemoveBcfBimSnippet,
operator.RemoveBcfComment,
operator.RemoveBcfDocumentReference,
operator.RemoveBcfFile,
operator.RemoveBcfLabel,
operator.RemoveBcfReferenceLink,
operator.RemoveBcfRelatedTopic,
operator.RemoveBcfTopic,
operator.RemoveBcfViewpoint,
operator.SaveBcfProject,
operator.SelectBcfBimSnippetReference,
operator.SelectBcfDocumentReference,
operator.SelectBcfHeaderFile,
operator.ViewBcfTopic,
prop.BcfReferenceLink,
prop.BcfLabel,
prop.BcfBimSnippet,
@@ -20,14 +20,20 @@ import os
import bpy
import bcf
import bcf.bcfxml
import bcf.v2.model
import uuid
import numpy as np
import tempfile
import webbrowser
import ifcopenshell
import ifcopenshell.util.unit
from . import bcfstore
import blenderbim.bim.module.bcf.prop as bcf_prop
from . import bcfstore
from pathlib import Path
from blenderbim.bim.ifc import IfcStore
from math import radians, degrees, atan, tan, cos, sin
from mathutils import Vector, Matrix, Euler, geometry
from xsdata.models.datatype import XmlDateTime
class NewBcfProject(bpy.types.Operator):
@@ -56,7 +62,6 @@ class LoadBcfProject(bpy.types.Operator):
if self.filepath:
bcfstore.BcfStore.bcfxml = bcf.bcfxml.load(self.filepath)
bcfxml = bcfstore.BcfStore.get_bcfxml()
bcfxml.get_project()
context.scene.BCFProperties.name = bcfxml.project.name
bpy.ops.bim.load_bcf_topics()
context.scene.BCFProperties.is_loaded = True
@@ -74,7 +79,6 @@ class LoadBcfTopics(bpy.types.Operator):
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
bcfxml.get_topics()
context.scene.BCFProperties.topics.clear()
for index, topic_guid in enumerate(bcfxml.topics.keys()):
new = context.scene.BCFProperties.topics.add()
@@ -91,7 +95,7 @@ class LoadBcfTopic(bpy.types.Operator):
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
topic = bcfxml.get_topic(self.topic_guid)
topic = bcfxml.topics[self.topic_guid]
bcfxml.get_header(self.topic_guid)
new = context.scene.BCFProperties.topics[self.topic_index]
data_map = {
@@ -125,10 +129,10 @@ class LoadBcfTopic(bpy.types.Operator):
if topic.topic.bim_snippet:
data_map = {
"type": topic.bim_snippet.snippet_type,
"is_external": topic.bim_snippet.is_external,
"reference": topic.bim_snippet.reference,
"schema": topic.bim_snippet.reference_schema,
"type": topic.topic.bim_snippet.snippet_type,
"is_external": topic.topic.bim_snippet.is_external,
"reference": topic.topic.bim_snippet.reference,
"schema": topic.topic.bim_snippet.reference_schema,
}
for key, value in data_map.items():
if value is not None:
@@ -164,10 +168,9 @@ class LoadBcfComments(bpy.types.Operator):
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
bcfxml.get_comments(self.topic_guid)
blender_topic = context.scene.BCFProperties.topics.get(self.topic_guid)
blender_topic.comments.clear()
for comment in bcfxml.topics[self.topic_guid].comments.values():
for comment in bcfxml.topics[self.topic_guid].comments:
new = blender_topic.comments.add()
data_map = {
"name": comment.guid,
@@ -192,7 +195,6 @@ class EditBcfProjectName(bpy.types.Operator):
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
bcfxml.project.name = context.scene.BCFProperties.name
bcfxml.edit_project()
return {"FINISHED"}
@@ -216,9 +218,8 @@ class EditBcfTopicName(bpy.types.Operator):
props = context.scene.BCFProperties
blender_topic = props.active_topic
bcfxml = bcfstore.BcfStore.get_bcfxml()
topic = bcfxml.topics[blender_topic.name]
topic = bcfxml.topics[blender_topic.name].topic
topic.title = blender_topic.title
bcfxml.edit_topic(topic)
return {"FINISHED"}
@@ -232,7 +233,7 @@ class EditBcfTopic(bpy.types.Operator):
blender_topic = props.active_topic
bcfxml = bcfstore.BcfStore.get_bcfxml()
topic = bcfxml.topics[blender_topic.name]
topic = bcfxml.topics[blender_topic.name].topic
topic.title = blender_topic.title or None
topic.priority = blender_topic.priority or None
topic.due_date = blender_topic.due_date or None
@@ -242,7 +243,6 @@ class EditBcfTopic(bpy.types.Operator):
topic.topic_status = blender_topic.status or None
topic.topic_type = blender_topic.type or None
bcfxml.edit_topic(topic)
props.refresh_topic(context)
return {"FINISHED"}
@@ -256,7 +256,7 @@ class SaveBcfProject(bpy.types.Operator):
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
bcfxml.save_project(self.filepath)
bcfxml.save(self.filepath)
return {"FINISHED"}
def invoke(self, context, event):
@@ -299,15 +299,17 @@ class AddBcfBimSnippet(bpy.types.Operator):
props = context.scene.BCFProperties
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
bim_snippet = bcf.v2.data.BimSnippet()
bim_snippet.reference = props.bim_snippet_reference
bim_snippet.reference_schema = props.bim_snippet_schema
bim_snippet.snippet_type = props.bim_snippet_type
bcfxml.add_bim_snippet(topic, bim_snippet)
is_external = "://" in props.bim_snippet_reference
bim_snippet = bcf.v2.model.BimSnippet(
reference=props.bim_snippet_reference if is_external else Path(props.bim_snippet_reference).name,
reference_schema=props.bim_snippet_schema,
snippet_type=props.bim_snippet_type,
is_external=is_external,
)
topic.topic.bim_snippet = bim_snippet
with open(props.bim_snippet_reference, "r") as f:
topic.bim_snippet = f.read()
bpy.ops.bim.load_bcf_topic(topic_guid=topic.guid, topic_index=props.active_topic_index)
bim_snippet.reference = ""
bim_snippet.reference_schema = ""
bim_snippet.snippet_type = ""
return {"FINISHED"}
@@ -326,15 +328,14 @@ class AddBcfRelatedTopic(bpy.types.Operator):
if props.related_topic == blender_topic.title:
# Prevent adding self as related topic
return False
related_topic = None
related_topic_guid = None
for topic in bcfxml.topics.values():
if topic.title == props.related_topic:
related_topic = bcf.v2.data.RelatedTopic()
related_topic.guid = topic.guid
if topic.topic.title == props.related_topic:
related_topic_guid = topic.guid
break
if not related_topic:
if not related_topic_guid:
return False
if str(related_topic.guid) in [t.name for t in blender_topic.related_topics]:
if str(related_topic_guid) in [t.name for t in blender_topic.related_topics]:
# Prevent adding the same related topic more than once
return False
return True
@@ -343,11 +344,12 @@ class AddBcfRelatedTopic(bpy.types.Operator):
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = context.scene.BCFProperties
blender_topic = props.active_topic
related_topic = bcf.v2.data.RelatedTopic()
related_topic.guid = next((t for t in bcfxml.topics.values() if t.title == props.related_topic)).guid
topic = bcfxml.topics[blender_topic.name]
topic.related_topics.append(related_topic)
bcfxml.edit_topic(topic)
topic.topic.related_topic.append(
bcf.v2.model.TopicRelatedTopic(
guid=next((t for t in bcfxml.topics.values() if t.topic.title == props.related_topic)).guid
)
)
bpy.ops.bim.load_bcf_topic(topic_guid=topic.guid, topic_index=props.active_topic_index)
props.related_topic = ""
return {"FINISHED"}
@@ -367,15 +369,24 @@ class AddBcfHeaderFile(bpy.types.Operator):
props = context.scene.BCFProperties
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
header_file = bcf.v2.data.HeaderFile()
header_file.reference = props.file_reference
if not os.path.exists(header_file.reference):
header_file.filename = header_file.reference
if len(props.file_ifc_project) == 22:
header_file.ifc_project = props.file_ifc_project
if len(props.file_ifc_spatial_structure_element) == 22:
header_file.ifc_spatial_structure_element = props.file_ifc_spatial_structure_element
bcfxml.add_file(topic, header_file)
is_external = "://" in props.file_reference
filename = Path(props.file_reference).name if os.path.exists(props.file_reference) else None
if filename:
with open(props.file_reference, "r") as f:
topic.reference_files[filename] = f.read()
header_file = bcf.v2.model.HeaderFile(
filename=filename,
date=XmlDateTime.now(),
reference=props.file_reference if is_external else Path(props.file_reference).name,
ifc_project=props.file_ifc_project,
ifc_spatial_structure_element=props.file_ifc_spatial_structure_element,
is_external=is_external,
)
topic.header.file.append(header_file)
props.refresh_topic(context)
return {"FINISHED"}
@@ -409,27 +420,34 @@ class AddBcfViewpoint(bpy.types.Operator):
props = context.scene.BCFProperties
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
viewpoint = bcf.v2.data.Viewpoint()
if blender_camera.data.type == "ORTHO":
camera = bcf.v2.data.OrthogonalCamera()
camera.view_to_world_scale = blender_camera.data.ortho_scale
viewpoint.orthogonal_camera = camera
elif blender_camera.data.type == "PERSP":
camera = bcf.v2.data.PerspectiveCamera()
camera.field_of_view = degrees(blender_camera.data.angle)
viewpoint.perspective_camera = camera
camera.camera_view_point.x = blender_camera.location.x
camera.camera_view_point.y = blender_camera.location.y
camera.camera_view_point.z = blender_camera.location.z
direction = blender_camera.matrix_world.to_quaternion() @ Vector((0.0, 0.0, -1.0))
camera.camera_direction.x = direction.x
camera.camera_direction.y = direction.y
camera.camera_direction.z = direction.z
up = blender_camera.matrix_world.to_quaternion() @ Vector((0.0, 1.0, 0.0))
camera.camera_up_vector.x = up.x
camera.camera_up_vector.y = up.y
camera.camera_up_vector.z = up.z
camera_view_point = bcf.v2.model.Point(
x=blender_camera.location.x, y=blender_camera.location.y, z=blender_camera.location.z
)
camera_direction = bcf.v2.model.Direction(x=direction.x, y=direction.y, z=direction.z)
camera_up_vector = bcf.v2.model.Direction(x=up.x, y=up.y, z=up.z)
if blender_camera.data.type == "ORTHO":
camera = bcf.v2.model.OrthogonalCamera(
view_to_world_scale=blender_camera.data.ortho_scale,
camera_view_point=camera_view_point,
camera_direction=camera_direction,
camera_up_vector=camera_up_vector,
)
visualization_info = bcf.v2.model.VisualizationInfo(orthogonal_camera=camera)
elif blender_camera.data.type == "PERSP":
camera = bcf.v2.model.PerspectiveCamera(
field_of_view=degrees(blender_camera.data.angle),
camera_view_point=camera_view_point,
camera_direction=camera_direction,
camera_up_vector=camera_up_vector,
)
visualization_info = bcf.v2.model.VisualizationInfo(guid=str(uuid.uuid4()), perspective_camera=camera)
# TODO allow the user to enable or disable snapshotting
snapshot = None
blender_render = context.scene.render
old_file_format = blender_render.image_settings.file_format
@@ -437,9 +455,17 @@ class AddBcfViewpoint(bpy.types.Operator):
old_filepath = blender_render.filepath
blender_render.filepath = os.path.join(context.scene.BIMProperties.data_dir, "snapshot.png")
bpy.ops.render.opengl(write_still=True)
viewpoint.snapshot = blender_render.filepath
with open(blender_render.filepath, "rb") as f:
snapshot = f.read()
# viewpoint.snapshot = blender_render.filepath
# TODO we should handle selection and visibility state
vizinfo = bcf.v2.visinfo.VisualizationInfoHandler(visualization_info=visualization_info, snapshot=snapshot)
topic.viewpoints[vizinfo.guid + ".bcfv"] = vizinfo
topic.markup.viewpoints.append(
bcf.v2.model.ViewPoint(viewpoint=vizinfo.guid + ".bcfv", guid=vizinfo.guid, snapshot=vizinfo.guid + ".png")
)
bcfxml.add_viewpoint(topic, viewpoint)
blender_render.filepath = old_filepath
blender_render.image_settings.file_format = old_file_format
props.refresh_topic(context)
@@ -459,9 +485,15 @@ class RemoveBcfViewpoint(bpy.types.Operator):
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = context.scene.BCFProperties
blender_topic = props.active_topic
viewpoint_guid = blender_topic.viewpoints
topic = bcfxml.topics[blender_topic.name]
bcfxml.delete_viewpoint(viewpoint_guid, topic)
for key, viewpoint in topic.viewpoints.items():
if viewpoint.guid == blender_topic.viewpoints:
break
del topic.viewpoints[key]
for i, viewpoint in enumerate(topic.markup.viewpoints):
if viewpoint.guid == blender_topic.viewpoints:
break
del topic.markup.viewpoints[i]
props.refresh_topic(context)
return {"FINISHED"}
@@ -477,7 +509,7 @@ class RemoveBcfFile(bpy.types.Operator):
props = context.scene.BCFProperties
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
bcfxml.delete_file(topic, self.index)
del topic.header.file[self.index]
props.refresh_topic(context)
return {"FINISHED"}
@@ -495,8 +527,7 @@ class RemoveBcfTopic(bpy.types.Operator):
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = context.scene.BCFProperties
topic_to_delete = props.active_topic
bcfxml.delete_topic(topic_to_delete.name)
del bcfxml.topics[props.active_topic.name]
bpy.ops.bim.load_bcf_topics()
return {"FINISHED"}
@@ -515,8 +546,7 @@ class AddBcfReferenceLink(bpy.types.Operator):
props = context.scene.BCFProperties
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
topic.reference_links.append(props.reference_link)
bcfxml.edit_topic(topic)
topic.topic.reference_link.append(props.reference_link)
bpy.ops.bim.load_bcf_topic(topic_guid=topic.guid, topic_index=props.active_topic_index)
props.reference_link = ""
return {"FINISHED"}
@@ -536,10 +566,23 @@ class AddBcfDocumentReference(bpy.types.Operator):
props = context.scene.BCFProperties
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
document_reference = bcf.v2.data.DocumentReference()
document_reference.referenced_document = props.document_reference
document_reference.description = props.document_reference_description or None
bcfxml.add_document_reference(topic, document_reference)
is_external = "://" in props.document_reference
filename = Path(props.document_reference).name if os.path.exists(props.document_reference) else None
if filename:
with open(props.document_reference, "rb") as f:
topic.document_references[filename] = f.read()
document_reference = bcf.v2.model.TopicDocumentReference(
referenced_document=props.document_reference if is_external else filename,
description=props.document_reference_description or None,
guid=str(uuid.uuid4()),
is_external=is_external,
)
topic.topic.document_reference.append(document_reference)
bpy.ops.bim.load_bcf_topic(topic_guid=topic.guid, topic_index=props.active_topic_index)
props.document_reference = ""
props.document_reference_description = ""
@@ -562,8 +605,7 @@ class AddBcfLabel(bpy.types.Operator):
topic = bcfxml.topics[blender_topic.name]
new = blender_topic.labels.add()
new.name = props.label
topic.labels.append(props.label)
bcfxml.edit_topic(topic)
topic.topic.labels.append(props.label)
props.label = ""
return {"FINISHED"}
@@ -578,11 +620,10 @@ class EditBcfReferenceLinks(bpy.types.Operator):
props = context.scene.BCFProperties
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
for index, reference_link in enumerate(topic.reference_links):
for index, reference_link in enumerate(topic.topic.reference_link):
if reference_link == blender_topic.reference_links[index].name:
continue
topic.reference_links[index] = blender_topic.reference_links[index].name
bcfxml.edit_topic(topic)
topic.topic.reference_link[index] = blender_topic.reference_links[index].name
return {"FINISHED"}
@@ -597,12 +638,11 @@ class EditBcfLabels(bpy.types.Operator):
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
for index, label in enumerate(blender_topic.labels):
if index >= len(topic.labels):
if index >= len(topic.topic.labels):
break
if label.name == topic.labels[index]:
if label.name == topic.topic.labels[index]:
continue
topic.labels[index] = blender_topic.labels[index].name
bcfxml.edit_topic(topic)
topic.topic.labels[index] = blender_topic.labels[index].name
return {"FINISHED"}
@@ -617,8 +657,7 @@ class RemoveBcfReferenceLink(bpy.types.Operator):
props = context.scene.BCFProperties
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
del topic.reference_links[self.index]
bcfxml.edit_topic(topic)
del topic.topic.reference_link[self.index]
blender_topic.reference_links.remove(self.index)
return {"FINISHED"}
@@ -634,8 +673,7 @@ class RemoveBcfLabel(bpy.types.Operator):
props = context.scene.BCFProperties
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
del topic.labels[self.index]
bcfxml.edit_topic(topic)
del topic.topic.labels[self.index]
blender_topic.labels.remove(self.index)
return {"FINISHED"}
@@ -650,7 +688,7 @@ class RemoveBcfBimSnippet(bpy.types.Operator):
props = context.scene.BCFProperties
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
bcfxml.delete_bim_snippet(topic)
topic.topic.bim_snippet = None
blender_topic.bim_snippet.schema = ""
blender_topic.bim_snippet.reference = ""
blender_topic.bim_snippet.type = ""
@@ -668,7 +706,7 @@ class RemoveBcfDocumentReference(bpy.types.Operator):
props = context.scene.BCFProperties
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
bcfxml.delete_document_reference(topic, self.index)
del topic.topic.document_reference[self.index]
bpy.ops.bim.load_bcf_topic(topic_guid=topic.guid, topic_index=props.active_topic_index)
return {"FINISHED"}
@@ -684,8 +722,7 @@ class RemoveBcfRelatedTopic(bpy.types.Operator):
props = context.scene.BCFProperties
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
del topic.related_topics[self.index]
bcfxml.edit_topic(topic)
del topic.topic.related_topic[self.index]
bpy.ops.bim.load_bcf_topic(topic_guid=topic.guid, topic_index=props.active_topic_index)
return {"FINISHED"}
@@ -701,7 +738,10 @@ class RemoveBcfComment(bpy.types.Operator):
props = context.scene.BCFProperties
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
bcfxml.delete_comment(self.comment_guid, topic)
for i, comment in enumerate(topic.comments):
if comment.guid == self.comment_guid:
break
del topic.comments[i]
bpy.ops.bim.load_bcf_comments(topic_guid=topic.guid)
return {"FINISHED"}
@@ -718,9 +758,11 @@ class EditBcfComment(bpy.types.Operator):
blender_topic = props.active_topic
blender_comment = blender_topic.comments.get(self.comment_guid)
topic = bcfxml.topics[blender_topic.name]
comment = topic.comments[self.comment_guid]
comment.comment = blender_comment.comment
bcfxml.edit_comment(comment, topic)
for i, comment in enumerate(topic.comments):
if comment.guid == self.comment_guid:
comment.comment = blender_comment.comment
comment.modified_date = XmlDateTime.now()
comment.modified_author = context.scene.BCFProperties.author
bpy.ops.bim.load_bcf_comments(topic_guid=topic.guid)
return {"FINISHED"}
@@ -745,12 +787,15 @@ class AddBcfComment(bpy.types.Operator):
props = context.scene.BCFProperties
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
comment = bcf.v2.data.Comment()
comment.comment = props.comment
comment = bcf.v2.model.Comment(
date=XmlDateTime.now(),
author=context.scene.BCFProperties.author,
comment=props.comment,
guid=str(uuid.uuid4()),
)
if props.has_related_viewpoint:
comment.viewpoint = bcf.v2.data.Viewpoint()
comment.viewpoint.guid = blender_topic.viewpoints
bcfxml.add_comment(topic, comment)
comment.viewpoint = bcf.v2.model.CommentViewpoint(guid=blender_topic.viewpoints)
topic.comments.append(comment)
bpy.ops.bim.load_bcf_comments(topic_guid=topic.guid)
props.comment = ""
props.has_related_viewpoint = False
@@ -791,11 +836,13 @@ class ActivateBcfViewpoint(bpy.types.Operator):
cam_height = context.scene.render.resolution_y
cam_aspect = cam_width / cam_height
obj.data.background_images.clear()
if viewpoint.snapshot:
obj.data.show_background_images = True
obj.data.background_images.clear()
background = obj.data.background_images.new()
background.image = bpy.data.images.load(os.path.join(bcfxml.filepath, topic.guid, viewpoint.snapshot))
with tempfile.NamedTemporaryFile(delete=False) as f:
f.write(viewpoint.snapshot)
background.image = bpy.data.images.load(f.name)
src_width = background.image.size[0]
src_height = background.image.size[1]
src_aspect = src_width / src_height
@@ -805,6 +852,9 @@ class ActivateBcfViewpoint(bpy.types.Operator):
else:
background.frame_method = "CROP"
background.display_depth = "FRONT"
else:
obj.data.show_background_images = False
area = next(area for area in context.screen.areas if area.type == "VIEW_3D")
area.spaces[0].region_3d.view_perspective = "CAMERA"
@@ -814,27 +864,27 @@ class ActivateBcfViewpoint(bpy.types.Operator):
gp = bpy.data.grease_pencils.get("BCF")
if gp:
bpy.data.grease_pencils.remove(gp)
if viewpoint.lines:
if viewpoint.visualization_info.lines:
self.draw_lines(viewpoint, context)
self.delete_clipping_planes(context)
if viewpoint.clipping_planes:
if viewpoint.visualization_info.clipping_planes:
self.create_clipping_planes(viewpoint)
self.delete_bitmaps(context)
if viewpoint.bitmaps:
if viewpoint.visualization_info.bitmap:
self.create_bitmaps(bcfxml, viewpoint, topic)
self.setup_camera(viewpoint, obj, cam_aspect, context)
return {"FINISHED"}
def setup_camera(self, viewpoint, obj, cam_aspect, context):
if viewpoint.orthogonal_camera:
camera = viewpoint.orthogonal_camera
if viewpoint.visualization_info.orthogonal_camera:
camera = viewpoint.visualization_info.orthogonal_camera
obj.data.type = "ORTHO"
obj.data.ortho_scale = viewpoint.orthogonal_camera.view_to_world_scale
elif viewpoint.perspective_camera:
camera = viewpoint.perspective_camera
obj.data.ortho_scale = viewpoint.visualization_info.orthogonal_camera.view_to_world_scale
elif viewpoint.visualization_info.perspective_camera:
camera = viewpoint.visualization_info.perspective_camera
obj.data.type = "PERSP"
if cam_aspect >= 1:
obj.data.angle = radians(camera.field_of_view)
@@ -875,22 +925,22 @@ class ActivateBcfViewpoint(bpy.types.Operator):
obj.matrix_world = Matrix(matrix.tolist())
def set_viewpoint_components(self, viewpoint, context):
if not viewpoint.components:
if not viewpoint.visualization_info.components:
return
# Operators with context overrides are used because they are
# significantly faster than looping through all objects
exception_global_ids = {v.ifc_guid for v in viewpoint.components.visibility.exceptions}
exception_global_ids = {v.ifc_guid for v in viewpoint.visualization_info.components.visibility.exceptions or []}
if viewpoint.components.visibility.default_visibility:
if viewpoint.visualization_info.components.visibility.default_visibility:
old = context.area.type
context.area.type = "VIEW_3D"
bpy.ops.object.hide_view_clear()
context.area.type = old
for global_id in exception_global_ids:
obj = IfcStore.get_element(global_id)
if obj:
if obj and bpy.context.view_layer.objects.get(obj.name):
obj.hide_set(True)
else:
objs = []
@@ -907,11 +957,13 @@ class ActivateBcfViewpoint(bpy.types.Operator):
bpy.ops.object.hide_view_set(context_override, unselected=True)
context.area.type = old
if viewpoint.components.view_setup_hints:
if not viewpoint.components.view_setup_hints.spaces_visible:
if viewpoint.visualization_info.components.view_setup_hints:
if not viewpoint.visualization_info.components.view_setup_hints.spaces_visible:
self.hide_spaces(context)
if viewpoint.components.view_setup_hints.openings_visible is not None:
self.set_openings_visibility(viewpoint.components.view_setup_hints.openings_visible, context)
if viewpoint.visualization_info.components.view_setup_hints.openings_visible is not None:
self.set_openings_visibility(
viewpoint.visualization_info.components.view_setup_hints.openings_visible, context
)
else:
self.hide_spaces(context)
self.set_openings_visibility(False, context)
@@ -927,11 +979,12 @@ class ActivateBcfViewpoint(bpy.types.Operator):
context.area.type = old
def set_openings_visibility(self, is_visible, context):
for collection in self.get_opening_collections(context):
collection.hide_viewport = not is_visible
pass # We no longer have an openings collection
def set_selection(self, viewpoint):
selected_global_ids = [s.ifc_guid for s in viewpoint.components.selection]
if not viewpoint.visualization_info.components or not viewpoint.visualization_info.components.selection:
return
selected_global_ids = [s.ifc_guid for s in viewpoint.visualization_info.components.selection.component or []]
bpy.ops.object.select_all(action="DESELECT")
for global_id in selected_global_ids:
obj = IfcStore.get_element(global_id)
@@ -940,7 +993,7 @@ class ActivateBcfViewpoint(bpy.types.Operator):
def set_colours(self, viewpoint):
global_id_colours = {}
for coloring in viewpoint.components.coloring:
for coloring in viewpoint.visualization_info.components.coloring or []:
for component in coloring.components:
global_id_colours.setdefault(component.ifc_guid, coloring.color)
for global_id, color in global_id_colours.items():
@@ -948,14 +1001,6 @@ class ActivateBcfViewpoint(bpy.types.Operator):
if obj:
obj.color = self.hex_to_rgb(color)
def get_opening_collections(self, context):
collections = []
for collection in context.view_layer.layer_collection.children:
opening_collection = collection.children.get("IfcOpeningElements")
if opening_collection:
collections.append(opening_collection)
return collections
def draw_lines(self, viewpoint, context):
gp = bpy.data.grease_pencils.new("BCF")
scene = context.scene
@@ -967,9 +1012,9 @@ class ActivateBcfViewpoint(bpy.types.Operator):
frame = layer.frames.new(1)
stroke = frame.strokes.new()
stroke.display_mode = "3DSPACE"
stroke.points.add(len(viewpoint.lines) * 2)
stroke.points.add(len(viewpoint.visualization_info.lines.line) * 2)
coords = []
for l in viewpoint.lines:
for l in viewpoint.visualization_info.lines.line:
coords.extend(
[l.start_point.x, l.start_point.y, l.start_point.z, l.end_point.x, l.end_point.y, l.end_point.z]
)
@@ -977,7 +1022,7 @@ class ActivateBcfViewpoint(bpy.types.Operator):
def create_clipping_planes(self, viewpoint):
n = 0
for plane in viewpoint.clipping_planes:
for plane in viewpoint.visualization_info.clipping_planes.clipping_plane:
bpy.ops.bim.add_section_plane()
if n == 0:
obj = bpy.data.objects["Section"]
@@ -1010,10 +1055,14 @@ class ActivateBcfViewpoint(bpy.types.Operator):
collection = bpy.data.collections.get("Bitmaps")
if not collection:
collection = bpy.data.collections.new("Bitmaps")
for bitmap in viewpoint.bitmaps:
for bitmap in viewpoint.visualization_info.bitmap:
obj = bpy.data.objects.new("Bitmap", None)
obj.empty_display_type = "IMAGE"
image = bpy.data.images.load(os.path.join(bcfxml.filepath, topic.guid, bitmap.reference))
# image = bpy.data.images.load(os.path.join(bcfxml.filepath, topic.guid, bitmap.reference))
with tempfile.NamedTemporaryFile(delete=False) as f:
topic.extract_file(bitmap, f.name)
# f.write(bitmap.what)
image = bpy.data.images.load(f.name)
src_width = image.size[0]
src_height = image.size[1]
if src_height > src_width:
@@ -1052,6 +1101,7 @@ class SelectBcfHeaderFile(bpy.types.Operator):
bl_label = "Select BCF Header File"
bl_options = {"REGISTER", "UNDO"}
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
filter_glob: bpy.props.StringProperty(default="*.ifc;*.ifczip;*.ifcxml;*.ifcjson", options={"HIDDEN"})
def execute(self, context):
if self.filepath:
@@ -1093,3 +1143,25 @@ class SelectBcfDocumentReference(bpy.types.Operator):
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
return {"RUNNING_MODAL"}
class ExtractBcfFile(bpy.types.Operator):
bl_idname = "bim.extract_bcf_file"
bl_label = "Extract BCF Header File"
bl_options = {"REGISTER", "UNDO"}
entity_type: bpy.props.StringProperty()
index: bpy.props.IntProperty()
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
topic = bcfxml.topics[context.scene.BCFProperties.active_topic.name]
if self.entity_type == "HEADER_FILE":
entity = topic.header.file[self.index]
elif self.entity_type == "BIM_SNIPPET":
entity = topic.markup.topic.bim_snippet
elif self.entity_type == "DOCUMENT_REFERENCE":
entity = topic.markup.topic.document_reference[self.index]
webbrowser.open(str(topic.extract_file(entity).parent))
return {"FINISHED"}
@@ -95,8 +95,8 @@ def getBcfViewpoints(self, context, force_update=False):
props = context.scene.BCFProperties
bcfxml = bcfstore.BcfStore.get_bcfxml()
topic = props.active_topic
viewpoints = bcfxml.get_viewpoints(topic.name) if topic else {}
bcfviewpoints_enum.extend([(v, f"Viewpoint {i+1}", "") for i, v in enumerate(viewpoints.keys())])
viewpoints = bcfxml.topics[topic.name].viewpoints.keys() if topic else []
bcfviewpoints_enum.extend([(v, f"Viewpoint {i+1}", "") for i, v in enumerate(viewpoints)])
return bcfviewpoints_enum
+12 -9
View File
@@ -127,20 +127,21 @@ class BIM_PT_bcf_metadata(Panel):
layout.label(text="Header Files:")
if bcf_topic.header:
for index, f in enumerate(bcf_topic.header.files):
for index, f in enumerate(bcf_topic.header.file):
box = self.layout.box()
row = box.row(align=True)
row.label(text=f.filename, icon="FILE_BLANK")
if f.is_external:
row.operator("bim.open_uri", icon="URL", text="").uri = f.reference
else:
op = row.operator("bim.open_uri", icon="FILE_FOLDER", text="")
op.uri = os.path.join(bcfxml.filepath, topic.name, f.reference)
op = row.operator("bim.extract_bcf_file", icon="FILE_FOLDER", text="")
op.index = index
op.entity_type = "HEADER_FILE"
row.operator("bim.remove_bcf_file", icon="X", text="").index = index
row = box.row()
row.label(text="Date")
row.label(text=f.date)
row.label(text=str(f.date))
if f.ifc_project:
row = box.row()
@@ -196,8 +197,8 @@ class BIM_PT_bcf_metadata(Panel):
if topic.bim_snippet.is_external:
row.operator("bim.open_uri", icon="URL", text="").uri = topic.bim_snippet.reference
else:
op = row.operator("bim.open_uri", icon="FILE_FOLDER", text="")
op.uri = os.path.join(bcfxml.filepath, topic.name, topic.bim_snippet.reference)
op = row.operator("bim.extract_bcf_file", icon="FILE_FOLDER", text="")
op.entity_type = "BIM_SNIPPET"
row.operator("bim.remove_bcf_bim_snippet", icon="X", text="")
else:
row = layout.row(align=True)
@@ -218,8 +219,10 @@ class BIM_PT_bcf_metadata(Panel):
if doc.is_external:
row.operator("bim.open_uri", icon="URL", text="").uri = doc.reference
else:
op = row.operator("bim.open_uri", icon="FILE_FOLDER", text="")
op.uri = os.path.join(bcfxml.filepath, topic.name, doc.reference)
op = row.operator("bim.extract_bcf_file", icon="FILE_FOLDER", text="")
op.entity_type = "DOCUMENT_REFERENCE"
op.index = index
row.operator("bim.remove_bcf_document_reference", icon="X", text="").index = index
row = box.row(align=True)
row.prop(doc, "description")
@@ -236,7 +239,7 @@ class BIM_PT_bcf_metadata(Panel):
try:
row = layout.row(align=True)
op = row.operator(
"bim.view_bcf_topic", text=f"Select {bcfxml.topics[related_topic.name.lower()].title}"
"bim.view_bcf_topic", text=f"Select {bcfxml.topics[related_topic.name.lower()].topic.title}"
)
op.topic_guid = related_topic.name
row.operator("bim.remove_bcf_related_topic", icon="X", text="").index = index
@@ -20,25 +20,37 @@ import bpy
from . import ui, operator, prop
classes = (
operator.AddBoundary,
operator.ColourByRelatedBuildingElement,
operator.DisableEditingBoundary,
operator.DisableEditingBoundaryGeometry,
operator.EditBoundaryAttributes,
operator.EditBoundaryGeometry,
operator.EnableEditingBoundary,
operator.EnableEditingBoundaryGeometry,
operator.HideBoundaries,
operator.LoadBoundary,
operator.LoadProjectSpaceBoundaries,
operator.LoadSpaceBoundaries,
operator.LoadBoundary,
operator.SelectProjectBoundaries,
operator.ColourByRelatedBuildingElement,
operator.EnableEditingBoundary,
operator.DisableEditingBoundary,
operator.EditBoundaryAttributes,
operator.SelectRelatedElementBoundaries,
operator.SelectRelatedElementTypeBoundaries,
operator.SelectSpaceBoundaries,
operator.ShowBoundaries,
operator.UpdateBoundaryGeometry,
ui.BIM_PT_Boundary,
ui.BIM_PT_SpaceBoundaries,
ui.BIM_PT_SceneBoundaries,
ui.BIM_PT_SpaceBoundaries,
prop.BIMBoundaryProperties,
prop.BIMObjectBoundaryProperties,
)
def register():
bpy.types.Object.bim_boundary_properties = bpy.props.PointerProperty(type=prop.BIMBoundaryProperties)
bpy.types.Scene.BIMBoundaryProperties = bpy.props.PointerProperty(type=prop.BIMBoundaryProperties)
bpy.types.Object.bim_boundary_properties = bpy.props.PointerProperty(type=prop.BIMObjectBoundaryProperties)
def unregister():
del bpy.types.Scene.BIMBoundaryProperties
del bpy.types.Object.bim_boundary_properties
@@ -0,0 +1,43 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2023 Dion Moult <dion@thinkmoult.com>
#
# This file is part of BlenderBIM Add-on.
#
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# BlenderBIM Add-on is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import bpy
import blenderbim.tool as tool
def refresh():
SpaceBoundariesData.is_loaded = False
class SpaceBoundariesData:
data = {}
is_loaded = False
@classmethod
def load(cls):
cls.data = {"boundaries": cls.boundaries()}
cls.is_loaded = True
@classmethod
def boundaries(cls):
results = []
element = tool.Ifc.get_entity(bpy.context.active_object)
for rel in element.BoundedBy or []:
description = f"{rel.id()} > {rel.RelatedBuildingElement.is_a()}/{rel.RelatedBuildingElement.Name}"
results.append({"id": rel.id(), "description": description})
return results
@@ -0,0 +1,112 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2023 Dion Moult <dion@thinkmoult.com>
#
# This file is part of BlenderBIM Add-on.
#
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# BlenderBIM Add-on is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import gpu
import bmesh
import blenderbim.tool as tool
from bpy.types import SpaceView3D
from mathutils import Vector
from gpu_extras.batch import batch_for_shader
class BoundaryDecorator:
installed = None
@classmethod
def install(cls, context):
if cls.installed:
cls.uninstall()
handler = cls()
cls.installed = SpaceView3D.draw_handler_add(handler, (context,), "WINDOW", "POST_VIEW")
@classmethod
def uninstall(cls):
try:
SpaceView3D.draw_handler_remove(cls.installed, "WINDOW")
except ValueError:
pass
cls.installed = None
def draw_batch(self, shader_type, content_pos, color, indices=None):
shader = self.line_shader if shader_type == "LINES" else self.shader
batch = batch_for_shader(shader, shader_type, {"pos": content_pos}, indices=indices)
shader.uniform_float("color", color)
batch.draw(shader)
def __call__(self, context):
self.addon_prefs = context.preferences.addons["blenderbim"].preferences
selected_elements_color = self.addon_prefs.decorator_color_selected
unselected_elements_color = self.addon_prefs.decorator_color_unselected
special_elements_color = self.addon_prefs.decorator_color_special
def transparent_color(color, alpha=0.1):
color = [i for i in color]
color[3] = alpha
return color
gpu.state.point_size_set(6)
gpu.state.blend_set("ALPHA")
self.line_shader = gpu.shader.from_builtin("3D_POLYLINE_UNIFORM_COLOR")
self.line_shader.bind() # required to be able to change uniforms of the shader
# POLYLINE_UNIFORM_COLOR specific uniforms
self.line_shader.uniform_float("viewportSize", (context.region.width, context.region.height))
self.line_shader.uniform_float("lineWidth", 2.0)
# general shader
self.shader = gpu.shader.from_builtin("3D_UNIFORM_COLOR")
selected_vertices = []
selected_edges = []
selected_tris = []
unselected_vertices = []
unselected_edges = []
unselected_tris = []
for boundary in context.scene.BIMBoundaryProperties.boundaries:
obj = boundary.obj
if not obj or not obj.data: # A boundary may not have data if it has no connection geometry
continue
if obj.mode == "EDIT":
continue # A profile decorator or something else is used here.
else:
bm = bmesh.new()
bm.from_mesh(obj.data)
obj.data.calc_loop_triangles()
if obj.select_get():
offset = len(selected_vertices)
selected_vertices.extend([tuple(obj.matrix_world @ v.co) for v in bm.verts])
selected_edges.extend([tuple([v.index + offset for v in e.verts]) for e in bm.edges])
selected_tris.extend([tuple([i + offset for i in t.vertices]) for t in obj.data.loop_triangles])
else:
offset = len(unselected_vertices)
unselected_vertices.extend([tuple(obj.matrix_world @ v.co) for v in bm.verts])
unselected_edges.extend([tuple([v.index + offset for v in e.verts]) for e in bm.edges])
unselected_tris.extend([tuple([i + offset for i in t.vertices]) for t in obj.data.loop_triangles])
if obj.mode != "EDIT":
bm.free()
if unselected_edges:
self.draw_batch("LINES", unselected_vertices, special_elements_color, unselected_edges)
self.draw_batch("TRIS", unselected_vertices, transparent_color(special_elements_color), unselected_tris)
if selected_edges:
self.draw_batch("LINES", selected_vertices, selected_elements_color, selected_edges)
self.draw_batch("TRIS", selected_vertices, transparent_color(selected_elements_color), selected_tris)
@@ -17,12 +17,20 @@
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import bpy
import bmesh
import mathutils
import logging
import numpy as np
import ifcopenshell.api
import ifcopenshell.util.placement
import ifcopenshell.util.unit
import blenderbim.tool as tool
from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.boundary.data import Data
import blenderbim.bim.import_ifc as import_ifc
from math import pi, inf
from mathutils import Vector
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.module.model.decorator import ProfileDecorator
from blenderbim.bim.module.boundary.decorator import BoundaryDecorator
def get_boundaries_collection(blender_space):
@@ -35,13 +43,27 @@ def get_boundaries_collection(blender_space):
return boundaries_collection
def disable_editing_boundary_geometry(context):
ProfileDecorator.uninstall()
bpy.ops.object.mode_set(mode="OBJECT")
obj = context.active_object
element = tool.Ifc.get_entity(obj)
old_mesh = obj.data
loader = Loader()
obj.data = loader.create_mesh(element)
tool.Geometry.delete_data(old_mesh)
return {"FINISHED"}
class Loader:
def __init__(self):
self.ifc_file = None
self.logger = None
self.ifc_importer = None
self.settings = None
self.load_settings()
self.settings = self.load_settings()
self.fallback_settings = self.load_fallback_settings()
self.load_importer()
def create_mesh(self, boundary):
@@ -49,18 +71,50 @@ class Loader:
if not boundary.ConnectionGeometry:
return None
surface = boundary.ConnectionGeometry.SurfaceOnRelatingElement
# workaround for invalid geometry provided by Revit. See https://github.com/IfcOpenShell/IfcOpenShell/issues/635#issuecomment-770366838
# Workaround for invalid geometry provided by Revit. See https://github.com/Autodesk/revit-ifc/issues/270
if surface.is_a("IfcCurveBoundedPlane") and not getattr(surface, "InnerBoundaries", None):
surface.InnerBoundaries = ()
shape = ifcopenshell.geom.create_shape(self.settings, surface)
mesh = self.ifc_importer.create_mesh(None, shape)
self.ifc_importer.link_mesh(shape, mesh)
try:
shape = ifcopenshell.geom.create_shape(self.settings, surface)
mesh = self.ifc_importer.create_mesh(None, shape)
tool.Loader.link_mesh(shape, mesh)
except RuntimeError:
# Fallback solution for invalid geometry provided by Revit. (InnerBoundaries cuting OuterBoundary)
print(f"Failed to create mesh from IfcRelSpaceBoundary with ID {boundary.id()}. Geometry might be invalid")
shape = ifcopenshell.geom.create_shape(self.fallback_settings, surface.OuterBoundary)
mesh = bpy.data.meshes.new(str(surface.id()))
bm = bmesh.new()
verts = [bm.verts.new(shape.verts[i : i + 3]) for i in range(0, len(shape.verts), 3)]
bm.faces.new(verts)
for inner_boundary in surface.InnerBoundaries:
shape = ifcopenshell.geom.create_shape(self.fallback_settings, inner_boundary)
verts = [bm.verts.new(shape.verts[i : i + 3]) for i in range(0, len(shape.verts), 3)]
for i in range(len(verts) - 1):
bm.edges.new(verts[i : i + 2])
bm.edges.new((verts[-1], verts[0]))
bm.to_mesh(mesh)
bm.free()
mesh.BIMMeshProperties.ifc_definition_id = surface.id()
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
matrix = mathutils.Matrix(
ifcopenshell.util.placement.get_axis2placement(surface.BasisSurface.Position).tolist()
)
matrix[0][3] *= unit_scale
matrix[1][3] *= unit_scale
matrix[2][3] *= unit_scale
mesh.transform(matrix)
return mesh
def load_settings(self):
self.settings = ifcopenshell.geom.settings()
self.settings.set(self.settings.EXCLUDE_SOLIDS_AND_SURFACES, False)
self.settings.set(self.settings.USE_BREP_DATA, False)
settings = ifcopenshell.geom.settings()
settings.set(settings.EXCLUDE_SOLIDS_AND_SURFACES, False)
settings.set(settings.USE_BREP_DATA, False)
return settings
def load_fallback_settings(self):
settings = ifcopenshell.geom.settings()
settings.set(settings.INCLUDE_CURVES, True)
return settings
def load_importer(self):
self.ifc_file = tool.Ifc.get()
@@ -69,8 +123,7 @@ class Loader:
self.ifc_importer = import_ifc.IfcImporter(ifc_import_settings)
self.ifc_importer.file = self.ifc_file
def load_boundary(self, boundary_id, blender_space):
boundary = self.ifc_file.by_id(boundary_id)
def load_boundary(self, boundary, blender_space):
obj = tool.Ifc.get_object(boundary)
if obj:
return obj
@@ -85,32 +138,27 @@ class Loader:
class LoadProjectSpaceBoundaries(bpy.types.Operator):
bl_idname = "bim.load_project_space_boundaries"
bl_label = "Load all project space boundaries"
bl_label = "Load All Project Space Boundaries"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
loader = Loader()
if not Data.is_loaded:
Data.load(tool.Ifc.get())
for space_id, boundaries_id in Data.spaces.items():
blender_space = tool.Ifc.get_object(tool.Ifc.get().by_id(space_id))
for boundary_id in boundaries_id:
loader.load_boundary(boundary_id, blender_space)
for rel in tool.Ifc.get().by_type("IfcRelSpaceBoundary"):
loader.load_boundary(rel, tool.Ifc.get_object(rel.RelatingSpace))
return {"FINISHED"}
class LoadBoundary(bpy.types.Operator):
bl_idname = "bim.load_boundary"
bl_label = "Load boundary"
bl_label = "Load Boundary"
bl_options = {"REGISTER", "UNDO"}
boundary_id: bpy.props.IntProperty()
def execute(self, context):
loader = Loader()
blender_space = context.active_object
for obj in context.visible_objects:
obj.select_set(False)
obj = loader.load_boundary(self.boundary_id, blender_space)
obj = loader.load_boundary(tool.Ifc.get().by_id(self.boundary_id), context.active_object)
obj.select_set(True)
bpy.context.view_layer.objects.active = obj
return {"FINISHED"}
@@ -118,32 +166,91 @@ class LoadBoundary(bpy.types.Operator):
class LoadSpaceBoundaries(bpy.types.Operator):
bl_idname = "bim.load_space_boundaries"
bl_label = "Load selected space boundaries"
bl_label = "Load Selected Space Boundaries"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
blender_space = context.active_object
oprops = context.active_object.BIMObjectProperties
loader = Loader()
if not Data.is_loaded:
Data.load(tool.Ifc.get())
for boundary_id in Data.spaces.get(oprops.ifc_definition_id, []):
loader.load_boundary(boundary_id, blender_space)
element = tool.Ifc.get_entity(context.active_object)
for rel in element.BoundedBy or []:
loader.load_boundary(rel, context.active_object)
return {"FINISHED"}
class SelectProjectBoundaries(bpy.types.Operator):
bl_idname = "bim.select_project_space_boundaries"
bl_label = "Select all project space boundaries"
def get_element_boundaries(element):
if not element:
return ()
if element.is_a("IfcSpace"):
return (b for b in element.BoundedBy or ())
return (b for b in element.ProvidesBoundaries)
class SelectRelatedElementBoundaries(bpy.types.Operator):
bl_idname = "bim.select_related_element_boundaries"
bl_label = "Select Related Element Space Boundaries"
bl_options = {"REGISTER", "UNDO"}
related_element: bpy.props.IntProperty()
def execute(self, context):
for obj in context.visible_objects:
obj.select_set(False)
element = tool.Ifc.get().by_id(self.related_element)
for rel in get_element_boundaries(element):
obj = tool.Ifc.get_object(rel)
if obj:
obj.select_set(True)
return {"FINISHED"}
class SelectRelatedElementTypeBoundaries(bpy.types.Operator):
bl_idname = "bim.select_related_element_type_boundaries"
bl_label = "Select Related Element Type Space Boundaries"
bl_options = {"REGISTER", "UNDO"}
related_element: bpy.props.IntProperty()
def execute(self, context):
for obj in context.visible_objects:
obj.select_set(False)
element = tool.Ifc.get().by_id(self.related_element)
if not element:
return {"FINISHED"}
element_type = tool.Root.get_element_type(element)
if not element_type:
return {"FINISHED"}
for child in tool.Type.get_type_occurrences(element_type):
for rel in get_element_boundaries(child):
obj = tool.Ifc.get_object(rel)
if obj:
obj.select_set(True)
return {"FINISHED"}
class SelectSpaceBoundaries(bpy.types.Operator):
bl_idname = "bim.select_space_boundaries"
bl_label = "Select All Space Boundaries"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
for obj in context.visible_objects:
obj.select_set(False)
ifc_file = tool.Ifc.get()
for boundary_id in Data.boundaries:
boundary = ifc_file.by_id(boundary_id)
obj = tool.Ifc.get_object(boundary)
element = tool.Ifc.get_entity(context.active_object)
for rel in element.BoundedBy or []:
obj = tool.Ifc.get_object(rel)
if obj:
obj.select_set(True)
return {"FINISHED"}
class SelectProjectBoundaries(bpy.types.Operator):
bl_idname = "bim.select_project_space_boundaries"
bl_label = "Select All Project Space Boundaries"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
for obj in context.visible_objects:
obj.select_set(False)
for rel in tool.Ifc.get().by_type("IfcRelSpaceBoundary"):
obj = tool.Ifc.get_object(rel)
if obj:
obj.select_set(True)
return {"FINISHED"}
@@ -172,7 +279,7 @@ def get_colour(ifc_boundary):
class ColourByRelatedBuildingElement(bpy.types.Operator):
bl_idname = "bim.colour_by_related_building_element"
bl_label = "Apply colour based on related building elements"
bl_label = "Apply Colour Based on Related Building Elements"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
@@ -184,11 +291,10 @@ class ColourByRelatedBuildingElement(bpy.types.Operator):
return result
def _execute(self, context):
self.file = tool.Ifc.get()
for obj in context.visible_objects:
if not obj.BIMObjectProperties.ifc_definition_id:
continue
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
element = tool.Ifc.get_entity(obj)
if not element.is_a("IfcRelSpaceBoundary"):
continue
obj.color = get_colour(element)
@@ -221,7 +327,7 @@ EDITABLE_ATTRIBUTES = {
class EnableEditingBoundary(bpy.types.Operator):
bl_idname = "bim.enable_editing_boundary"
bl_label = "Edit boundary relations"
bl_label = "Edit Boundary Relations"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
@@ -240,7 +346,7 @@ class EnableEditingBoundary(bpy.types.Operator):
class DisableEditingBoundary(bpy.types.Operator):
bl_idname = "bim.disable_editing_boundary"
bl_label = "Disable editing boundary relations"
bl_label = "Disable Editing Boundary Relations"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
@@ -253,7 +359,7 @@ class DisableEditingBoundary(bpy.types.Operator):
class EditBoundaryAttributes(bpy.types.Operator):
bl_idname = "bim.edit_boundary_attributes"
bl_label = "Disable editing boundary relations"
bl_label = "Disable Editing Boundary Relations"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
@@ -274,7 +380,7 @@ class EditBoundaryAttributes(bpy.types.Operator):
class UpdateBoundaryGeometry(bpy.types.Operator):
bl_idname = "bim.update_boundary_geometry"
bl_label = "Update boundary geometry"
bl_label = "Update Boundary Geometry"
bl_description = """
Update boundary connection geometry from mesh.
Mesh must lie on a single plane. It should look like a face or a face with holes.
@@ -289,3 +395,237 @@ class UpdateBoundaryGeometry(bpy.types.Operator):
settings = tool.Boundary.get_assign_connection_geometry_settings(context.active_object)
ifcopenshell.api.run("boundary.assign_connection_geometry", tool.Ifc.get(), **settings)
return {"FINISHED"}
class EnableEditingBoundaryGeometry(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.enable_editing_boundary_geometry"
bl_label = "Enable Editing Boundary Geometry"
bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
return context.selected_objects
def _execute(self, context):
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
obj = context.active_object
element = tool.Ifc.get_entity(obj)
if element.ConnectionGeometry.is_a("IfcConnectionSurfaceGeometry"):
surface = element.ConnectionGeometry.SurfaceOnRelatingElement
tool.Model.import_surface(surface, obj)
bpy.ops.object.mode_set(mode="EDIT")
ProfileDecorator.install(context, exit_edit_mode_callback=lambda: disable_editing_boundary_geometry(context))
if not bpy.app.background:
bpy.ops.wm.tool_set_by_id(tool.Blender.get_viewport_context(), name="bim.cad_tool")
return {"FINISHED"}
class EditBoundaryGeometry(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.edit_boundary_geometry"
bl_label = "Edit Boundary Geometry"
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
ProfileDecorator.uninstall()
bpy.ops.object.mode_set(mode="OBJECT")
obj = context.active_object
element = tool.Ifc.get_entity(obj)
if element.ConnectionGeometry.is_a("IfcConnectionSurfaceGeometry"):
surface = tool.Model.export_surface(obj)
if not surface:
def msg(self, context):
self.layout.label(text="INVALID PROFILE")
bpy.context.window_manager.popup_menu(msg, title="Error", icon="ERROR")
ProfileDecorator.install(
context, exit_edit_mode_callback=lambda: disable_editing_boundary_geometry(context)
)
bpy.ops.object.mode_set(mode="EDIT")
return
old_surface = element.ConnectionGeometry.SurfaceOnRelatingElement
for inverse in tool.Ifc.get().get_inverse(old_surface):
ifcopenshell.util.element.replace_attribute(inverse, old_surface, surface)
ifcopenshell.util.element.remove_deep2(tool.Ifc.get(), old_surface)
old_mesh = obj.data
loader = Loader()
obj.data = loader.create_mesh(element)
tool.Geometry.delete_data(old_mesh)
class DisableEditingBoundaryGeometry(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.disable_editing_boundary_geometry"
bl_label = "Disable Editing Boundary Geometry"
bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
return context.selected_objects
def _execute(self, context):
return disable_editing_boundary_geometry(context)
class ShowBoundaries(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.show_boundaries"
bl_label = "Show Boundaries"
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
props = bpy.context.scene.BIMBoundaryProperties
loader = Loader()
for obj in context.selected_objects:
element = tool.Ifc.get_entity(obj)
if not element or not getattr(element, "BoundedBy", None):
continue
if tool.Ifc.is_moved(obj):
blenderbim.core.geometry.edit_object_placement(tool.Ifc, tool.Geometry, tool.Surveyor, obj=obj)
element = tool.Ifc.get_entity(obj)
for rel in element.BoundedBy or []:
boundary_obj = loader.load_boundary(rel, obj)
new = props.boundaries.add()
new.obj = boundary_obj
BoundaryDecorator.install(bpy.context)
return {"FINISHED"}
class HideBoundaries(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.hide_boundaries"
bl_label = "Hide Boundaries"
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
to_delete = set()
spaces = set()
for obj in context.selected_objects:
element = tool.Ifc.get_entity(obj)
if not element:
continue
if element.is_a("IfcSpace"):
spaces.add(element)
elif element.is_a("IfcRelSpaceBoundary"):
spaces.add(element.RelatingSpace)
for element in spaces:
for boundary in element.BoundedBy or []:
boundary_obj = tool.Ifc.get_object(boundary)
if boundary_obj:
to_delete.add(boundary_obj)
for boundary_obj in to_delete:
tool.Ifc.unlink(obj=boundary_obj)
bpy.data.objects.remove(boundary_obj)
context.scene.BIMBoundaryProperties.boundaries.clear()
return {"FINISHED"}
class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.add_boundary"
bl_label = "Add Boundary"
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
relating_space = None
related_building_element = None
relating_space_obj = None
related_building_element_obj = None
objs = context.selected_objects
if len(objs) == 2:
# The user may select two objects, a space and its related building element
for obj in objs:
element = tool.Ifc.get_entity(obj)
if not element:
continue
if element.is_a("IfcSpace"):
relating_space = element
relating_space_obj = obj
else:
related_building_element = element
related_building_element_obj = obj
elif len(objs) == 1:
# Optionally the user may select just the space, and the building element shall be auto-detected
def msg(self, context):
self.layout.label(text="NO ACTIVE STOREY")
element = tool.Ifc.get_entity(objs[0])
if element.is_a("IfcSpace"):
relating_space = element
relating_space_obj = objs[0]
target = bpy.context.scene.cursor.location
collection = context.view_layer.active_layer_collection.collection
collection_obj = bpy.data.objects.get(collection.name)
if not collection_obj:
bpy.context.window_manager.popup_menu(msg, title="Error", icon="ERROR")
return
spatial_element = tool.Ifc.get_entity(collection_obj)
if not spatial_element:
bpy.context.window_manager.popup_menu(msg, title="Error", icon="ERROR")
return
for subelement in ifcopenshell.util.element.get_decomposition(spatial_element):
if not (subelement.is_a("IfcWall") or subelement.is_a("IfcSlab")):
continue
obj = tool.Ifc.get_object(subelement)
if obj:
raycast = obj.closest_point_on_mesh(obj.matrix_world.inverted() @ target, distance=0.1)
if raycast[0]:
related_building_element = subelement
related_building_element_obj = obj
break
if not relating_space or not related_building_element:
return
bm = bmesh.new()
bm.from_mesh(relating_space_obj.data)
bmesh.ops.dissolve_limit(bm, angle_limit=pi * 2 / 360, verts=bm.verts, edges=bm.edges)
target_distance = inf
target_face = None
for face in bm.faces:
centroid = relating_space_obj.matrix_world @ face.calc_center_median()
raycast = related_building_element_obj.closest_point_on_mesh(related_building_element_obj.matrix_world.inverted() @ centroid, distance=1)
if raycast[0]:
distance = (related_building_element_obj.matrix_world @ raycast[1] - centroid).length
if distance < target_distance:
target_face = face
target_distance = distance
if not target_face:
return
bm2 = bmesh.new()
bm2.faces.new([bm2.verts.new(v.co.copy()) for v in target_face.verts])
mesh = bpy.data.meshes.new("Boundary")
bm2.to_mesh(mesh)
bm2.free()
bm.free()
obj = bpy.data.objects.new("Boundary", mesh)
obj.matrix_world = relating_space_obj.matrix_world.copy()
bpy.context.scene.collection.objects.link(obj)
surface = tool.Model.export_surface(obj)
connection_geometry = tool.Ifc.get().createIfcConnectionSurfaceGeometry(surface)
boundary = tool.Ifc.run("root.create_entity", ifc_class="IfcRelSpaceBoundary")
boundary.RelatingSpace = relating_space
boundary.RelatedBuildingElement = related_building_element
boundary.ConnectionGeometry = connection_geometry
bpy.data.objects.remove(obj)
bpy.ops.bim.show_boundaries()
obj = tool.Ifc.get_object(boundary)
obj.select_set(True)
@@ -18,6 +18,7 @@
import bpy
from bpy.types import PropertyGroup
from blenderbim.bim.prop import ObjProperty
from bpy.props import (
PointerProperty,
StringProperty,
@@ -52,9 +53,13 @@ def element_filter(self, object):
return False
class BIMBoundaryProperties(PropertyGroup):
class BIMObjectBoundaryProperties(PropertyGroup):
is_editing: BoolProperty(name="Is Editing")
relating_space: PointerProperty(name="RelatingSpace", type=bpy.types.Object, poll=space_filter)
related_building_element: PointerProperty(name="RelatedBuildingElement", type=bpy.types.Object, poll=element_filter)
parent_boundary: PointerProperty(name="ParentBoundary", type=bpy.types.Object, poll=boundary_filter)
corresponding_boundary: PointerProperty(name="CorrespondingBoundary", type=bpy.types.Object, poll=boundary_filter)
class BIMBoundaryProperties(PropertyGroup):
boundaries: bpy.props.CollectionProperty(type=ObjProperty)
@@ -21,7 +21,7 @@ import blenderbim.bim.helper
from bpy.types import Panel, UIList
from blenderbim.bim.ifc import IfcStore
import blenderbim.tool as tool
from ifcopenshell.api.boundary.data import Data
from blenderbim.bim.module.boundary.data import SpaceBoundariesData
class BIM_PT_SceneBoundaries(Panel):
@@ -102,6 +102,11 @@ class BIM_PT_Boundary(Panel):
row.label(text=f"{entity.is_a()}/{entity.Name}")
op = row.operator("bim.select_global_id", text="", icon="OBJECT_DATA")
op.global_id = entity.GlobalId
if ifc_attribute in ("RelatingSpace", "RelatedBuildingElement"):
op = row.operator("bim.select_related_element_boundaries", text="", icon="RESTRICT_SELECT_OFF")
op.related_element = entity.id()
op = row.operator("bim.select_related_element_type_boundaries", text="", icon="ASSET_MANAGER")
op.related_element = entity.id()
else:
row.label(text="")
@@ -118,6 +123,7 @@ class BIM_PT_SpaceBoundaries(Panel):
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "object"
bl_order = 1
bl_parent_id = "BIM_PT_geometry_object"
@classmethod
@@ -129,28 +135,23 @@ class BIM_PT_SpaceBoundaries(Panel):
return False
if not IfcStore.get_element(props.ifc_definition_id):
return False
valid_classes = ("IfcSpace", "IfcExternalSpatialElement")
entity = IfcStore.get_file().by_id(props.ifc_definition_id)
for ifc_class in valid_classes:
if entity.is_a(ifc_class):
element = tool.Ifc.get_entity(context.active_object)
for ifc_class in ("IfcSpace", "IfcExternalSpatialElement"):
if element.is_a(ifc_class):
return True
return False
def draw(self, context):
if not SpaceBoundariesData.is_loaded:
SpaceBoundariesData.load()
self.props = context.active_object.BIMObjectProperties
self.ifc_file = tool.Ifc.get()
row = self.layout.row()
row.operator("bim.load_space_boundaries")
if not Data.is_loaded:
Data.load(self.ifc_file)
for boundary_id in Data.spaces.get(self.props.ifc_definition_id, []):
boundary_data = Data.boundaries[boundary_id]
building_element = self.ifc_file.by_id(boundary_data["RelatedBuildingElement"])
row.operator("bim.select_space_boundaries", text="", icon="RESTRICT_SELECT_OFF")
for boundary in SpaceBoundariesData.data["boundaries"]:
row = self.layout.row()
if building_element:
bld_el_description = f"{building_element.is_a()}/{building_element.Name}"
else:
bld_el_description = None
row.label(text=f"{boundary_id} > {bld_el_description}", icon="GHOST_ENABLED")
row.label(text=boundary["description"], icon="GHOST_ENABLED")
op = row.operator("bim.load_boundary", text="", icon="RESTRICT_SELECT_OFF")
op.boundary_id = boundary_id
op.boundary_id = boundary["id"]
@@ -18,8 +18,8 @@
import bpy
from blenderbim.bim.module.model.data import AuthoringData
from blenderbim.bim.module.model.root import ConstrTypeEntityNotFound
from bpy.types import PropertyGroup
from math import pi
class BIMCadProperties(PropertyGroup):
@@ -28,3 +28,7 @@ class BIMCadProperties(PropertyGroup):
distance: bpy.props.FloatProperty(name="Distance", default=0.1)
x: bpy.props.FloatProperty(name="X", default=0.2)
y: bpy.props.FloatProperty(name="Y", default=0.1)
gable_roof_edge_angle: bpy.props.FloatProperty(
name="Gable Roof Edge Angle", default=pi / 2, soft_min=0, soft_max=pi / 2, subtype="ANGLE"
)
gable_roof_separate_verts: bpy.props.BoolProperty(name="Separate Verts", default=True)
@@ -21,7 +21,7 @@ import bpy
import blenderbim.tool as tool
import blenderbim.bim.module.type.prop as type_prop
from bpy.types import WorkSpaceTool
from blenderbim.bim.module.model.data import AuthoringData
from blenderbim.bim.module.model.data import AuthoringData, RailingData, RoofData
class CadTool(WorkSpaceTool):
@@ -34,7 +34,7 @@ class CadTool(WorkSpaceTool):
bl_icon = os.path.join(os.path.dirname(__file__), "ops.authoring.cad")
bl_widget = None
# https://docs.blender.org/api/current/bpy.types.KeyMapItems.html
bl_keymap = (
bl_keymap = tool.Blender.get_default_selection_keypmap() + (
("bim.cad_hotkey", {"type": "C", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_C")]}),
("bim.cad_hotkey", {"type": "E", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_E")]}),
("bim.cad_hotkey", {"type": "F", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_F")]}),
@@ -46,20 +46,27 @@ class CadTool(WorkSpaceTool):
("bim.cad_hotkey", {"type": "X", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_X")]}),
)
def draw_settings(context, layout, tool):
def draw_settings(context, layout, workspace_tool):
obj = context.active_object
if obj and obj.data and hasattr(obj.data, "BIMMeshProperties") and obj.data.BIMMeshProperties.is_profile:
if not obj or not obj.data:
return
if hasattr(obj.data, "BIMMeshProperties") and obj.data.BIMMeshProperties.subshape_type == "PROFILE":
row = layout.row(align=True)
row.label(text="", icon="EVENT_SHIFT")
row.label(text="", icon="EVENT_Q")
if obj.BIMObjectProperties.ifc_definition_id:
row.operator("bim.edit_extrusion_profile", text="Save Profile")
row.operator("bim.align_view_to_profile", text="", icon="AXIS_FRONT")
row.operator("bim.disable_editing_extrusion_profile", text="", icon="CANCEL")
else:
row.operator("bim.edit_arbitrary_profile", text="Save Profile")
row.operator("bim.align_view_to_profile", text="", icon="AXIS_FRONT")
row.operator("bim.disable_editing_arbitrary_profile", text="", icon="CANCEL")
element = tool.Ifc.get_entity(obj)
if element:
if element.is_a("IfcProfileDef"):
row.operator("bim.edit_arbitrary_profile", text="Save Profile")
row.operator("bim.align_view_to_profile", text="", icon="AXIS_FRONT")
row.operator("bim.disable_editing_arbitrary_profile", text="", icon="CANCEL")
elif element.is_a("IfcRelSpaceBoundary"):
row.operator("bim.edit_boundary_geometry", text="Save Profile")
row.operator("bim.disable_editing_boundary_geometry", text="", icon="CANCEL")
else:
row.operator("bim.edit_extrusion_profile", text="Save Profile")
row.operator("bim.align_view_to_profile", text="", icon="AXIS_FRONT")
row.operator("bim.disable_editing_extrusion_profile", text="", icon="CANCEL")
row = layout.row(align=True)
row.label(text="", icon="EVENT_SHIFT")
@@ -100,7 +107,62 @@ class CadTool(WorkSpaceTool):
row.label(text="", icon="EVENT_SHIFT")
row.label(text="", icon="EVENT_X")
row.operator("bim.reset_vertex", text="Reset Vertex")
elif hasattr(obj.data, "BIMMeshProperties") and obj.data.BIMMeshProperties.subshape_type == "AXIS":
row = layout.row(align=True)
row.label(text="", icon="EVENT_SHIFT")
row.label(text="", icon="EVENT_Q")
row.operator("bim.edit_extrusion_axis", text="Save Axis")
row.operator("bim.disable_editing_extrusion_axis", text="", icon="CANCEL")
row = layout.row(align=True)
row.label(text="", icon="EVENT_SHIFT")
row.label(text="", icon="EVENT_E")
row.operator("bim.cad_hotkey", text="Extend").hotkey = "S_E"
row = layout.row(align=True)
row.label(text="", icon="EVENT_SHIFT")
row.label(text="", icon="EVENT_T")
row.operator("bim.cad_hotkey", text="Mitre").hotkey = "S_T"
row = layout.row(align=True)
row.label(text="", icon="EVENT_SHIFT")
row.label(text="", icon="EVENT_F")
row.operator("bim.cad_hotkey", text="Fillet").hotkey = "S_F"
row = layout.row(align=True)
row.label(text="", icon="EVENT_SHIFT")
row.label(text="", icon="EVENT_O")
row.operator("bim.cad_hotkey", text="Offset").hotkey = "S_O"
else:
if (
(RailingData.is_loaded or not RailingData.load())
and RailingData.data["parameters"]
and context.active_object.BIMRailingProperties.is_editing_path
):
row = layout.row(align=True)
row.label(text="", icon="EVENT_SHIFT")
row.label(text="", icon="EVENT_Q")
row.operator("bim.cad_hotkey", text="Apply Railing Path").hotkey = "S_Q"
row.operator("bim.cancel_editing_railing_path", icon="CANCEL", text="")
elif (
(RoofData.is_loaded or not RoofData.load())
and RoofData.data["parameters"]
and context.active_object.BIMRoofProperties.is_editing_path
):
row = layout.row(align=True)
row.label(text="", icon="EVENT_SHIFT")
row.label(text="", icon="EVENT_Q")
row.operator("bim.cad_hotkey", text="Apply Roof Path").hotkey = "S_Q"
row.operator("bim.cancel_editing_roof_path", icon="CANCEL", text="")
row = layout.row(align=True)
row.label(text="", icon="EVENT_SHIFT")
row.label(text="", icon="EVENT_R")
row.operator("bim.cad_hotkey", text="Set Gable Roof Angle").hotkey = "S_R"
row = layout.row(align=True)
row.label(text="", icon="EVENT_SHIFT")
row.label(text="", icon="EVENT_E")
@@ -148,20 +210,32 @@ class CadHotkey(bpy.types.Operator):
if self.is_profile():
row = self.layout.row()
row.prop(props, "radius")
elif self.hotkey == "S_F":
if not self.is_profile():
row = self.layout.row()
row.prop(props, "resolution")
row = self.layout.row()
row.prop(props, "radius")
elif self.hotkey == "S_O":
row = self.layout.row()
row.prop(props, "distance")
elif self.hotkey == "S_R":
row = self.layout.row()
row.prop(props, "x")
row = self.layout.row()
row.prop(props, "y")
if self.is_profile():
row = self.layout.row()
row.prop(props, "x")
row = self.layout.row()
row.prop(props, "y")
elif (
(RoofData.is_loaded or not RoofData.load())
and RoofData.data["parameters"]
and bpy.context.active_object.BIMRoofProperties.is_editing_path
):
self.layout.row().prop(props, "gable_roof_edge_angle")
self.layout.row().prop(props, "gable_roof_separate_verts")
elif self.hotkey == "S_V":
if not self.is_profile():
row = self.layout.row()
@@ -186,14 +260,42 @@ class CadHotkey(bpy.types.Operator):
bpy.ops.bim.cad_offset(distance=self.props.distance)
def hotkey_S_Q(self):
if tool.Ifc.get_entity(bpy.context.active_object):
bpy.ops.bim.edit_extrusion_profile()
else:
bpy.ops.bim.edit_arbitrary_profile()
element = tool.Ifc.get_entity(bpy.context.active_object)
if bpy.context.active_object.data.BIMMeshProperties.subshape_type == "PROFILE":
if element.is_a("IfcProfileDef"):
bpy.ops.bim.edit_arbitrary_profile()
elif element.is_a("IfcProfileDef"):
bpy.ops.bim.edit_boundary_geometry()
else:
bpy.ops.bim.edit_extrusion_profile()
elif bpy.context.active_object.data.BIMMeshProperties.subshape_type == "AXIS":
bpy.ops.bim.edit_extrusion_axis()
elif (
(RailingData.is_loaded or not RailingData.load())
and RailingData.data["parameters"]
and bpy.context.active_object.BIMRailingProperties.is_editing_path
):
bpy.ops.bim.finish_editing_railing_path()
elif (
(RoofData.is_loaded or not RoofData.load())
and RoofData.data["parameters"]
and bpy.context.active_object.BIMRoofProperties.is_editing_path
):
bpy.ops.bim.finish_editing_roof_path()
def hotkey_S_R(self):
if self.is_profile():
bpy.ops.bim.add_rectangle(x=self.props.x, y=self.props.y)
elif (
(RoofData.is_loaded or not RoofData.load())
and RoofData.data["parameters"]
and bpy.context.active_object.BIMRoofProperties.is_editing_path
):
bpy.ops.bim.set_gable_roof_edge_angle(
angle=self.props.gable_roof_edge_angle, separate_verts=self.props.gable_roof_separate_verts
)
def hotkey_S_T(self):
bpy.ops.bim.cad_mitre()
@@ -210,4 +312,9 @@ class CadHotkey(bpy.types.Operator):
def is_profile(self):
obj = bpy.context.active_object
return obj and obj.data and hasattr(obj.data, "BIMMeshProperties") and obj.data.BIMMeshProperties.is_profile
return (
obj
and obj.data
and hasattr(obj.data, "BIMMeshProperties")
and obj.data.BIMMeshProperties.subshape_type == "PROFILE"
)
@@ -22,6 +22,7 @@ import json
import bmesh
import logging
import numpy as np
import ifcopenshell
from mathutils import Matrix
from math import radians
from blenderbim.bim.ifc import IfcStore
@@ -30,8 +31,10 @@ from blenderbim.bim.ifc import IfcStore
class ExportClashSets(bpy.types.Operator):
bl_idname = "bim.export_clash_sets"
bl_label = "Export Clash Sets"
bl_description = "Export clash sets to a selected file"
filename_ext = ".json"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
filter_glob: bpy.props.StringProperty(default="*.json", options={"HIDDEN"})
def invoke(self, context, event):
self.filepath = bpy.path.ensure_ext(bpy.data.filepath, ".json")
@@ -62,8 +65,10 @@ class ImportClashSets(bpy.types.Operator):
bl_idname = "bim.import_clash_sets"
bl_label = "Import Clash Sets"
bl_options = {"REGISTER", "UNDO"}
bl_description = "Import clash sets from a selected file"
filename_ext = ".json"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
filter_glob: bpy.props.StringProperty(default="*.json", options={"HIDDEN"})
def invoke(self, context, event):
self.filepath = bpy.path.ensure_ext(bpy.data.filepath, ".json")
@@ -98,6 +103,7 @@ class AddClashSet(bpy.types.Operator):
bl_idname = "bim.add_clash_set"
bl_label = "Add Clash Set"
bl_options = {"REGISTER", "UNDO"}
bl_description = "Add a clash set"
def execute(self, context):
new = context.scene.BIMClashProperties.clash_sets.add()
@@ -110,6 +116,7 @@ class RemoveClashSet(bpy.types.Operator):
bl_idname = "bim.remove_clash_set"
bl_label = "Remove Clash Set"
bl_options = {"REGISTER", "UNDO"}
bl_description = "Remove the selected clash set"
index: bpy.props.IntProperty()
def execute(self, context):
@@ -121,6 +128,7 @@ class AddClashSource(bpy.types.Operator):
bl_idname = "bim.add_clash_source"
bl_label = "Add Clash Source"
bl_options = {"REGISTER", "UNDO"}
bl_description = "Add a clash source to this group"
group: bpy.props.StringProperty()
def execute(self, context):
@@ -133,6 +141,7 @@ class RemoveClashSource(bpy.types.Operator):
bl_idname = "bim.remove_clash_source"
bl_label = "Remove Clash Source"
bl_options = {"REGISTER", "UNDO"}
bl_description = "Remove this clash source"
index: bpy.props.IntProperty()
group: bpy.props.StringProperty()
@@ -146,6 +155,7 @@ class SelectClashSource(bpy.types.Operator):
bl_idname = "bim.select_clash_source"
bl_label = "Select Clash Source"
bl_options = {"REGISTER", "UNDO"}
bl_description = "Select an IFC file to add as a clash source"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
filter_glob: bpy.props.StringProperty(default="*.ifc", options={"HIDDEN"})
index: bpy.props.IntProperty()
@@ -194,11 +204,13 @@ class SelectSmartGroupedClashesPath(bpy.types.Operator):
class ExecuteIfcClash(bpy.types.Operator):
bl_idname = "bim.execute_ifc_clash"
bl_label = "Execute IFC Clash"
filename_ext = ".bcf"
bl_description = "Execute clash detection and save the information to a .bcf or .json file"
filter_glob: bpy.props.StringProperty(default="*.bcf;*.json", options={"HIDDEN"})
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def invoke(self, context, event):
if ".json" not in bpy.data.filepath:
_, extension = os.path.splitext(self.filepath)
if extension != ".json":
self.filepath = bpy.path.ensure_ext(bpy.data.filepath, ".bcf")
WindowManager = context.window_manager
WindowManager.fileselect_add(self)
@@ -207,9 +219,10 @@ class ExecuteIfcClash(bpy.types.Operator):
def execute(self, context):
from ifcclash import ifcclash
settings = ifcclash.ClashSettings()
if ".json" not in self.filepath:
_, extension = os.path.splitext(self.filepath)
if extension != ".json":
self.filepath = bpy.path.ensure_ext(self.filepath, ".bcf")
settings = ifcclash.ClashSettings()
settings.output = self.filepath
settings.logger = logging.getLogger("Clash")
settings.logger.setLevel(logging.DEBUG)
@@ -261,6 +274,7 @@ class SelectIfcClashResults(bpy.types.Operator):
bl_idname = "bim.select_ifc_clash_results"
bl_label = "Select IFC Clash Results"
bl_options = {"REGISTER", "UNDO"}
bl_description = "Select the clashing IFC geometry stored in a file"
filename_ext = ".json"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
@@ -271,6 +285,7 @@ class SelectIfcClashResults(bpy.types.Operator):
return {"RUNNING_MODAL"}
def execute(self, context):
self.file = IfcStore.get_file()
self.filepath = bpy.path.ensure_ext(self.filepath, ".json")
with open(self.filepath) as f:
clash_sets = json.load(f)
@@ -284,10 +299,26 @@ class SelectIfcClashResults(bpy.types.Operator):
return {"CANCELLED"}
for clash in clash_set["clashes"].values():
global_ids.extend([clash["a_global_id"], clash["b_global_id"]])
for obj in context.visible_objects:
if not obj.BIMObjectProperties.ifc_definition_id:
continue
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
ifc_file = ""
for scene in obj.users_scene:
if scene.BIMProperties.ifc_file:
ifc_file = scene.BIMProperties.ifc_file
if scene.library:
break
if ifc_file:
if ifc_file not in IfcStore.session_files:
IfcStore.session_files[ifc_file] = ifcopenshell.open(ifc_file)
element_file = IfcStore.session_files[ifc_file]
else:
element_file = self.file
element = element_file.by_id(obj.BIMObjectProperties.ifc_definition_id)
if element.GlobalId in global_ids:
obj.select_set(True)
return {"FINISHED"}
@@ -304,14 +335,14 @@ class SmartClashGroup(bpy.types.Operator):
return context.scene.BIMClashProperties.clash_results_path
def execute(self, context):
import ifcclash
from ifcclash import ifcclash
settings = ifcclash.IfcClashSettings()
settings = ifcclash.ClashSettings()
self.filepath = bpy.path.ensure_ext(context.scene.BIMClashProperties.clash_results_path, ".json")
settings.output = self.filepath
settings.logger = logging.getLogger("Clash")
settings.logger.setLevel(logging.DEBUG)
ifc_clasher = ifcclash.IfcClasher(settings)
ifc_clasher = ifcclash.Clasher(settings)
with open(self.filepath) as f:
clash_sets = json.load(f)
@@ -65,12 +65,13 @@ class ClassificationsData:
class ReferencesData:
@classmethod
def is_available_classification_added(cls):
def active_classification_library(cls):
if not IfcStore.classification_file or not IfcStore.classification_file.by_type("IfcClassification"):
return False
props = bpy.context.scene.BIMClassificationProperties
name = IfcStore.classification_file.by_id(int(props.available_classifications)).Name
return name in [e.Name for e in tool.Ifc.get().by_type("IfcClassification")]
if name in [e.Name for e in tool.Ifc.get().by_type("IfcClassification")]:
return name
class ClassificationReferencesData(ReferencesData):
@@ -81,7 +82,7 @@ class ClassificationReferencesData(ReferencesData):
def load(cls):
cls.is_loaded = True
cls.data["references"] = cls.references()
cls.data["is_available_classification_added"] = cls.is_available_classification_added()
cls.data["active_classification_library"] = cls.active_classification_library()
@classmethod
def references(cls):
@@ -103,7 +104,7 @@ class MaterialClassificationsData(ReferencesData):
def load(cls):
cls.is_loaded = True
cls.data["references"] = cls.references()
cls.data["is_available_classification_added"] = cls.is_available_classification_added()
cls.data["active_classification_library"] = cls.active_classification_library()
@classmethod
def references(cls):
@@ -125,7 +126,7 @@ class CostClassificationsData(ReferencesData):
def load(cls):
cls.is_loaded = True
cls.data["references"] = cls.references()
cls.data["is_available_classification_added"] = cls.is_available_classification_added()
cls.data["active_classification_library"] = cls.active_classification_library()
@classmethod
def references(cls):
@@ -100,12 +100,16 @@ class ReferenceUI:
self.draw_reference_ui(reference)
def draw_add_ui(self, context):
if not self.data.data["is_available_classification_added"]:
if not self.data.data["active_classification_library"]:
row = self.layout.row(align=True)
row.label(text="No Active Classification Library")
row.operator("bim.load_classification_library", text="", icon="IMPORT")
return
row = self.layout.row(align=True)
row.prop(self.sprops, "available_classifications", text="")
row.label(text=f"Active Classification Library: {self.data.data['active_classification_library']}")
#row.prop(self.sprops, "available_classifications", text="")
if not self.sprops.available_library_references:
op = row.operator("bim.change_classification_level", text="", icon="IMPORT")
op = row.operator("bim.change_classification_level", text="", icon="GREASEPENCIL")
op.parent_id = int(self.sprops.available_classifications)
return
if self.sprops.active_library_referenced_source:
@@ -0,0 +1,59 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2023 Dion Moult <dion@thinkmoult.com>
#
# This file is part of BlenderBIM Add-on.
#
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# BlenderBIM Add-on is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import bpy
import blenderbim.tool as tool
def refresh():
ConstraintsData.is_loaded = False
ObjectConstraintsData.is_loaded = False
class ConstraintsData:
data = {}
is_loaded = False
@classmethod
def load(cls):
cls.data = {"total_objectives": cls.total_objectives()}
cls.is_loaded = True
@classmethod
def total_objectives(cls):
return len(tool.Ifc.get().by_type("IfcObjective"))
class ObjectConstraintsData:
data = {}
is_loaded = False
@classmethod
def load(cls):
cls.data = {"constraints": cls.constraints()}
cls.is_loaded = True
@classmethod
def constraints(cls):
results = []
element = tool.Ifc.get_entity(bpy.context.active_object)
for rel in element.HasAssociations or []:
if rel.is_a("IfcRelAssociatesConstraint"):
constraint = rel.RelatingConstraint
results.append({"id": constraint.id(), "type": constraint.is_a(), "name": constraint.Name or "Unnamed"})
return results
@@ -21,8 +21,8 @@ import json
import ifcopenshell.api
import ifcopenshell.util.attribute
import blenderbim.bim.helper
import blenderbim.tool as tool
from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.constraint.data import Data
class LoadObjectives(bpy.types.Operator):
@@ -33,10 +33,10 @@ class LoadObjectives(bpy.types.Operator):
def execute(self, context):
props = context.scene.BIMConstraintProperties
props.constraints.clear()
for constraint_id, constraint in Data.objectives.items():
for constraint in tool.Ifc.get().by_type("IfcObjective"):
new = props.constraints.add()
new.name = constraint["Name"] or "Unnamed"
new.ifc_definition_id = constraint_id
new.name = constraint.Name or "Unnamed"
new.ifc_definition_id = constraint.id()
props.is_editing = "IfcObjective"
bpy.ops.bim.disable_editing_constraint()
return {"FINISHED"}
@@ -62,11 +62,7 @@ class EnableEditingConstraint(bpy.types.Operator):
def execute(self, context):
props = context.scene.BIMConstraintProperties
props.constraint_attributes.clear()
if props.is_editing == "IfcObjective":
data = Data.objectives[self.constraint]
blenderbim.bim.helper.import_attributes(props.is_editing, props.constraint_attributes, data)
blenderbim.bim.helper.import_attributes2(tool.Ifc.get().by_id(self.constraint), props.constraint_attributes)
props.active_constraint_id = self.constraint
return {"FINISHED"}
@@ -91,7 +87,6 @@ class AddObjective(bpy.types.Operator):
def _execute(self, context):
result = ifcopenshell.api.run("constraint.add_objective", IfcStore.get_file())
Data.load(IfcStore.get_file())
bpy.ops.bim.load_objectives()
bpy.ops.bim.enable_editing_constraint(constraint=result.id())
return {"FINISHED"}
@@ -121,7 +116,6 @@ class EditObjective(bpy.types.Operator):
self.file,
**{"objective": self.file.by_id(props.active_constraint_id), "attributes": attributes}
)
Data.load(IfcStore.get_file())
bpy.ops.bim.load_objectives()
return {"FINISHED"}
@@ -141,7 +135,6 @@ class RemoveConstraint(bpy.types.Operator):
ifcopenshell.api.run(
"constraint.remove_constraint", self.file, **{"constraint": self.file.by_id(self.constraint)}
)
Data.load(IfcStore.get_file())
if props.is_editing == "IfcObjective":
bpy.ops.bim.load_objectives()
return {"FINISHED"}
@@ -200,7 +193,6 @@ class AssignConstraint(bpy.types.Operator):
"constraint": self.file.by_id(self.constraint),
}
)
Data.load(self.file, obj_id)
return {"FINISHED"}
@@ -229,5 +221,4 @@ class UnassignConstraint(bpy.types.Operator):
"constraint": self.file.by_id(self.constraint),
}
)
Data.load(self.file, obj_id)
return {"FINISHED"}
@@ -19,7 +19,7 @@
from bpy.types import Panel, UIList
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.helper import draw_attributes
from ifcopenshell.api.constraint.data import Data
from blenderbim.bim.module.constraint.data import ConstraintsData, ObjectConstraintsData
class BIM_PT_constraints(Panel):
@@ -36,14 +36,14 @@ class BIM_PT_constraints(Panel):
return IfcStore.get_file()
def draw(self, context):
if not Data.is_loaded:
Data.load(IfcStore.get_file())
if not ConstraintsData.is_loaded:
ConstraintsData.load()
self.props = context.scene.BIMConstraintProperties
if not self.props.is_editing or self.props.is_editing == "IfcObjective":
row = self.layout.row(align=True)
row.label(text="{} Objectives Found".format(len(Data.objectives)), icon="LIGHT")
row.label(text=f"{ConstraintsData.data['total_objectives']} Objectives Found", icon="LIGHT")
if self.props.is_editing == "IfcObjective":
row.operator("bim.disable_constraint_editing_ui", text="", icon="CHECKMARK")
row.operator("bim.add_objective", text="", icon="ADD")
@@ -74,6 +74,7 @@ class BIM_PT_object_constraints(Panel):
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "object"
bl_order = 1
bl_parent_id = "BIM_PT_misc_object"
@classmethod
@@ -85,33 +86,29 @@ class BIM_PT_object_constraints(Panel):
return bool(context.active_object.BIMObjectProperties.ifc_definition_id)
def draw(self, context):
if not ObjectConstraintsData.is_loaded:
ObjectConstraintsData.load()
obj = context.active_object
self.oprops = obj.BIMObjectProperties
self.sprops = context.scene.BIMConstraintProperties
self.props = obj.BIMObjectConstraintProperties
self.file = IfcStore.get_file()
if not Data.is_loaded:
Data.load(IfcStore.get_file())
if self.oprops.ifc_definition_id not in Data.products:
Data.load(IfcStore.get_file(), self.oprops.ifc_definition_id)
self.draw_add_ui()
constraint_ids = Data.products[self.oprops.ifc_definition_id]
if not constraint_ids:
if not ObjectConstraintsData.data["constraints"]:
row = self.layout.row(align=True)
row.label(text="No Constraints", icon="LIGHT")
for constraint_id in constraint_ids:
try:
constraint = Data.objectives[constraint_id]
for constraint in ObjectConstraintsData.data["constraints"]:
if constraint["type"] == "IfcObjective":
icon = "LIGHT"
except:
pass # Metric not implemented
else:
continue # Metric not implemented
row = self.layout.row(align=True)
row.label(text=constraint.get("Name") or "Unnamed")
row.operator("bim.unassign_constraint", text="", icon="X").constraint = constraint_id
row.label(text=constraint["name"])
row.operator("bim.unassign_constraint", text="", icon="X").constraint = constraint["id"]
def draw_add_ui(self):
if self.props.is_adding:
@@ -22,7 +22,6 @@ import blenderbim.core.context as core
import blenderbim.bim.module.context.data
import blenderbim.bim.handler
from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.context.data import Data
class Operator:
@@ -30,6 +30,8 @@ classes = (
operator.EnableEditingCostSchedule,
operator.EnableEditingCostItems,
operator.EnableEditingCostItem,
operator.ExportCostSchedules,
operator.ExpandCostItems,
operator.EnableEditingCostItemQuantities,
operator.EnableEditingCostItemQuantity,
operator.EnableEditingCostItemValues,
@@ -65,6 +67,15 @@ classes = (
operator.ExpandCostItemRate,
operator.ContractCostItemRate,
operator.CalculateCostItemResourceValue,
operator.ClearCostItemAssignments,
operator.HighlightProductCostItem,
operator.LoadProductCostItems,
operator.ReorderCostItem,
operator.SelectUnassignedProducts,
operator.LoadCostItemElementQuantities,
operator.LoadCostItemTaskQuantities,
operator.LoadCostItemResourceQuantities,
operator.ChangeParentCostItem,
prop.CostItem,
prop.CostItemQuantity,
prop.CostItemType,
@@ -78,6 +89,8 @@ classes = (
ui.BIM_UL_cost_item_quantities,
ui.BIM_UL_cost_item_types,
ui.BIM_UL_cost_item_rates,
ui.BIM_UL_product_cost_items,
ui.BIM_PT_Costing_Tools,
)
@@ -0,0 +1,340 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2023 Dion Moult <dion@thinkmoult.com>
#
# This file is part of BlenderBIM Add-on.
#
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# BlenderBIM Add-on is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import bpy
import ifcopenshell
import ifcopenshell.util.cost
import ifcopenshell.util.element
import blenderbim.tool as tool
import blenderbim.bim.schema
from ifcopenshell.util.doc import get_entity_doc, get_predefined_type_doc
def refresh():
CostSchedulesData.is_loaded = False
CostItemRatesData.is_loaded = False
CostItemQuantitiesData.is_loaded = False
class CostSchedulesData:
data = {}
is_loaded = False
@classmethod
def load(cls):
cls.data = {
"predefined_types": cls.get_cost_schedule_types(),
"total_cost_schedules": cls.total_cost_schedules(),
"schedules": cls.schedules(),
"is_editing_rates": cls.is_editing_rates(),
"cost_items": cls.cost_items(),
"cost_quantities": cls.cost_quantities(),
"cost_values": cls.cost_values(),
"quantity_types": cls.quantity_types(),
}
cls.is_loaded = True
@classmethod
def total_cost_schedules(cls):
return len(tool.Ifc.get().by_type("IfcCostSchedule"))
@classmethod
def schedules(cls):
results = []
if bpy.context.scene.BIMCostProperties.active_cost_schedule_id:
schedule = tool.Ifc.get().by_id(bpy.context.scene.BIMCostProperties.active_cost_schedule_id)
results.append(
{
"id": schedule.id(),
"name": schedule.Name or "Unnamed",
}
)
else:
for schedule in tool.Ifc.get().by_type("IfcCostSchedule"):
results.append(
{
"id": schedule.id(),
"name": schedule.Name or "Unnamed",
}
)
return results
@classmethod
def is_editing_rates(cls):
ifc_id = bpy.context.scene.BIMCostProperties.active_cost_schedule_id
if not ifc_id:
return
return tool.Ifc.get().by_id(ifc_id).PredefinedType == "SCHEDULEOFRATES"
@classmethod
def cost_items(cls):
cls._cost_values = {}
results = {}
for cost_item in tool.Ifc.get().by_type("IfcCostItem"):
data = {}
cls._load_cost_item_quantities(cost_item, data)
cls._load_cost_values(cost_item, data)
cls._load_nesting_index(cost_item, data)
results[cost_item.id()] = data
return results
@classmethod
def _load_nesting_index(cls, cost_item, data):
data["NestingIndex"] = None
for rel in cost_item.Nests or []:
data["NestingIndex"] = rel.RelatedObjects.index(cost_item)
@classmethod
def _load_cost_values(cls, root_element, data):
# data["CostValues"] = []
data["CategoryValues"] = {}
data["UnitBasisValueComponent"] = None
data["UnitBasisUnitSymbol"] = None
data["TotalAppliedValue"] = 0.0
data["TotalCost"] = 0.0
if root_element.is_a("IfcCostItem"):
values = root_element.CostValues
elif root_element.is_a("IfcConstructionResource"):
values = root_element.BaseCosts
for cost_value in values or []:
cls._load_cost_value(root_element, data, cost_value)
# data["CostValues"].append(cost_value.id())
data["TotalAppliedValue"] += cls._cost_values[cost_value.id()]["AppliedValue"]
if cost_value.UnitBasis:
cost_value_data = cls._cost_values[cost_value.id()]
data["UnitBasisValueComponent"] = cost_value_data["UnitBasis"]["ValueComponent"]
data["UnitBasisUnitSymbol"] = cost_value_data["UnitBasis"]["UnitSymbol"]
if data["UnitBasisValueComponent"]:
data["TotalCost"] = data["TotalCostQuantity"] / data["UnitBasisValueComponent"] * data["TotalAppliedValue"]
else:
data["TotalCost"] = data["TotalCostQuantity"] * data["TotalAppliedValue"]
@classmethod
def _load_cost_item_quantities(cls, cost_item, data):
# parametric_quantities = []
# for rel in cost_item.Controls:
# for related_object in rel.RelatedObjects or []:
# quantities = cls._get_object_quantities(cost_item, related_object)
# parametric_quantities.extend(quantities)
data["TotalCostQuantity"] = ifcopenshell.util.cost.get_total_quantity(cost_item)
data["UnitSymbol"] = "-"
if cost_item.CostQuantities:
quantity = cost_item.CostQuantities[0]
unit = ifcopenshell.util.unit.get_property_unit(quantity, tool.Ifc.get())
if unit:
data["UnitSymbol"] = ifcopenshell.util.unit.get_unit_symbol(unit)
else:
data["UnitSymbol"] = None
# same_unit_nested_cost_item = set()
# data["DerivedTotalCostQuantity"] = None
# if cost_item.IsNestedBy:
# data["TotalCostQuantity"] = 0.0
# derived_quantity = 0
# derived_unit = None
# should_sum = False
# for rel in cost_item.IsNestedBy:
# for related_object in rel.RelatedObjects or []:
# for quantity in related_object.CostQuantities or []:
# nested_unit = ifcopenshell.util.unit.get_property_unit(quantity, tool.Ifc.get())
# if derived_unit is None:
# derived_unit = nested_unit
# elif nested_unit and nested_unit.id() == derived_unit.id():
# same_unit_nested_cost_item.add(related_object)
# else:
# derived_unit = None
# break
# for cost in same_unit_nested_cost_item:
# qto = ifcopenshell.util.cost.get_total_quantity(cost)
# derived_quantity += qto
# print(derived_quantity)
# if derived_quantity not in [0, None]:
# data["DerivedTotalCostQuantity"] = derived_quantity
# if derived_unit:
# data["DerivedUnitSymbol"] = ifcopenshell.util.unit.get_unit_symbol(derived_unit)
# else:
# data["DerivedUnitSymbol"] = "?"
# print("Total Cost", data["DerivedTotalCostQuantity"], cost_item.Name)
@classmethod
def _get_object_quantities(cls, cost_item, element):
if not element.is_a("IfcObject"):
return []
results = []
for relationship in element.IsDefinedBy:
if not relationship.is_a("IfcRelDefinesByProperties"):
continue
qto = relationship.RelatingPropertyDefinition
if not qto.is_a("IfcElementQuantity"):
continue
for prop in qto.Quantities:
if prop in cost_item.CostQuantities or []:
results.append(prop.id())
return results
@classmethod
def _load_cost_value(cls, root_element, root_element_data, cost_value):
value_data = cost_value.get_info()
del value_data["AppliedValue"]
if value_data["UnitBasis"]:
data = cost_value.UnitBasis.get_info()
data["ValueComponent"] = data["ValueComponent"].wrappedValue
data["UnitComponent"] = data["UnitComponent"].id()
data["UnitSymbol"] = ifcopenshell.util.unit.get_unit_symbol(cost_value.UnitBasis.UnitComponent)
value_data["UnitBasis"] = data
if value_data["ApplicableDate"]:
value_data["ApplicableDate"] = ifcopenshell.util.date.ifc2datetime(value_data["ApplicableDate"])
if value_data["FixedUntilDate"]:
value_data["FixedUntilDate"] = ifcopenshell.util.date.ifc2datetime(value_data["FixedUntilDate"])
value_data["Components"] = [c.id() for c in value_data["Components"] or []]
value_data["AppliedValue"] = ifcopenshell.util.cost.calculate_applied_value(root_element, cost_value)
if cost_value.Category not in [None, "*"]:
root_element_data["CategoryValues"].setdefault(cost_value.Category, 0)
root_element_data["CategoryValues"][cost_value.Category] += value_data["AppliedValue"]
value_data["Formula"] = ifcopenshell.util.cost.serialise_cost_value(cost_value)
cls._cost_values[cost_value.id()] = value_data
for component in cost_value.Components or []:
cls._load_cost_value(root_element, root_element_data, component)
@classmethod
def cost_quantities(cls):
results = []
ifc_id = bpy.context.scene.BIMCostProperties.active_cost_item_id
if not ifc_id:
return results
for quantity in tool.Ifc.get().by_id(ifc_id).CostQuantities or []:
results.append({"id": quantity.id(), "name": quantity.Name, "value": "{0:.2f}".format(quantity[3])})
return results
@classmethod
def cost_values(cls):
results = []
ifc_id = bpy.context.scene.BIMCostProperties.active_cost_item_id
if not ifc_id:
return results
cost_item = tool.Ifc.get().by_id(ifc_id)
for cost_value in cost_item.CostValues or []:
label = "{0:.2f}".format(ifcopenshell.util.cost.calculate_applied_value(cost_item, cost_value))
label += " = {}".format(ifcopenshell.util.cost.serialise_cost_value(cost_value))
results.append({"id": cost_value.id(), "label": label})
return results
@classmethod
def quantity_types(cls):
return [
(t.name(), t.name(), "")
for t in tool.Ifc.schema().declaration_by_name("IfcPhysicalSimpleQuantity").subtypes()
]
@classmethod
def get_cost_schedule_types(cls):
results = []
declaration = tool.Ifc().schema().declaration_by_name("IfcCostSchedule")
version = tool.Ifc.get_schema()
for attribute in declaration.attributes():
if attribute.name() == "PredefinedType":
results.extend(
[
(e, e, get_predefined_type_doc(version, "IfcCostSchedule", e))
for e in attribute.type_of_attribute().declared_type().enumeration_items()
]
)
break
return results
class CostItemRatesData:
data = {}
is_loaded = False
@classmethod
def load(cls):
cls.data = {
"schedule_of_rates": cls.schedule_of_rates(),
}
cls.is_loaded = True
@classmethod
def schedule_of_rates(cls):
return [
(str(s.id()), s.Name or "Unnamed", "")
for s in tool.Ifc.get().by_type("IfcCostSchedule")
if s.PredefinedType == "SCHEDULEOFRATES"
]
class CostItemQuantitiesData:
data = {}
is_loaded = False
@classmethod
def load(cls):
cls.data = {
"product_quantity_names": cls.product_quantity_names(),
"process_quantity_names": cls.process_quantity_names(),
"resource_quantity_names": cls.resource_quantity_names(),
}
cls.is_loaded = True
@classmethod
def product_quantity_names(cls):
total_selected_objects = len(bpy.context.selected_objects)
names = set()
for obj in bpy.context.selected_objects:
element = tool.Ifc.get_entity(obj)
if not element:
continue
potential_names = set()
qtos = ifcopenshell.util.element.get_psets(element, qtos_only=True)
for qset, quantities in qtos.items():
potential_names.update(quantities.keys())
names = names.intersection(potential_names) if names else potential_names
return [(n, n, "") for n in names if n != "id"]
@classmethod
def process_quantity_names(cls):
active_task_index = bpy.context.scene.BIMWorkScheduleProperties.active_task_index
total_tasks = len(bpy.context.scene.BIMTaskTreeProperties.tasks)
if not total_tasks or active_task_index >= total_tasks:
return []
ifc_definition_id = bpy.context.scene.BIMTaskTreeProperties.tasks[active_task_index].ifc_definition_id
element = tool.Ifc.get().by_id(ifc_definition_id)
names = set()
qtos = ifcopenshell.util.element.get_psets(element, qtos_only=True)
for qset, quantities in qtos.items():
names = set(quantities.keys())
return [(n, n, "") for n in names if n != "id"]
@classmethod
def resource_quantity_names(cls):
active_resource_index = bpy.context.scene.BIMResourceProperties.active_resource_index
total_resources = len(bpy.context.scene.BIMResourceTreeProperties.resources)
if not total_resources or active_resource_index >= total_resources:
return []
ifc_definition_id = bpy.context.scene.BIMResourceTreeProperties.resources[
active_resource_index
].ifc_definition_id
element = tool.Ifc.get().by_id(ifc_definition_id)
names = set()
qtos = ifcopenshell.util.element.get_psets(element, qtos_only=True)
for qset, quantities in qtos.items():
names = set(quantities.keys())
return [(n, n, "") for n in names if n != "id"]
File diff suppressed because it is too large Load Diff
+58 -113
View File
@@ -18,10 +18,10 @@
import bpy
import ifcopenshell.api
import blenderbim.tool as tool
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.module.classification.data import CostClassificationsData
from ifcopenshell.api.cost.data import Data
from ifcopenshell.api.pset.data import Data as PsetData
from blenderbim.bim.module.cost.data import CostSchedulesData, CostItemRatesData, CostItemQuantitiesData
from blenderbim.bim.prop import StrProperty, Attribute
from bpy.types import PropertyGroup
from bpy.props import (
@@ -36,135 +36,46 @@ from bpy.props import (
)
quantitytypes_enum = []
productquantitynames_enum = []
productquantitynames_count = []
processquantitynames_enum = []
processquantitynames_id = 0
resourcequantitynames_enum = []
resourcequantitynames_id = 0
scheduleofrates_enum = []
def purge():
global quantitytypes_enum
global productquantitynames_enum
global productquantitynames_count
global processquantitynames_enum
global processquantitynames_id
global resourcequantitynames_enum
global resourcequantitynames_id
global scheduleofrates_enum
quantitytypes_enum = []
productquantitynames_enum = []
productquantitynames_count = []
processquantitynames_enum = []
processquantitynames_id = 0
resourcequantitynames_enum = []
resourcequantitynames_id = 0
scheduleofrates_enum = []
def get_schedule_of_rates(self, context):
global scheduleofrates_enum
if len(scheduleofrates_enum) == 0:
scheduleofrates_enum.extend(
(str(ifc_definition_id), schedule["Name"] or "Unnamed", "")
for ifc_definition_id, schedule in Data.cost_schedules.items()
if schedule["PredefinedType"] == "SCHEDULEOFRATES"
)
return scheduleofrates_enum
if not CostItemRatesData.is_loaded:
CostItemRatesData.load()
return CostItemRatesData.data["schedule_of_rates"]
def update_schedule_of_rates(self, context):
bpy.ops.bim.load_schedule_of_rates(cost_schedule=int(self.schedule_of_rates))
tool.Cost.load_schedule_of_rates_tree(schedule_of_rates=tool.Ifc.get().by_id(int(self.schedule_of_rates)))
def get_quantity_types(self, context):
global quantitytypes_enum
if len(quantitytypes_enum) == 0 and IfcStore.get_schema():
quantitytypes_enum.extend(
[
(t.name(), t.name(), "")
for t in IfcStore.get_schema().declaration_by_name("IfcPhysicalSimpleQuantity").subtypes()
]
)
return quantitytypes_enum
if not CostSchedulesData.is_loaded:
CostSchedulesData.load()
return CostSchedulesData.data["quantity_types"]
def get_product_quantity_names(self, context):
global productquantitynames_enum
global productquantitynames_count
ifc_file = IfcStore.get_file()
total_selected_objects = len(context.selected_objects)
if total_selected_objects != productquantitynames_count or total_selected_objects == 1:
productquantitynames_enum = []
productquantitynames_count = total_selected_objects
names = set()
for obj in context.selected_objects:
element_id = obj.BIMObjectProperties.ifc_definition_id
if not element_id:
continue
potential_names = set()
if element_id not in PsetData.products:
PsetData.load(ifc_file, element_id)
for qto_id in PsetData.products[element_id]["qtos"]:
qto = PsetData.qtos[qto_id]
[potential_names.add(PsetData.properties[p]["Name"]) for p in qto["Properties"]]
names = names.intersection(potential_names) if names else potential_names
productquantitynames_enum.extend([(n, n, "") for n in names])
return productquantitynames_enum
if not CostItemQuantitiesData.is_loaded:
CostItemQuantitiesData.load()
return CostItemQuantitiesData.data["product_quantity_names"]
def get_process_quantity_names(self, context):
global processquantitynames_enum
global processquantitynames_id
ifc_file = IfcStore.get_file()
active_task_index = context.scene.BIMWorkScheduleProperties.active_task_index
total_tasks = len(context.scene.BIMTaskTreeProperties.tasks)
if not total_tasks or active_task_index >= total_tasks:
return []
ifc_definition_id = context.scene.BIMTaskTreeProperties.tasks[active_task_index].ifc_definition_id
if processquantitynames_id != ifc_definition_id:
processquantitynames_enum = []
processquantitynames_id = ifc_definition_id
names = set()
if ifc_definition_id not in PsetData.products:
PsetData.load(ifc_file, ifc_definition_id)
for qto_id in PsetData.products[ifc_definition_id]["qtos"]:
qto = PsetData.qtos[qto_id]
[names.add(PsetData.properties[p]["Name"]) for p in qto["Properties"]]
processquantitynames_enum.extend([(n, n, "") for n in names])
return processquantitynames_enum
if not CostItemQuantitiesData.is_loaded:
CostItemQuantitiesData.load()
return CostItemQuantitiesData.data["process_quantity_names"]
def get_resource_quantity_names(self, context):
global resourcequantitynames_enum
global resourcequantitynames_id
ifc_file = IfcStore.get_file()
active_resource_index = context.scene.BIMResourceProperties.active_resource_index
total_resources = len(context.scene.BIMResourceTreeProperties.resources)
if not total_resources or active_resource_index >= total_resources:
return []
ifc_definition_id = context.scene.BIMResourceTreeProperties.resources[active_resource_index].ifc_definition_id
if resourcequantitynames_id != ifc_definition_id:
resourcequantitynames_enum = []
resourcequantitynames_id = ifc_definition_id
names = set()
if ifc_definition_id not in PsetData.products:
PsetData.load(ifc_file, ifc_definition_id)
for qto_id in PsetData.products[ifc_definition_id]["qtos"]:
qto = PsetData.qtos[qto_id]
[names.add(PsetData.properties[p]["Name"]) for p in qto["Properties"]]
resourcequantitynames_enum.extend([(n, n, "") for n in names])
return resourcequantitynames_enum
if not CostItemQuantitiesData.is_loaded:
CostItemQuantitiesData.load()
return CostItemQuantitiesData.data["resource_quantity_names"]
def update_active_cost_item_index(self, context):
if Data.cost_schedules[self.active_cost_schedule_id]["PredefinedType"] == "SCHEDULEOFRATES":
bpy.ops.bim.load_cost_item_types()
schedule = tool.Ifc.get().by_id(self.active_cost_schedule_id)
if schedule.PredefinedType == "SCHEDULEOFRATES":
tool.Cost.load_cost_item_types()
else:
bpy.ops.bim.load_cost_item_quantities()
tool.Cost.load_cost_item_quantities()
CostClassificationsData.load()
@@ -178,7 +89,6 @@ def update_cost_item_identification(self, context):
self.file,
**{"cost_item": self.file.by_id(self.ifc_definition_id), "attributes": {"Identification": self.identification}},
)
Data.load(self.file)
if props.active_cost_item_id == self.ifc_definition_id:
attribute = props.cost_item_attributes.get("Identification")
attribute.string_value = self.identification
@@ -194,12 +104,17 @@ def update_cost_item_name(self, context):
self.file,
**{"cost_item": self.file.by_id(self.ifc_definition_id), "attributes": {"Name": self.name}},
)
Data.load(IfcStore.get_file())
if props.active_cost_item_id == self.ifc_definition_id:
attribute = props.cost_item_attributes.get("Name")
attribute.string_value = self.name
def get_schedule_predefined_types(self, context):
if not CostSchedulesData.is_loaded:
CostSchedulesData.load()
return CostSchedulesData.data["predefined_types"]
class CostItem(PropertyGroup):
name: StringProperty(name="Name", update=update_cost_item_name)
identification: StringProperty(name="Identification", update=update_cost_item_identification)
@@ -220,7 +135,27 @@ class CostItemType(PropertyGroup):
ifc_definition_id: IntProperty(name="IFC Definition ID")
def update_cost_item_parent(self, context):
cost_item = tool.Cost.get_highlighted_cost_item()
tool.Cost.toggle_cost_item_parent(cost_item=cost_item)
def update_active_cost_item_elements(self, context):
bpy.ops.bim.load_cost_item_element_quantities()
def update_active_cost_item_tasks(self, context):
bpy.ops.bim.load_cost_item_task_quantities()
def update_active_cost_item_resources(self, context):
bpy.ops.bim.load_cost_item_resource_quantities()
class BIMCostProperties(PropertyGroup):
cost_schedule_predefined_types: EnumProperty(
items=get_schedule_predefined_types, name="Predefined Type", default=None
)
is_cost_update_enabled: BoolProperty(name="Is Cost Update Enabled", default=True)
cost_schedule_attributes: CollectionProperty(name="Cost Schedule Attributes", type=Attribute)
is_editing: StringProperty(name="Is Editing")
@@ -246,6 +181,7 @@ class BIMCostProperties(PropertyGroup):
name="Cost Types",
)
cost_category: StringProperty(name="Cost Category")
fixed_cost_value: FloatProperty(name="Fixed Cost Value")
active_cost_value_id: IntProperty(name="Active Cost Item Value Id")
cost_value_editing_type: StringProperty(name="Cost Value Editing Type")
cost_value_attributes: CollectionProperty(name="Cost Value Attributes", type=Attribute)
@@ -268,3 +204,12 @@ class BIMCostProperties(PropertyGroup):
cost_item_rates: CollectionProperty(name="Cost Item Rates", type=CostItem)
active_cost_item_rate_index: IntProperty(name="Active Cost Rate Index")
contracted_cost_item_rates: StringProperty(name="Contracted Cost Item Rates", default="[]")
product_cost_items: CollectionProperty(name="Product Cost Items", type=CostItem)
active_product_cost_item_index: IntProperty(name="Active Product Cost Item Index")
enable_reorder: BoolProperty(name="Enable Reorder", default=False)
show_nested_elements: BoolProperty(name="Show Nested Tasks", default=False, update=update_active_cost_item_elements)
show_nested_tasks: BoolProperty(name="Show Nested Tasks", default=False, update=update_active_cost_item_tasks)
show_nested_resources: BoolProperty(
name="Show Nested Tasks", default=False, update=update_active_cost_item_resources
)
change_cost_item_parent: BoolProperty(name="Change Cost Item Parent", default=False, update=update_cost_item_parent)
+164 -85
View File
@@ -20,8 +20,7 @@ import blenderbim.bim.helper
import blenderbim.bim.module.cost.prop as CostProp
from bpy.types import Panel, UIList
from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.cost.data import Data
from ifcopenshell.api.unit.data import Data as UnitData
from blenderbim.bim.module.cost.data import CostSchedulesData
class BIM_PT_cost_schedules(Panel):
@@ -39,54 +38,55 @@ class BIM_PT_cost_schedules(Panel):
return file and hasattr(file, "schema") and file.schema != "IFC2X3"
def draw(self, context):
if not CostSchedulesData.is_loaded:
CostSchedulesData.load()
self.props = context.scene.BIMCostProperties
if not Data.is_loaded:
Data.load(IfcStore.get_file())
if not UnitData.is_loaded:
UnitData.load(IfcStore.get_file())
row = self.layout.row()
if CostSchedulesData.data["total_cost_schedules"]:
row.label(text=f"{CostSchedulesData.data['total_cost_schedules']} Cost Schedules Found", icon="TEXT")
row.operator("bim.export_cost_schedules", text="Export as spreadsheet", icon="EXPORT")
else:
row.label(text="No Cost Schedules Found found.", icon="COMMUNITY")
row = self.layout.row()
row.operator("bim.add_cost_schedule", icon="ADD")
row.prop(self.props, "cost_schedule_predefined_types")
row.operator("bim.add_cost_schedule", icon="ADD", text="Add new")
if self.props.active_cost_schedule_id:
self.draw_cost_schedule_ui(
self.props.active_cost_schedule_id, Data.cost_schedules[self.props.active_cost_schedule_id]
)
else:
for cost_schedule_id, cost_schedule in Data.cost_schedules.items():
self.draw_cost_schedule_ui(cost_schedule_id, cost_schedule)
for schedule in CostSchedulesData.data["schedules"]:
self.draw_cost_schedule_ui(schedule)
def draw_cost_schedule_ui(self, cost_schedule_id, cost_schedule):
def draw_cost_schedule_ui(self, cost_schedule):
row = self.layout.row(align=True)
row.label(text=cost_schedule["Name"] or "Unnamed", icon="LINENUMBERS_ON")
row.label(text=cost_schedule["name"], icon="LINENUMBERS_ON")
if self.props.active_cost_schedule_id and self.props.active_cost_schedule_id == cost_schedule["id"]:
op = row.operator("bim.select_cost_schedule_products", icon="RESTRICT_SELECT_OFF", text="Assigned")
op.cost_schedule = cost_schedule["id"]
row.operator("bim.select_unassigned_products", icon="RESTRICT_SELECT_OFF", text="Unassigned")
if self.props.active_cost_schedule_id and self.props.active_cost_schedule_id == cost_schedule_id:
op = row.operator("bim.select_cost_schedule_products", icon="RESTRICT_SELECT_OFF", text="")
op.cost_schedule = cost_schedule_id
row.prop(self.props, "should_show_column_ui", text="", icon="SHORTDISPLAY")
if self.props.is_editing == "COST_SCHEDULE":
if self.props.is_editing == "COST_SCHEDULE_ATTRIBUTES":
row.operator("bim.edit_cost_schedule", text="", icon="CHECKMARK")
elif self.props.is_editing == "COST_ITEMS":
row.operator("bim.add_summary_cost_item", text="", icon="ADD").cost_schedule = cost_schedule_id
row.operator("bim.add_summary_cost_item", text="", icon="ADD").cost_schedule = cost_schedule["id"]
row.operator("bim.disable_editing_cost_schedule", text="", icon="CANCEL")
elif self.props.active_cost_schedule_id:
row.operator("bim.remove_cost_schedule", text="", icon="X").cost_schedule = cost_schedule_id
row.operator("bim.remove_cost_schedule", text="", icon="X").cost_schedule = cost_schedule["id"]
else:
row.operator("bim.enable_editing_cost_items", text="", icon="OUTLINER").cost_schedule = cost_schedule_id
row.operator("bim.enable_editing_cost_items", text="", icon="OUTLINER").cost_schedule = cost_schedule["id"]
row.operator(
"bim.enable_editing_cost_schedule", text="", icon="GREASEPENCIL"
).cost_schedule = cost_schedule_id
row.operator("bim.remove_cost_schedule", text="", icon="X").cost_schedule = cost_schedule_id
"bim.enable_editing_cost_schedule_attributes", text="", icon="GREASEPENCIL"
).cost_schedule = cost_schedule["id"]
row.operator("bim.remove_cost_schedule", text="", icon="X").cost_schedule = cost_schedule["id"]
if self.props.active_cost_schedule_id == cost_schedule_id:
if self.props.is_editing == "COST_SCHEDULE":
if self.props.active_cost_schedule_id == cost_schedule["id"]:
if self.props.is_editing == "COST_SCHEDULE_ATTRIBUTES":
self.draw_editable_cost_schedule_ui()
elif self.props.is_editing == "COST_ITEMS":
if self.props.should_show_column_ui:
self.draw_column_ui()
self.draw_editable_cost_item_ui(cost_schedule_id)
self.draw_editable_cost_item_ui()
def draw_column_ui(self):
row = self.layout.row(align=True)
@@ -97,15 +97,16 @@ class BIM_PT_cost_schedules(Panel):
def draw_editable_cost_schedule_ui(self):
blenderbim.bim.helper.draw_attributes(self.props.cost_schedule_attributes, self.layout)
def draw_editable_cost_item_ui(self, cost_schedule_id):
def draw_editable_cost_item_ui(self):
row = self.layout.row(align=True)
row.alignment = "RIGHT"
ifc_definition_id = None
if self.props.cost_items and self.props.active_cost_item_index < len(self.props.cost_items):
ifc_definition_id = self.props.cost_items[self.props.active_cost_item_index].ifc_definition_id
if ifc_definition_id:
if Data.cost_schedules[self.props.active_cost_schedule_id]["PredefinedType"] != "SCHEDULEOFRATES":
row.prop(self.props, "change_cost_item_parent", text="", icon="LINKED")
row.prop(self.props, "enable_reorder", text="", icon="SORTALPHA")
if not CostSchedulesData.data["is_editing_rates"]:
op = row.operator("bim.enable_editing_cost_item_quantities", text="", icon="PROPERTIES")
op.cost_item = ifc_definition_id
@@ -119,7 +120,7 @@ class BIM_PT_cost_schedules(Panel):
row.operator("bim.edit_cost_item", text="", icon="CHECKMARK")
row.operator("bim.disable_editing_cost_item", text="", icon="CANCEL")
else:
op = row.operator("bim.enable_editing_cost_item", text="", icon="GREASEPENCIL")
op = row.operator("bim.enable_editing_cost_item_attributes", text="", icon="GREASEPENCIL")
op.cost_item = ifc_definition_id
row.operator("bim.remove_cost_item", text="", icon="X").cost_item = ifc_definition_id
@@ -150,64 +151,55 @@ class BIM_PT_cost_schedules(Panel):
op.cost_item = self.props.active_cost_item_id
op.ifc_class = self.props.quantity_types
for quantity_id in Data.cost_items[self.props.active_cost_item_id]["CostQuantities"]:
quantity = Data.physical_quantities[quantity_id]
value = quantity[[k for k in quantity.keys() if "Value" in k][0]]
for quantity in CostSchedulesData.data["cost_quantities"]:
row = self.layout.row(align=True)
row.label(text=quantity["Name"])
row.label(text="{0:.2f}".format(value))
if self.props.active_cost_item_quantity_id and self.props.active_cost_item_quantity_id == quantity_id:
row.label(text=quantity["name"])
row.label(text=quantity["value"])
if self.props.active_cost_item_quantity_id and self.props.active_cost_item_quantity_id == quantity["id"]:
op = row.operator("bim.edit_cost_item_quantity", text="", icon="CHECKMARK")
op.physical_quantity = quantity_id
op.physical_quantity = quantity["id"]
row.operator("bim.disable_editing_cost_item_quantity", text="", icon="CANCEL")
elif self.props.active_cost_item_quantity_id:
op = row.operator("bim.remove_cost_item_quantity", text="", icon="X")
op.cost_item = self.props.active_cost_item_id
op.physical_quantity = quantity_id
op.physical_quantity = quantity["id"]
else:
op = row.operator("bim.enable_editing_cost_item_quantity", text="", icon="GREASEPENCIL")
op.physical_quantity = quantity_id
op.physical_quantity = quantity["id"]
op = row.operator("bim.remove_cost_item_quantity", text="", icon="X")
op.cost_item = self.props.active_cost_item_id
op.physical_quantity = quantity_id
op.physical_quantity = quantity["id"]
if self.props.active_cost_item_quantity_id and self.props.active_cost_item_quantity_id == quantity_id:
box = self.layout.box()
self.draw_editable_cost_item_quantity_ui(box)
def draw_editable_cost_item_quantity_ui(self, layout):
blenderbim.bim.helper.draw_attributes(self.props.quantity_attributes, self.layout)
if self.props.active_cost_item_quantity_id and self.props.active_cost_item_quantity_id == quantity["id"]:
blenderbim.bim.helper.draw_attributes(self.props.quantity_attributes, self.layout.box())
def draw_editable_cost_item_values_ui(self):
row = self.layout.row(align=True)
row.prop(self.props, "cost_types", text="")
if self.props.cost_types == "FIXED":
row.prop(self.props, "fixed_cost_value", text="")
if self.props.cost_types == "CATEGORY":
row.prop(self.props, "cost_category", text="")
op = row.operator("bim.add_cost_value", text="", icon="ADD")
op = row.operator("bim.add_cost_value", text="Add Value", icon="ADD")
op.parent = self.props.active_cost_item_id
op.cost_type = self.props.cost_types
if self.props.cost_types == "CATEGORY":
op.cost_category = self.props.cost_category
for cost_value_id in Data.cost_items[self.props.active_cost_item_id]["CostValues"]:
for cost_value in CostSchedulesData.data["cost_values"]:
row = self.layout.row(align=True)
self.draw_readonly_cost_value_ui(row, cost_value_id)
self.draw_readonly_cost_value_ui(row, cost_value)
if self.props.cost_value_editing_type == "ATTRIBUTES":
box = self.layout.box()
self.draw_editable_cost_value_ui(box, Data.cost_values[self.props.active_cost_value_id])
blenderbim.bim.helper.draw_attributes(self.props.cost_value_attributes, self.layout.box())
def draw_readonly_cost_value_ui(self, layout, cost_value_id):
cost_value = Data.cost_values[cost_value_id]
if self.props.active_cost_value_id == cost_value_id and self.props.cost_value_editing_type == "FORMULA":
def draw_readonly_cost_value_ui(self, layout, cost_value):
if self.props.active_cost_value_id == cost_value["id"] and self.props.cost_value_editing_type == "FORMULA":
layout.prop(self.props, "cost_value_formula", text="")
else:
cost_value_label = "{0:.2f}".format(cost_value["AppliedValue"])
cost_value_label += " = " + cost_value["Formula"]
layout.label(text=cost_value_label, icon="DISC")
layout.label(text=cost_value["label"], icon="DISC")
self.draw_cost_value_operator_ui(layout, cost_value_id, self.props.active_cost_item_id)
self.draw_cost_value_operator_ui(layout, cost_value["id"], self.props.active_cost_item_id)
def draw_cost_value_operator_ui(self, layout, cost_value_id, parent_id):
if self.props.active_cost_value_id and self.props.active_cost_value_id == cost_value_id:
@@ -231,9 +223,6 @@ class BIM_PT_cost_schedules(Panel):
op.parent = parent_id
op.cost_value = cost_value_id
def draw_editable_cost_value_ui(self, layout, cost_value):
blenderbim.bim.helper.draw_attributes(self.props.cost_value_attributes, layout)
class BIM_PT_cost_item_types(Panel):
bl_label = "IFC Cost Item Types"
@@ -250,7 +239,9 @@ class BIM_PT_cost_item_types(Panel):
total_cost_items = len(props.cost_items)
if not props.active_cost_schedule_id:
return False
if Data.cost_schedules[props.active_cost_schedule_id]["PredefinedType"] != "SCHEDULEOFRATES":
if not CostSchedulesData.is_loaded:
return False
if not CostSchedulesData.data["is_editing_rates"]:
return False
if total_cost_items > 0 and props.active_cost_item_index < total_cost_items:
return True
@@ -258,9 +249,7 @@ class BIM_PT_cost_item_types(Panel):
def draw(self, context):
self.props = context.scene.BIMCostProperties
cost_item = self.props.cost_items[self.props.active_cost_item_index]
grid = self.layout.grid_flow(columns=3, even_columns=True)
# Column1
@@ -336,18 +325,15 @@ class BIM_PT_cost_item_quantities(Panel):
total_cost_items = len(props.cost_items)
if not props.active_cost_schedule_id:
return False
if Data.cost_schedules[props.active_cost_schedule_id]["PredefinedType"] == "SCHEDULEOFRATES":
if not CostSchedulesData.is_loaded:
return False
if CostSchedulesData.data["is_editing_rates"]:
return False
if total_cost_items > 0 and props.active_cost_item_index < total_cost_items:
return True
return False
def draw(self, context):
if not Data.is_loaded:
Data.load(IfcStore.get_file())
if not UnitData.is_loaded:
UnitData.load(IfcStore.get_file())
self.props = context.scene.BIMCostProperties
cost_item = self.props.cost_items[self.props.active_cost_item_index]
@@ -360,7 +346,8 @@ class BIM_PT_cost_item_quantities(Panel):
has_quantity_names = CostProp.get_product_quantity_names(self, context)
row2 = col.row(align=True)
row2.label(text="Elements")
total_cost_item_products = len(self.props.cost_item_products)
row2.label(text="Elements ({})".format(total_cost_item_products))
op = row2.operator("bim.select_cost_item_products", icon="RESTRICT_SELECT_OFF", text="")
op.cost_item = cost_item.ifc_definition_id
@@ -380,6 +367,8 @@ class BIM_PT_cost_item_quantities(Panel):
op.cost_item = cost_item.ifc_definition_id
op.related_object = 0
row2 = col.row()
row2.prop(self.props, "show_nested_elements", text="Show nested")
row2 = col.row()
row2.template_list(
"BIM_UL_cost_item_quantities",
@@ -390,6 +379,11 @@ class BIM_PT_cost_item_quantities(Panel):
"active_cost_item_product_index",
)
row2 = col.row()
op = row2.operator("bim.clear_cost_item_assignments", text="Clear assignments", icon="X")
op.cost_item = cost_item.ifc_definition_id
op.related_object_type = "PRODUCT"
if has_quantity_names:
row2 = col.row()
row2.prop(self.props, "product_quantity_names", text="")
@@ -400,7 +394,8 @@ class BIM_PT_cost_item_quantities(Panel):
has_quantity_names = CostProp.get_process_quantity_names(self, context)
row2 = col.row(align=True)
row2.label(text="Tasks")
total_cost_item_processes = len(self.props.cost_item_processes)
row2.label(text="Tasks ({})".format(total_cost_item_processes))
tprops = context.scene.BIMTaskTreeProperties
wprops = context.scene.BIMWorkScheduleProperties
@@ -416,6 +411,9 @@ class BIM_PT_cost_item_quantities(Panel):
op.cost_item = cost_item.ifc_definition_id
op.prop_name = ""
row2 = col.row()
row2.prop(self.props, "show_nested_tasks", text="Show nested")
row2 = col.row()
row2.template_list(
"BIM_UL_cost_item_quantities",
@@ -426,6 +424,11 @@ class BIM_PT_cost_item_quantities(Panel):
"active_cost_item_process_index",
)
row2 = col.row()
op = row2.operator("bim.clear_cost_item_assignments", text="Clear assignments", icon="X")
op.cost_item = cost_item.ifc_definition_id
op.related_object_type = "PROCESS"
if has_quantity_names:
row2 = col.row()
row2.prop(self.props, "process_quantity_names", text="")
@@ -436,7 +439,8 @@ class BIM_PT_cost_item_quantities(Panel):
has_quantity_names = CostProp.get_resource_quantity_names(self, context)
row2 = col.row(align=True)
row2.label(text="Resources")
total_cost_item_resources = len(self.props.cost_item_resources)
row2.label(text="Resources ({})".format(total_cost_item_resources))
op = row2.operator("bim.calculate_cost_item_resource_value", text="", icon="DISC")
op.cost_item = cost_item.ifc_definition_id
@@ -455,6 +459,9 @@ class BIM_PT_cost_item_quantities(Panel):
op.cost_item = cost_item.ifc_definition_id
op.prop_name = ""
row2 = col.row()
row2.prop(self.props, "show_nested_resources", text="Show nested")
row2 = col.row()
row2.template_list(
"BIM_UL_cost_item_quantities",
@@ -465,6 +472,11 @@ class BIM_PT_cost_item_quantities(Panel):
"active_cost_item_resource_index",
)
row2 = col.row()
op = row2.operator("bim.clear_cost_item_assignments", text="Clear assignments", icon="X")
op.cost_item = cost_item.ifc_definition_id
op.related_object_type = "RESOURCE"
if has_quantity_names:
row2 = col.row()
row2.prop(self.props, "resource_quantity_names", text="")
@@ -485,7 +497,9 @@ class BIM_PT_cost_item_rates(Panel):
total_cost_items = len(props.cost_items)
if not props.active_cost_schedule_id:
return False
if Data.cost_schedules[props.active_cost_schedule_id]["PredefinedType"] == "SCHEDULEOFRATES":
if not CostSchedulesData.is_loaded:
return False
if CostSchedulesData.data["is_editing_rates"]:
return False
if total_cost_items > 0 and props.active_cost_item_index < total_cost_items:
return True
@@ -493,13 +507,14 @@ class BIM_PT_cost_item_rates(Panel):
def draw(self, context):
self.props = context.scene.BIMCostProperties
cost_item = self.props.cost_items[self.props.active_cost_item_index]
row = self.layout.row(align=True)
row.prop(self.props, "schedule_of_rates", text="")
if self.props.active_cost_item_rate_index < len(self.props.cost_item_rates):
cost_item = self.props.cost_items[self.props.active_cost_item_index]
cost_item_rate = self.props.cost_item_rates[self.props.active_cost_item_rate_index]
op = row.operator("bim.assign_cost_value", text="", icon="COPYDOWN")
op.cost_item = self.props.cost_items[self.props.active_cost_item_index].ifc_definition_id
op.cost_rate = self.props.cost_item_rates[self.props.active_cost_item_rate_index].ifc_definition_id
op.cost_item = cost_item.ifc_definition_id
op.cost_rate = cost_item_rate.ifc_definition_id
self.layout.template_list(
"BIM_UL_cost_item_rates",
"",
@@ -514,7 +529,7 @@ class BIM_UL_cost_items_trait:
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
if item:
self.props = context.scene.BIMCostProperties
cost_item = Data.cost_items[item.ifc_definition_id]
cost_item = CostSchedulesData.data["cost_items"][item.ifc_definition_id]
row = layout.row(align=True)
self.draw_hierarchy(row, item)
@@ -527,13 +542,30 @@ class BIM_UL_cost_items_trait:
self.draw_quantity_column(split2, cost_item)
self.draw_value_column(split2, cost_item)
if self.props.active_cost_item_id == item.ifc_definition_id:
row.operator("bim.disable_editing_cost_item", text="", icon="CANCEL")
for column in self.props.columns:
split2.label(text=str(cost_item["CategoryValues"].get(column.name, "-")))
self.draw_total_cost_column(split2, cost_item)
if self.props.enable_reorder:
self.draw_order_operator(row, item.ifc_definition_id, cost_item)
if self.props.change_cost_item_parent:
self.draw_parent_operator(row, item.ifc_definition_id)
# TODO: reimplement "bim.copy_cost_item_values" somewhere with better UX
def draw_parent_operator(self, row, cost_item_id):
if self.props.active_cost_item_id:
if self.props.active_cost_item_id != cost_item_id:
op = row.operator(
"bim.change_parent_cost_item", text="", icon="LINKED", emboss=False
).new_parent = cost_item_id
else:
row.label(text="", icon="BLANK1")
def draw_hierarchy(self, row, item):
for i in range(0, item.level_index):
row.label(text="", icon="BLANK1")
@@ -550,7 +582,7 @@ class BIM_UL_cost_items_trait:
layout.label(text="{0:.2f}".format(cost_item["TotalCost"]))
def draw_quantity_column(self, layout, cost_item):
if Data.cost_schedules[self.props.active_cost_schedule_id]["PredefinedType"] == "SCHEDULEOFRATES":
if CostSchedulesData.data["is_editing_rates"]:
self.draw_uom_column(layout, cost_item)
else:
self.draw_total_quantity_column(layout, cost_item)
@@ -564,6 +596,17 @@ class BIM_UL_cost_items_trait:
def draw_uom_column(self, layout, cost_item):
layout.label(text=cost_item["UnitBasisUnitSymbol"] or "?" if cost_item["UnitBasisValueComponent"] else "-")
def draw_order_operator(self, row, ifc_definition_id, cost_item):
if cost_item["NestingIndex"] is not None:
if cost_item["NestingIndex"] == 0:
op = row.operator("bim.reorder_cost_item_nesting", icon="TRIA_DOWN", text="")
op.cost_item = ifc_definition_id
op.new_index = cost_item["NestingIndex"] + 1
elif cost_item["NestingIndex"] > 0:
op = row.operator("bim.reorder_cost_item_nesting", icon="TRIA_UP", text="")
op.cost_item = ifc_definition_id
op.new_index = cost_item["NestingIndex"] - 1
def draw_total_quantity_column(self, layout, cost_item):
layout.label(text="{0:.2f}".format(cost_item["TotalCostQuantity"]) + f" {cost_item['UnitSymbol'] or '?'}")
@@ -618,9 +661,45 @@ class BIM_UL_cost_item_quantities(UIList):
if item:
row = layout.row(align=True)
op = row.operator("bim.select_product", text="", icon="RESTRICT_SELECT_OFF")
op.product = item.ifc_definition_id
row.split(factor=0.8)
row.label(text=item.name)
row.label(text="{0:.2f}".format(item.total_quantity))
op = row.operator("bim.unassign_cost_item_quantity", text="", icon="X")
op.cost_item = cost_item.ifc_definition_id
op.related_object = item.ifc_definition_id
class BIM_UL_product_cost_items(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
if item:
row = layout.row(align=True)
op = row.operator("bim.highlight_product_cost_item", text="", icon="STYLUS_PRESSURE")
op.cost_item = item.ifc_definition_id
row.split(factor=0.8)
row.label(text=item.name)
class BIM_PT_Costing_Tools(Panel):
bl_label = "5D Tools"
bl_idname = "BIM_PT_Costing_Tools"
bl_space_type = "VIEW_3D"
bl_region_type = "UI"
bl_category = "4D/5D Toolkit"
def draw(self, context):
self.props = context.scene.BIMCostProperties
row = self.layout.row()
row.operator(
"bim.load_product_cost_items", icon="FILE_REFRESH"
)
row = self.layout.row()
row.template_list(
"BIM_UL_product_cost_items",
"",
self.props,
"product_cost_items",
self.props,
"active_product_cost_item_index",
)
@@ -30,6 +30,7 @@ classes = (
operator.ProfileImportIFC,
operator.PurgeHdf5Cache,
operator.PurgeIfcLinks,
operator.ConvertToBlender,
operator.RewindInspector,
operator.SelectExpressFile,
operator.SelectHighPolygonMeshes,
@@ -65,6 +65,26 @@ class PurgeIfcLinks(bpy.types.Operator):
return {"FINISHED"}
class ConvertToBlender(bpy.types.Operator):
bl_idname = "bim.converttoblender"
bl_label = "Convert To Blender File"
bl_description = "Removes all IFC data, and converts the file to a simple Blender file."
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
for o in bpy.data.objects:
if o.type in {'MESH', 'EMPTY'}:
o.BIMObjectProperties.ifc_definition_id = 0
if o.data:
o.data.BIMMeshProperties.ifc_definition_id = 0
for m in bpy.data.materials:
m.BIMMaterialProperties.ifc_style_id = False
bpy.context.scene.BIMProperties.ifc_file = ""
IfcStore.purge()
blenderbim.bim.handler.purge_module_data()
return {"FINISHED"}
class ValidateIfcFile(bpy.types.Operator):
bl_idname = "bim.validate_ifc_file"
bl_label = "Validate IFC File"
@@ -54,6 +54,9 @@ class BIM_PT_debug(Panel):
row = layout.row()
row.operator("bim.purge_ifc_links")
row = layout.row()
row.operator("bim.converttoblender")
row = layout.row()
row.operator("bim.create_all_shapes")
@@ -23,7 +23,6 @@ import ifcopenshell.util.attribute
import blenderbim.tool as tool
import blenderbim.core.document as core
from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.document.data import Data
class LoadProjectDocuments(bpy.types.Operator, tool.Ifc.Operator):
@@ -129,7 +128,7 @@ class AssignDocument(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context):
document = tool.Ifc.get().by_id(self.document)
objs = [bpy.data.objects.get(self.obj)] if self.obj else context.selected_objects
objs = [bpy.data.objects.get(self.obj)] if self.obj else tool.Blender.get_selected_objects()
for obj in objs:
element = tool.Ifc.get_entity(obj)
if element:
@@ -145,7 +144,7 @@ class UnassignDocument(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context):
document = tool.Ifc.get().by_id(self.document)
objs = [bpy.data.objects.get(self.obj)] if self.obj else context.selected_objects
objs = [bpy.data.objects.get(self.obj)] if self.obj else tool.Blender.get_selected_objects()
for obj in objs:
element = tool.Ifc.get_entity(obj)
if element:
@@ -82,6 +82,7 @@ class BIM_PT_object_documents(Panel):
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "object"
bl_order = 1
bl_parent_id = "BIM_PT_misc_object"
@classmethod
@@ -17,11 +17,12 @@
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import bpy
from . import ui, prop, operator, handler, gizmos
from . import ui, prop, operator, handler, gizmos, workspace
classes = (
operator.ActivateDrawing,
operator.ActivateDrawingStyle,
operator.ActivateView,
operator.ActivateModel,
operator.AddAnnotation,
operator.AddDrawing,
operator.AddDrawingStyle,
@@ -29,12 +30,11 @@ classes = (
operator.AddDrawingToSheet,
operator.AddSchedule,
operator.AddScheduleToSheet,
operator.AddSectionsAnnotations,
operator.AddSheet,
operator.AddTextLiteral,
operator.BuildSchedule,
operator.CleanWireframes,
operator.ContractSheet,
operator.CopyGrid,
operator.CreateDrawing,
operator.CreateSheets,
operator.DisableEditingAssignedProduct,
@@ -42,28 +42,32 @@ classes = (
operator.DisableEditingSchedules,
operator.DisableEditingSheets,
operator.DisableEditingText,
operator.DuplicateDrawing,
operator.EditAssignedProduct,
operator.EditSheet,
operator.EditText,
operator.EditVectorStyle,
operator.EditTextPopup,
operator.EnableEditingAssignedProduct,
operator.EnableEditingText,
operator.ExpandSheet,
operator.LoadDrawings,
operator.LoadSchedules,
operator.LoadSheets,
operator.OpenDrawing,
operator.OpenSchedule,
operator.OpenSheet,
operator.OpenView,
operator.RemoveDrawing,
operator.RemoveDrawingFromSheet,
operator.RemoveDrawingStyle,
operator.RemoveDrawingStyleAttribute,
operator.RemoveSchedule,
operator.RemoveSheet,
operator.RemoveTextLiteral,
operator.SelectAllDrawings,
operator.ResizeText,
operator.SaveDrawingStyle,
operator.SelectDocIfcFile,
operator.SelectAssignedProduct,
operator.SelectDocIfcFile,
prop.Variable,
prop.Drawing,
prop.Schedule,
@@ -71,8 +75,10 @@ classes = (
prop.Sheet,
prop.DocProperties,
prop.BIMCameraProperties,
prop.Literal,
prop.BIMTextProperties,
prop.BIMAssignedProductProperties,
prop.BIMAnnotationProperties,
ui.BIM_PT_camera,
ui.BIM_PT_drawing_underlay,
ui.BIM_PT_annotation_utilities,
@@ -88,11 +94,15 @@ classes = (
gizmos.DimensionLabelGizmo,
gizmos.ExtrusionGuidesGizmo,
gizmos.ExtrusionWidget,
workspace.Hotkey,
)
def register():
if not bpy.app.background:
bpy.utils.register_tool(workspace.AnnotationTool, after={"bim.bim_tool"}, separator=True, group=True)
bpy.types.Scene.DocProperties = bpy.props.PointerProperty(type=prop.DocProperties)
bpy.types.Scene.BIMAnnotationProperties = bpy.props.PointerProperty(type=prop.BIMAnnotationProperties)
bpy.types.Camera.BIMCameraProperties = bpy.props.PointerProperty(type=prop.BIMCameraProperties)
bpy.types.Object.BIMAssignedProductProperties = bpy.props.PointerProperty(type=prop.BIMAssignedProductProperties)
bpy.types.Object.BIMTextProperties = bpy.props.PointerProperty(type=prop.BIMTextProperties)
@@ -102,7 +112,10 @@ def register():
def unregister():
if not bpy.app.background:
bpy.utils.unregister_tool(workspace.AnnotationTool)
del bpy.types.Scene.DocProperties
del bpy.types.Scene.BIMAnnotationProperties
del bpy.types.Camera.BIMCameraProperties
del bpy.types.Object.BIMAssignedProductProperties
del bpy.types.Object.BIMTextProperties
@@ -23,17 +23,6 @@ from mathutils import Vector
class Annotator:
@staticmethod
def get_svg_text_size(size):
sizes = {
"1.8": "2.97",
"2.5": "4.13",
"3.5": "5.78",
"5.0": "8.25",
"7.0": "11.55",
}
return float(sizes[str(size)])
@staticmethod
def add_text(related_element=None):
curve = bpy.data.curves.new(type="FONT", name="Text")
@@ -43,7 +32,7 @@ class Annotator:
if related_element is None:
location, _, _, _ = Annotator.get_placeholder_coords(bpy.context)
else:
obj.data.BIMTextProperties.related_element = related_element
obj.data.BIMAssignedProductProperties.related_element = related_element
location = related_element.location
obj.location = location
obj.hide_render = True
@@ -88,66 +77,35 @@ class Annotator:
co1, co2, _, _ = Annotator.get_placeholder_coords()
co1 = obj.matrix_world.inverted() @ co1
co2 = obj.matrix_world.inverted() @ co2
if isinstance(obj.data, bpy.types.Mesh):
obj.data.vertices.add(2)
obj.data.vertices[-2].co = co1
obj.data.vertices[-1].co = co2
obj.data.edges.add(1)
obj.data.edges[-1].vertices = (obj.data.vertices[-2].index, obj.data.vertices[-1].index)
if isinstance(obj.data, bpy.types.Curve):
polyline = obj.data.splines.new("POLY")
polyline.points.add(1)
polyline.points[-2].co = list(co1) + [1]
polyline.points[-1].co = list(co2) + [1]
return obj
@staticmethod
def add_plane_to_annotation(obj):
co1, co2, co3, co4 = Annotator.get_placeholder_coords()
co1 = obj.matrix_world.inverted() @ co1 # bot left
co2 = obj.matrix_world.inverted() @ co2 # top left
co3 = obj.matrix_world.inverted() @ co3 # bot right
co4 = obj.matrix_world.inverted() @ co4 # top right
# default order = bot left, top left, bot right, top right
# therefore we redefine the order
face_verts = [0, 2, 3, 1]
obj.data.vertices.add(4)
v1 = obj.data.vertices[-4]
v2 = obj.data.vertices[-3]
v3 = obj.data.vertices[-2]
v4 = obj.data.vertices[-1]
v1.co = co4
v2.co = co2
v3.co = co1
v4.co = co3
verts_world_space = Annotator.get_placeholder_coords()
verts_local = [obj.matrix_world.inverted() @ v for v in verts_world_space]
bm = tool.Blender.get_bmesh_for_mesh(obj.data, clean=True)
new_verts = [bm.verts.new(v) for v in verts_local]
obj.data.edges.add(4)
e1 = obj.data.edges[-4]
e2 = obj.data.edges[-3]
e3 = obj.data.edges[-2]
e4 = obj.data.edges[-1]
e1.vertices = (v1.index, v2.index)
e2.vertices = (v2.index, v3.index)
e3.vertices = (v3.index, v4.index)
e4.vertices = (v4.index, v1.index)
obj.data.loops.add(4)
l1 = obj.data.loops[-4]
l2 = obj.data.loops[-3]
l3 = obj.data.loops[-2]
l4 = obj.data.loops[-1]
l1.vertex_index = v1.index
l1.edge_index = e1.index
l2.vertex_index = v2.index
l2.edge_index = e2.index
l3.vertex_index = v3.index
l3.edge_index = e3.index
l4.vertex_index = v4.index
l4.edge_index = e4.index
obj.data.polygons.add(1)
p1 = obj.data.polygons[-1]
p1.vertices = (v1.index, v2.index, v3.index, v4.index)
p1.loop_start = l1.index
p1.loop_total = 4
bm.faces.new([new_verts[i] for i in face_verts])
tool.Blender.apply_bmesh(obj.data, bm, obj)
return obj
@staticmethod
@@ -155,15 +113,15 @@ class Annotator:
camera = tool.Ifc.get_object(drawing)
co1, _, _, _ = Annotator.get_placeholder_coords(camera)
matrix_world = camera.matrix_world.copy()
matrix_world[0][3] = co1.x
matrix_world[1][3] = co1.y
matrix_world[2][3] = co1.z
matrix_world.translation = co1
collection = camera.users_collection[0]
if object_type == "TEXT":
obj = bpy.data.objects.new(object_type, None)
obj.matrix_world = matrix_world
collection.objects.link(obj)
return obj
elif object_type in ("TEXT_LEADER", "SECTION_LEVEL"):
data = bpy.data.curves.new(object_type, type="CURVE")
data.dimensions = "3D"
@@ -172,17 +130,20 @@ class Annotator:
obj.matrix_world = matrix_world
collection.objects.link(obj)
return obj
if object_type != "ANGLE":
for obj in collection.objects:
element = tool.Ifc.get_entity(obj)
if element and element.ObjectType == object_type:
if element and element.ObjectType == object_type and obj.type == object_type.upper():
return obj
if data_type == "mesh":
data = bpy.data.meshes.new(object_type)
elif data_type == "curve":
data = bpy.data.curves.new(object_type, type="CURVE")
data.dimensions = "3D"
data.resolution_u = 2
obj = bpy.data.objects.new(object_type, data)
obj.matrix_world = matrix_world
collection.objects.link(obj)
@@ -193,14 +154,13 @@ class Annotator:
if not camera:
camera = bpy.context.scene.camera
z_offset = camera.matrix_world.to_quaternion() @ Vector((0, 0, -1))
if bpy.context.scene.render.resolution_x > bpy.context.scene.render.resolution_y:
y = (
camera.data.ortho_scale
* (bpy.context.scene.render.resolution_y / bpy.context.scene.render.resolution_x)
/ 4
)
else:
y = camera.data.ortho_scale / 4
y = camera.data.ortho_scale / 4
res_x = bpy.context.scene.render.resolution_x
res_y = bpy.context.scene.render.resolution_y
if res_x > res_y:
y *= res_y / res_x
y_offset = camera.matrix_world.to_quaternion() @ Vector((0, y, 0))
x_offset = camera.matrix_world.to_quaternion() @ Vector((y / 2, 0, 0))
return (
@@ -16,17 +16,23 @@
# You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import os
import bpy
import ifcopenshell.util.element
import ifcopenshell.util.representation
import blenderbim.tool as tool
from pathlib import Path
def refresh():
ProductAssignmentsData.is_loaded = False
TextData.is_loaded = False
SheetsData.is_loaded = False
SchedulesData.is_loaded = False
DrawingsData.is_loaded = False
AnnotationData.is_loaded = False
DecoratorData.data = {}
DecoratorData.cut_cache = {}
DecoratorData.layerset_cache = {}
class ProductAssignmentsData:
@@ -49,40 +55,42 @@ class ProductAssignmentsData:
return f"{rel.RelatingProduct.is_a()}/{name}"
class TextData:
data = {}
is_loaded = False
@classmethod
def load(cls):
cls.data = {"attributes": cls.attributes()}
cls.is_loaded = True
@classmethod
def attributes(cls):
element = tool.Ifc.get_entity(bpy.context.active_object)
if not element or not element.is_a("IfcAnnotation") or element.ObjectType not in ["TEXT", "TEXT_LEADER"]:
return []
rep = ifcopenshell.util.representation.get_representation(element, "Plan", "Annotation")
text_literal = [i for i in rep.Items if i.is_a("IfcTextLiteral")][0]
return [
{"name": "Literal", "value": text_literal.Literal},
{"name": "BoxAlignment", "value": text_literal.BoxAlignment},
]
class SheetsData:
data = {}
is_loaded = False
@classmethod
def load(cls):
cls.data = {"total_sheets": cls.total_sheets()}
cls.data = {
"has_saved_ifc": cls.has_saved_ifc(),
"total_sheets": cls.total_sheets(),
"titleblocks": cls.titleblocks(),
}
cls.is_loaded = True
@classmethod
def has_saved_ifc(cls):
return os.path.isfile(tool.Ifc.get_path())
@classmethod
def total_sheets(cls):
return len([d for d in tool.Ifc.get().by_type("IfcDocumentInformation") if d.Scope == "DOCUMENTATION"])
return len([d for d in tool.Ifc.get().by_type("IfcDocumentInformation") if d.Scope == "SHEET"])
@classmethod
def titleblocks(cls):
files = Path(os.path.join(bpy.context.scene.BIMProperties.data_dir, "templates", "titleblocks")).glob("*.svg")
files = [str(f.stem) for f in files]
if tool.Ifc.get():
project = tool.Ifc.get().by_type("IfcProject")[0]
titleblocks_dir = ifcopenshell.util.element.get_pset(project, "BBIM_Documentation", "TitleblocksDir")
if not titleblocks_dir:
titleblocks_dir = bpy.context.scene.DocProperties.titleblocks_dir
titleblocks_dir = tool.Ifc.resolve_uri(titleblocks_dir)
if os.path.exists(titleblocks_dir):
files.extend([str(f.stem) for f in Path(titleblocks_dir).glob("*.svg")])
return [(f, f, "") for f in sorted(list(set(files)))]
class DrawingsData:
@@ -91,9 +99,17 @@ class DrawingsData:
@classmethod
def load(cls):
cls.data = {"total_drawings": cls.total_drawings(), "location_hint": cls.location_hint()}
cls.data = {
"has_saved_ifc": cls.has_saved_ifc(),
"total_drawings": cls.total_drawings(),
"location_hint": cls.location_hint(),
}
cls.is_loaded = True
@classmethod
def has_saved_ifc(cls):
return os.path.isfile(tool.Ifc.get_path())
@classmethod
def total_drawings(cls):
return len([e for e in tool.Ifc.get().by_type("IfcAnnotation") if e.ObjectType == "DRAWING"])
@@ -115,9 +131,191 @@ class SchedulesData:
@classmethod
def load(cls):
cls.data = {"total_schedules": cls.total_schedules()}
cls.data = {"has_saved_ifc": cls.has_saved_ifc(), "total_schedules": cls.total_schedules()}
cls.is_loaded = True
@classmethod
def has_saved_ifc(cls):
return os.path.isfile(tool.Ifc.get_path())
@classmethod
def total_schedules(cls):
return len([d for d in tool.Ifc.get().by_type("IfcDocumentInformation") if d.Scope == "SCHEDULE"])
FONT_SIZES = {
"small": 1.8,
"regular": 2.5,
"large": 3.5,
"header": 5.0,
"title": 7.0,
}
class DecoratorData:
# stores 1 type of data per object
data = {}
cut_cache = {}
layerset_cache = {}
# used by Ifc Annotations with ObjectType = "BATTING"
@classmethod
def get_batting_thickness(cls, obj):
result = cls.data.get(obj.name, None)
if result is not None:
return result
element = tool.Ifc.get_entity(obj)
if element:
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
thickness = ifcopenshell.util.element.get_pset(element, "BBIM_Batting", "Thickness")
thickness = thickness * unit_scale if thickness else 1.5
cls.data[obj.name] = thickness
return thickness
# used by Ifc Annotations with ObjectType = "SECTION"
@classmethod
def get_section_markers_display_data(cls, obj):
result = cls.data.get(obj.name, None)
if result is not None:
return result
element = tool.Ifc.get_entity(obj)
if not element:
return
# default values
pset_data = {
"HasConnectedSectionLine": True,
"ShowStartArrow": True,
"StartArrowSymbol": "",
"ShowEndArrow": True,
"EndArrowSymbol": "",
}
obj_pset_data = ifcopenshell.util.element.get_pset(element, "BBIM_Section") or {}
pset_data.update(obj_pset_data)
# create more usable display data
start_symbol = pset_data["StartArrowSymbol"]
end_symbol = pset_data["EndArrowSymbol"]
display_data = {
"start": {
"add_circle": pset_data["ShowStartArrow"] and not start_symbol,
"add_symbol": pset_data["ShowStartArrow"] or bool(start_symbol),
"symbol": start_symbol or "section-arrow",
},
"end": {
"add_circle": pset_data["ShowEndArrow"] and not end_symbol,
"add_symbol": pset_data["ShowEndArrow"] or bool(end_symbol),
"symbol": end_symbol or "section-arrow",
},
"connect_markers": pset_data["HasConnectedSectionLine"],
}
cls.data[obj.name] = display_data
return display_data
# used by Ifc Annotations with ObjectType = "TEXT" / "TEXT_LEADER"
@classmethod
def get_ifc_text_data(cls, obj):
"""returns font size in mm for current ifc text object"""
result = cls.data.get(obj.name, None)
if result is not None:
return result
element = tool.Ifc.get_entity(obj)
if not element or not tool.Drawing.is_annotation_object_type(element, ["TEXT", "TEXT_LEADER"]):
return None
props = obj.BIMTextProperties
# getting font size
pset_data = ifcopenshell.util.element.get_pset(element, "EPset_Annotation") or {}
# use `regular` as default
# get font size
if classes := pset_data.get("Classes", None):
classes_split = classes.split()
# prioritize smaller font sizes just like in svg
font_size_type = next(
(font_size_type for font_size_type in FONT_SIZES if font_size_type in classes_split), "regular"
)
else:
font_size_type = "regular"
font_size = FONT_SIZES[font_size_type]
# get symbol
symbol = pset_data.get("Symbol", None)
# other attributes
props_literals = props.literals
props_literals_n = len(props.literals)
literals = tool.Drawing.get_text_literal(obj, return_list=True)
literals_data = []
for i, literal in enumerate(literals):
literal_data = {
"Literal": literal.Literal,
"BoxAlignment": literal.BoxAlignment,
}
if i < props_literals_n:
literal_data["CurrentValue"] = props_literals[i].value
else:
literal_data["CurrentValue"] = literal.Literal
literals_data.append(literal_data)
text_data = {"Literals": literals_data, "FontSize": font_size, "Symbol": symbol}
cls.data[obj.name] = text_data
return text_data
# used by Ifc Annotations with ObjectType = "DIMENSION" / "DIAMETER"
@classmethod
def get_dimension_data(cls, obj):
result = cls.data.get(obj.name, None)
if result is not None:
return result
element = tool.Ifc.get_entity(obj)
supported_object_types = ("DIMENSION", "DIAMETER")
if not element or not element.is_a("IfcAnnotation") or element.ObjectType not in supported_object_types:
return None
dimension_style = "arrow"
classes = ifcopenshell.util.element.get_pset(element, "EPset_Annotation", "Classes")
if classes and "oblique" in classes.lower().split():
dimension_style = "oblique"
pset_data = ifcopenshell.util.element.get_pset(element, "BBIM_Dimension") or {}
show_description_only = pset_data.get("ShowDescriptionOnly", False)
suppress_zero_inches = pset_data.get("SuppressZeroInches", False)
dimension_data = {
"dimension_style": dimension_style,
"show_description_only": show_description_only,
"suppress_zero_inches": suppress_zero_inches,
}
cls.data[obj.name] = dimension_data
return dimension_data
class AnnotationData:
data = {}
is_loaded = False
@classmethod
def load(cls):
cls.is_loaded = True
cls.props = bpy.context.scene.BIMAnnotationProperties
cls.data["relating_types"] = cls.get_relating_types()
@classmethod
def get_relating_types(cls):
object_type = cls.props.object_type
relating_types = []
for relating_type in tool.Ifc.get().by_type("IfcTypeProduct"):
if tool.Drawing.is_annotation_object_type(relating_type, object_type):
relating_types.append(relating_type)
enum_items = [(str(e.id()), e.Name or "Unnamed", e.Description or "") for e in relating_types]
# item to create anootations without relating types
enum_items.insert(0, ("0", "-", ""))
return enum_items
File diff suppressed because it is too large Load Diff
@@ -18,10 +18,9 @@
import bpy
import blf
import math
import gpu, bgl
import gpu
from bpy import types
from mathutils import Vector, Matrix
from mathutils import Vector
from mathutils import geometry
from bpy_extras import view3d_utils
from blenderbim.bim.module.drawing.shaders import DotsGizmoShader, ExtrusionGuidesShader, BaseLinesShader
@@ -273,7 +272,7 @@ class CustomGizmo:
gpu.matrix.multiply_matrix(self.matrix_world)
batch.draw()
bgl.glDisable(bgl.GL_BLEND)
gpu.state.blend_set("NONE")
class OffsetHandle:
@@ -500,7 +499,7 @@ class ExtrusionWidget(types.GizmoGroup):
gz = self.guides = self.gizmos.new("BIM_GT_extrusion_guides")
gz.matrix_basis = basis
gz.color = gz.color_highlight = tuple(theme.gizmo_secondary)
gz.alpha = gz.alpha_highlight = 0.5
gz.alpha = gz.alpha_highlight = 0.75
gz.use_draw_modal = True
gz.target_set_prop("depth", prop, "value")
gz.scale_value = scale_value
@@ -23,8 +23,7 @@ from bpy.app.handlers import persistent
@persistent
def toggleDecorationsOnLoad(*args):
toggle = bpy.context.scene.DocProperties.should_draw_decorations
if toggle:
if bpy.context.scene.DocProperties.should_draw_decorations:
decoration.DecorationsHandler.install(bpy.context)
else:
decoration.DecorationsHandler.uninstall()
@@ -120,9 +120,9 @@ class BoundingBox:
# This function stolen from https://github.com/kevancress/MeasureIt_ARCH/blob/dcf607ce0896aa2284463c6b4ae9cd023fc54cbe/measureit_arch_baseclass.py
# MeasureIt-ARCH is GPL-v3
# In the future I will need to rewrite this to allow the user to have custom
# settings for each annotation object, not read from Blender.
def format_distance(value, isArea=False, hide_units=True):
def format_distance(
value, isArea=False, hide_units=True, precision=None, decimal_places=None, suppress_zero_inches=False
):
s_code = "\u00b2" # Superscript two THIS IS LEGACY (but being kept for when Area Measurements are re-implimented)
# Get Scene Unit Settings
@@ -131,7 +131,7 @@ def format_distance(value, isArea=False, hide_units=True):
unit_length = bpy.context.scene.unit_settings.length_unit
toInches = 39.3700787401574887
inPerFoot = 11.999
inPerFoot = 11.9999
if isArea:
toInches = 1550
@@ -141,8 +141,7 @@ def format_distance(value, isArea=False, hide_units=True):
# Imperial Formatting
if unit_system == "IMPERIAL":
precision = bpy.context.scene.BIMProperties.imperial_precision
if precision == "NONE":
if not precision:
precision = 256
elif precision == "1":
precision = 1
@@ -186,44 +185,50 @@ def format_distance(value, isArea=False, hide_units=True):
inches = 0
if not isArea:
add_inches = bool(inches) or not suppress_zero_inches
tx_dist = ""
if feet:
tx_dist += str(feet) + "'"
if feet and inches:
if feet and add_inches:
tx_dist += " - "
if inches:
if add_inches:
tx_dist += str(inches)
if inches and frac:
if add_inches and frac:
tx_dist += " "
if frac:
tx_dist += str(frac) + "/" + str(base)
if inches or frac:
if add_inches or frac:
tx_dist += '"'
else:
tx_dist = str("%1.3f" % (value * toInches / inPerFoot)) + " sq. ft."
# METRIC FORMATTING
elif unit_system == "METRIC":
precision = bpy.context.scene.BIMProperties.metric_precision
if precision != 0:
if precision:
value = precision * round(float(value) / precision)
if decimal_places:
fmt = "%1." + str(decimal_places) + "f"
# Meters
if unit_length == "METERS":
fmt = "%1.3f"
if not decimal_places:
fmt = "%1.3f"
if hide_units is False:
fmt += " m"
tx_dist = fmt % value
# Centimeters
elif unit_length == "CENTIMETERS":
fmt = "%1.1f"
if not decimal_places:
fmt = "%1.1f"
if hide_units is False:
fmt += " cm"
d_cm = value * (100)
tx_dist = fmt % d_cm
# Millimeters
elif unit_length == "MILLIMETERS":
fmt = "%1.0f"
if not decimal_places:
fmt = "%1.0f"
if hide_units is False:
fmt += " mm"
d_mm = value * (1000)
@@ -231,20 +236,21 @@ def format_distance(value, isArea=False, hide_units=True):
# Otherwise Use Adaptive Units
else:
if round(value, 2) >= 1.0:
if round(value, 2) >= 1.0 and not decimal_places:
fmt = "%1.3f"
if hide_units is False:
fmt += " m"
tx_dist = fmt % value
else:
if round(value, 2) >= 0.01:
if round(value, 2) >= 0.01 and not decimal_places:
fmt = "%1.1f"
if hide_units is False:
fmt += " cm"
d_cm = value * (100)
tx_dist = fmt % d_cm
else:
fmt = "%1.0f"
if not decimal_places:
fmt = "%1.0f"
if hide_units is False:
fmt += " mm"
d_mm = value * (1000)
File diff suppressed because it is too large Load Diff
@@ -25,7 +25,8 @@ import blenderbim.tool as tool
import blenderbim.core.drawing as core
import blenderbim.bim.module.drawing.annotation as annotation
import blenderbim.bim.module.drawing.decoration as decoration
from blenderbim.bim.module.drawing.data import DrawingsData
from blenderbim.bim.module.drawing.data import DrawingsData, DecoratorData, SheetsData, AnnotationData
from blenderbim.bim.module.drawing.data import refresh as refresh_drawing_data
from pathlib import Path
from blenderbim.bim.prop import Attribute, StrProperty
from bpy.types import PropertyGroup
@@ -38,24 +39,19 @@ from bpy.props import (
FloatProperty,
FloatVectorProperty,
CollectionProperty,
BoolVectorProperty,
)
diagram_scales_enum = []
titleblocks_enum = []
sheets_enum = []
vector_styles_enum = []
def purge():
global diagram_scales_enum
global titleblocks_enum
global sheets_enum
global vector_styles_enum
diagram_scales_enum = []
titleblocks_enum = []
sheets_enum = []
vector_styles_enum = []
def update_target_view(self, context):
@@ -75,10 +71,16 @@ def update_diagram_scale(self, context):
if "|" not in scale:
return
human_scale, scale = scale.split("|")
element = tool.Ifc.get_entity(context.active_object)
if not element:
try:
element = (
tool.Ifc.get()
.by_id(self.id_data.BIMMeshProperties.ifc_definition_id)
.OfProductRepresentation[0]
.ShapeOfProduct[0]
)
except:
return
pset = ifcopenshell.util.element.get_psets(element).get("EPset_Drawing")
pset = ifcopenshell.util.element.get_pset(element, "EPset_Drawing")
if pset:
pset = tool.Ifc.get().by_id(pset["id"])
else:
@@ -183,20 +185,14 @@ def update_layer(self, context, name, value):
ifcopenshell.api.run("pset.edit_pset", tool.Ifc.get(), pset=pset, properties={name: value})
def getTitleblocks(self, context):
global titleblocks_enum
if len(titleblocks_enum) < 1:
titleblocks_enum.clear()
files = Path(os.path.join(context.scene.BIMProperties.data_dir, "templates", "titleblocks")).glob("*.svg")
files = sorted([str(f.stem) for f in files])
titleblocks_enum.extend([(f, f, "") for f in files])
return titleblocks_enum
def get_titleblocks(self, context):
if not SheetsData.is_loaded:
SheetsData.load()
return SheetsData.data["titleblocks"]
def refreshTitleblocks(self, context):
global titleblocks_enum
titleblocks_enum.clear()
getTitleblocks(self, context)
def update_titleblocks(self, context):
SheetsData.data["titleblocks"] = SheetsData.titleblocks()
def toggleDecorations(self, context):
@@ -206,25 +202,12 @@ def toggleDecorations(self, context):
collection = context.scene.camera.users_collection[0]
for obj in collection.objects:
tool.Drawing.update_text_value(obj)
refresh_drawing_data()
decoration.DecorationsHandler.install(context)
else:
decoration.DecorationsHandler.uninstall()
def getVectorStyles(self, context):
global vector_styles_enum
if len(vector_styles_enum) < 1:
sheets_enum.clear()
for filename in Path(os.path.join(context.scene.BIMProperties.data_dir, "styles")).glob("*.css"):
f = str(filename.stem)
vector_styles_enum.append((f, f, ""))
return vector_styles_enum
def refreshFontSize(self, context):
annotation.Annotator.resize_text(context.active_object)
class Variable(PropertyGroup):
name: StringProperty(name="Name")
prop_key: StringProperty(name="Property Key")
@@ -234,6 +217,7 @@ class Drawing(PropertyGroup):
ifc_definition_id: IntProperty(name="IFC Definition ID")
name: StringProperty(name="Name", update=update_drawing_name)
target_view: StringProperty(name="Target View")
is_selected: BoolProperty(name="Is Selected", default=True)
class Schedule(PropertyGroup):
@@ -243,19 +227,9 @@ class Schedule(PropertyGroup):
class Sheet(PropertyGroup):
def set_name(self, new):
old = self.get("name")
path = os.path.join(bpy.context.scene.BIMProperties.data_dir, "sheets")
if old and os.path.isfile(os.path.join(path, old + ".svg")):
os.rename(os.path.join(path, old + ".svg"), os.path.join(path, new + ".svg"))
self["name"] = new
def get_name(self):
return self.get("name")
ifc_definition_id: IntProperty(name="IFC Definition ID")
identification: StringProperty(name="Identification")
name: StringProperty(name="Name", get=get_name, set=set_name)
name: StringProperty(name="Name")
is_sheet: BoolProperty(name="Is Sheet", default=False)
reference_type: StringProperty(name="Reference Type")
is_expanded: BoolProperty(name="Is Expanded", default=False)
@@ -273,7 +247,6 @@ class DrawingStyle(PropertyGroup):
name="Render Type",
default="VIEWPORT",
)
vector_style: EnumProperty(items=getVectorStyles, name="Vector Style")
include_query: StringProperty(name="Include Query")
exclude_query: StringProperty(name="Exclude Query")
attributes: CollectionProperty(name="Attributes", type=StrProperty)
@@ -332,19 +305,30 @@ class DocProperties(PropertyGroup):
current_drawing_index: IntProperty(name="Current Drawing Index")
schedules: CollectionProperty(name="Schedules", type=Schedule)
active_schedule_index: IntProperty(name="Active Schedule Index")
titleblock: EnumProperty(items=getTitleblocks, name="Titleblock", update=refreshTitleblocks)
titleblock: EnumProperty(items=get_titleblocks, name="Titleblock", update=update_titleblocks)
is_editing_sheets: BoolProperty(name="Is Editing Sheets", default=False)
sheets: CollectionProperty(name="Sheets", type=Sheet)
active_sheet_index: IntProperty(name="Active Sheet Index")
ifc_files: CollectionProperty(name="IFCs", type=StrProperty)
drawing_styles: CollectionProperty(name="Drawing Styles", type=DrawingStyle)
should_draw_decorations: BoolProperty(name="Should Draw Decorations", update=toggleDecorations)
decorations_colour: FloatVectorProperty(
name="Decorations Colour", subtype="COLOR", default=(1, 1, 1, 1), min=0.0, max=1.0, size=4
sheets_dir: StringProperty(default=os.path.join("sheets") + os.path.sep, name="Default Sheets Directory")
layouts_dir: StringProperty(default=os.path.join("layouts") + os.path.sep, name="Default Layouts Directory")
titleblocks_dir: StringProperty(
default=os.path.join("layouts", "titleblocks") + os.path.sep, name="Default Titleblocks Directory"
)
drawings_dir: StringProperty(default=os.path.join("drawings") + os.path.sep, name="Default Drawings Directory")
stylesheet_path: StringProperty(
default=os.path.join("drawings", "assets", "default.css"), name="Default Stylesheet"
)
markers_path: StringProperty(default=os.path.join("drawings", "assets", "markers.svg"), name="Default Markers")
symbols_path: StringProperty(default=os.path.join("drawings", "assets", "symbols.svg"), name="Default Symbols")
patterns_path: StringProperty(default=os.path.join("drawings", "assets", "patterns.svg"), name="Default Patterns")
class BIMCameraProperties(PropertyGroup):
calculate_shapely_surfaces: BoolProperty(name="Calculate Shapely Surfaces", default=False)
calculate_svgfill_surfaces: BoolProperty(name="Calculate SVGFill Surfaces", default=False)
has_underlay: BoolProperty(name="Underlay", default=False, update=update_has_underlay)
has_linework: BoolProperty(name="Linework", default=True, update=update_has_linework)
has_annotation: BoolProperty(name="Annotation", default=True, update=update_has_annotation)
@@ -375,10 +359,66 @@ class BIMCameraProperties(PropertyGroup):
return False
DEFAULT_BOX_ALIGNMENT = [False] * 6 + [True] + [False] * 2
BOX_ALIGNMENT_POSITIONS = [
"top-left",
"top-middle",
"top-right",
"middle-left",
"center",
"middle-right",
"bottom-left",
"bottom-middle",
"bottom-right",
]
class Literal(PropertyGroup):
def set_box_alignment(self, new_value):
markers = new_value.count(True)
if not markers:
return
if markers > 1:
prev_value = self.get("box_alignment", DEFAULT_BOX_ALIGNMENT)
# looking for the first value changed to positive
first_changed_value = next((i for i in range(9) if new_value[i] and new_value[i] != prev_value[i]), None)
# if nothing have changed we just keep the previous value
if first_changed_value is None:
return
new_value = [False] * 9
new_value[first_changed_value] = True
self["box_alignment"] = new_value
position_string = BOX_ALIGNMENT_POSITIONS[next(i for i in range(9) if new_value[i])]
self.attributes["BoxAlignment"].set_value(position_string)
def get_box_alignment(self):
return self.get("box_alignment", DEFAULT_BOX_ALIGNMENT)
attributes: CollectionProperty(name="Attributes", type=Attribute)
# Current text value with evaluated experessions stored in `value`.
# The original (Literal) value stored in `attributes['Literal']`
# and can be accessed with `get_text()`
value: StringProperty(name="Value", default="TEXT")
box_alignment: BoolVectorProperty(
name="Box alignment", size=9, set=set_box_alignment, get=get_box_alignment, default=DEFAULT_BOX_ALIGNMENT
)
ifc_definition_id: IntProperty(name="IFC definition ID", default=0)
def get_literal_edited_data(self):
text_data = {
"CurrentValue": self.attributes["Literal"].string_value,
"Literal": self.attributes["Literal"].string_value,
"BoxAlignment": self.attributes["BoxAlignment"].string_value,
}
return text_data
class BIMTextProperties(PropertyGroup):
is_editing: BoolProperty(name="Is Editing", default=False)
attributes: CollectionProperty(name="Attributes", type=Attribute)
value: StringProperty(name="Value", default="TEXT")
literals: CollectionProperty(name="Literals", type=Literal)
font_size: EnumProperty(
items=[
("1.8", "1.8 - Small", ""),
@@ -388,11 +428,79 @@ class BIMTextProperties(PropertyGroup):
("7.0", "7.0 - Title", ""),
],
default="2.5",
update=refreshFontSize,
name="Font Size",
)
def get_text_edited_data(self):
"""should be called only if `is_editing`
otherwise should use `DecoratorData.get_ifc_text_data(obj)` instead
because this data could be out of date
"""
literals_data = []
for literal in self.literals:
literal_data = literal.get_literal_edited_data()
literals_data.append(literal_data)
text_data = {
"Literals": literals_data,
"FontSize": float(self.font_size),
}
return text_data
class BIMAssignedProductProperties(PropertyGroup):
is_editing_product: BoolProperty(name="Is Editing Product", default=False)
relating_product: PointerProperty(name="Relating Product", type=bpy.types.Object)
# ObjectType: annotation_name, description, icon, data_type
# fmt: off
ANNOTATION_TYPES_DATA = {
"DIMENSION": ("Dimension", "Add dimensions annotation.\nMeasurement values can be hidden through ShowDescriptionOnly property\nof BBIM_Dimension property set", "FIXED_SIZE", "curve"),
"ANGLE": ("Angle", "", "DRIVER_ROTATIONAL_DIFFERENCE", "curve"),
"RADIUS": ("Radius", "", "FORWARD", "curve"),
"DIAMETER": ("Diameter", "Add diameter annotation.\nMeasurement values can be hidden through ShowDescriptionOnly property\nof BBIM_Dimension property set", "ARROW_LEFTRIGHT", "curve"),
"TEXT": ("Text", "", "SMALL_CAPS", "empty"),
"TEXT_LEADER": ("Leader", "", "TRACKING_BACKWARDS", "curve"),
"STAIR_ARROW": ("Stair Arrow", "Add stair arrow annotation.\nIf you have IfcStairFlight object selected, it will be used as a reference for the annotation", "SCREEN_BACK", "curve"),
"HIDDEN_LINE": ("Hidden", "", "CON_TRACKTO", "mesh"),
"PLAN_LEVEL": ("Level (Plan)", "", "SORTBYEXT", "curve"),
"SECTION_LEVEL": ("Level (Section)", "", "TRIA_DOWN", "curve"),
"BREAKLINE": ("Breakline", "", "FCURVE", "mesh"),
"LINEWORK": ("Line", "", "MESH_MONKEY", "mesh"),
"BATTING": ("Batting", "Add batting annotation.\nThickness could be changed through Thickness property of BBIM_Batting property set", "FORCE_FORCE", "mesh"),
"FILL_AREA": ("Fill Area", "", "NODE_TEXTURE", "mesh"),
"FALL": ("Fall", "", "SORT_ASC", "curve"),
}
# fmt: on
annotation_classes = [(x, *ANNOTATION_TYPES_DATA[x][:3], i) for i, x in enumerate(ANNOTATION_TYPES_DATA)]
def get_annotation_data_prop(prop_name):
def function(self, context):
if not AnnotationData.is_loaded:
AnnotationData.load()
return AnnotationData.data[prop_name]
return function
def update_annotation_object_type(self, context):
self.relating_type_id = "0"
# changing enum doesn't trigger refresh by itself
AnnotationData.is_loaded = False
class BIMAnnotationProperties(PropertyGroup):
object_type: bpy.props.EnumProperty(
name="Annotation Object Type", items=annotation_classes, default="TEXT", update=update_annotation_object_type
)
relating_type_id: bpy.props.EnumProperty(
name="Relating Annotation Type", items=get_annotation_data_prop("relating_types")
)
create_representation_for_type: bpy.props.BoolProperty(
name="Create Representation For Type",
default=False,
description='Whether "Add type" should define a representation for the type \n'
"or allow occurences to have their own",
)
@@ -16,6 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
from blenderbim.bim.module.drawing.svgwriter import SvgWriter
import svgwrite
from odf.opendocument import load
@@ -23,6 +24,8 @@ from odf.table import Table, TableRow, TableColumn, TableCell
from odf.text import P
from odf.style import Style
FONT_SIZE = 4.13
class Scheduler:
def schedule(self, infile, outfile):
@@ -37,32 +40,87 @@ class Scheduler:
styles = {}
for style in doc.getElementsByType(Style):
name = style.getAttribute("name")
# looking for table-column-properties or table-row-properties
if not style.firstChild:
continue
styles[name] = {key[1]: value for key, value in style.firstChild.attributes.items()}
if style.firstChild.tagName in ["style:table-column-properties", "style:table-row-properties"]:
style_children = [style.firstChild]
else:
# for style:table-cell-properties we need to collect also text and paragraph properties
style_children = style.childNodes
styles[name] = {}
for child in style_children:
child_params = {key[1]: value for key, value in child.attributes.items()}
styles[name].update(child_params)
table = doc.getElementsByType(Table)[0]
# collect columns width
column_widths = []
for col in table.getElementsByType(TableColumn):
style_name = col.getAttribute("stylename")
repeat = col.getAttribute("numbercolumnsrepeated")
repeat = int(repeat) if repeat else 1
for i in range(0, repeat):
for i in range(repeat):
if not style_name or "column-width" not in styles[style_name]:
column_widths.append(50)
column_width = 50
else:
column_widths.append(self.convert_to_mm(styles[style_name]["column-width"]))
column_width = self.convert_to_mm(styles[style_name]["column-width"])
column_widths.append(column_width)
# collect rows height
row_heights = []
for col in table.getElementsByType(TableRow):
style_name = col.getAttribute("stylename")
repeat = col.getAttribute("numberrowsrepeated")
repeat = int(repeat) if repeat else 1
for i in range(repeat):
if not style_name or "row-height" not in styles[style_name]:
row_height = 6
else:
row_height = self.convert_to_mm(styles[style_name]["row-height"])
row_heights.append(row_height)
# draw table
y = self.margin
for tri, tr in enumerate(table.getElementsByType(TableRow)):
x = self.margin
height = 6
height = row_heights[tri]
tdi = 0
for td in tr.getElementsByType(TableCell):
column_span = td.getAttribute("numbercolumnsspanned")
column_span = int(column_span) if column_span else 1
repeat = td.getAttribute("numbercolumnsrepeated")
repeat = int(repeat) if repeat else 1
# figuring text alignment
style_name = td.getAttribute("stylename")
style = styles[style_name] if style_name else {}
if style_name and "vertical-align" in style and style["vertical-align"] != "automatic":
vertical_align = style["vertical-align"]
else:
vertical_align = "bottom"
if style_name and "text-align" in style and style["text-align"] != "automatic":
horizontal_align = style["text-align"]
horizontal_align = "middle" if horizontal_align == "center" else horizontal_align
else:
horizontal_align = "left"
if vertical_align == "middle" and horizontal_align == "middle":
box_alignment = "center"
else:
box_alignment = f"{vertical_align}-{horizontal_align}"
# for future use
wrap_text = style.get("wrap-option", None) == "wrap"
# drawing cells and text
for i in range(0, repeat):
width = column_widths[tdi]
width = sum(column_widths[tdi : tdi + int(column_span)])
self.svg.add(
self.svg.rect(
insert=(x, y),
@@ -72,9 +130,26 @@ class Scheduler:
)
value = td.getElementsByType(P)
if value:
self.add_text(value[0], x + self.padding, y + self.padding)
# figuring text position based on alignment
text_position = [0, 0]
if box_alignment.endswith("left"):
text_position[0] = x + self.padding
elif box_alignment.endswith("middle") or box_alignment == "center":
text_position[0] = x + width / 2
elif box_alignment.endswith("right"):
text_position[0] = x + width - self.padding
if box_alignment.startswith("top"):
text_position[1] = y + self.padding
elif box_alignment.startswith("middle") or box_alignment == "center":
text_position[1] = y + height / 2
elif box_alignment.startswith("bottom"):
text_position[1] = y + height - self.padding
self.add_text(value[0], *text_position, box_alignment=box_alignment)
x += width
tdi += 1
tdi += column_span
y += height
total_width = sum(column_widths) + (self.margin * 2)
self.svg["width"] = "{}mm".format(total_width)
@@ -82,20 +157,16 @@ class Scheduler:
self.svg["viewBox"] = "0 0 {} {}".format(total_width, y)
self.svg.save(pretty=True)
def add_text(self, text, x, y):
self.svg.add(
self.svg.text(
str(text).upper(),
insert=tuple((x, y)),
**{
"font-size": 4.13,
"font-family": "OpenGost Type B TT",
"text-anchor": "start",
"alignment-baseline": "baseline",
"dominant-baseline": "hanging",
}
)
)
def add_text(self, text, x, y, box_alignment="bottom-left"):
box_alignment_params = SvgWriter.get_box_alignment_parameters(box_alignment)
text_params = {
"font-size": FONT_SIZE,
"font-family": "OpenGost Type B TT",
"text-anchor": "start",
}
text_params.update(box_alignment_params)
text_tag = self.svg.text(str(text).upper(), insert=tuple((x, y)), **text_params)
self.svg.add(text_tag)
def convert_to_mm(self, value):
# XSL is what defines the units of measurements in ODF
@@ -16,10 +16,177 @@
# You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import bgl
from mathutils import Matrix
from gpu.types import GPUShader
from gpu_extras.batch import batch_for_shader
import gpu
BASE_DEF_GLSL = """
#define PI 3.141592653589793
#define MAX_POINTS 64
#define CIRCLE_SEGS 12
#define SMOOTH_WIDTH 1.0
#define lineSmooth true
"""
# since `bgl` is deprecated, creating smoothing lines became tricky
# Notes for creating shaders with smoothed lines:
# in geom shader - use triangle_strip, DEFAULT_SETUP, do_edge_verts or do_vertex to emit vertices
# in frag shader - use lineWidth uniform, smoothline flaot in, smoothing shader code from base shader
# mind the vertex limit since emitting vertices for smoothed lines produces twice as much vertices
BASE_LIB_GLSL = """
// TODO: redefine as macor instead
uniform vec2 winsize;
uniform float lineWidth;
#define half_winsize (winsize / 2)
// convert camera to window
#define C2W(v) vec4(v.x * half_winsize.x / v.w, v.y * half_winsize.y / v.w, v.z / v.w, 1)
// convert window to camera
#define W2C(v) vec4(v.x * v.w / half_winsize.x, v.y * v.w / half_winsize.y, v.z * v.w, 1)
void emitSegment(vec4 p0, vec4 p1) {
gl_Position = p0;
EmitVertex();
gl_Position = p1;
EmitVertex();
EndPrimitive();
}
void emitTriangle(vec4 p1, vec4 p2, vec4 p3) {
gl_Position = p1;
EmitVertex();
gl_Position = p2;
EmitVertex();
gl_Position = p3;
EmitVertex();
EndPrimitive();
}
#define matCLIP2WIN() vec4(winsize.x/2, winsize.y/2, 1, 1)
#define CLIP2WIN(v) (clip2win * (v) / (v).w)
#define matWIN2CLIP() vec4(2/winsize.x, 2/winsize.y, 1, 1)
#define WIN2CLIP(v) (win2clip * (v) * (v).w)
#define DEFAULT_SETUP() vec4 clip2win = matCLIP2WIN(), win2clip = matWIN2CLIP(); vec2 EDGE_DIR
out float smoothline;
void arrow_head(in vec4 dir, in float size, in float angle, out vec4 head[3]) {
vec4 nose = dir * size;
float c = cos(angle), s = sin(angle);
head[0] = nose;
head[1] = vec4(mat2(c, -s, +s, c) * nose.xy, 0, 0);
head[2] = vec4(mat2(c, +s, -s, c) * nose.xy, 0, 0);
}
void circle_head(in float size, out vec4 head[CIRCLE_SEGS]) {
float angle_d = PI * 2 / CIRCLE_SEGS;
for(int i = 0; i<CIRCLE_SEGS; i++) {
float angle = angle_d * i;
head[i] = vec4(cos(angle), sin(angle), 0, 0) * size;
}
}
bool check_counterclockwise(in vec4 A, in vec4 B, in vec4 C) {
return (C.y-A.y) * (B.x-A.x) > (B.y-A.y) * (C.x-A.x);
}
void angle_circle_head(
in vec4 circle_start, in float circle_angle,
in bool counterclockwise,
out vec4 head[CIRCLE_SEGS+1], out float angle_segs) {
// 1 added to CIRCLE_SEGS because we're number of vertices
// for n segments is n+1
float angle_d;
angle_d = PI * 2 / CIRCLE_SEGS; // 30d
// need to bottom clamp it to 1, otherwise it causes Blender crash at extruding the curve
angle_segs = max(1, ceil(circle_angle / angle_d));
angle_d = circle_angle / angle_segs;
for(int i = 0; i < (angle_segs + 1); i++) {
float angle = angle_d * i;
if (counterclockwise) {
head[i] = vec4(
circle_start.x * cos(-angle) + circle_start.y * sin(-angle),
circle_start.x * -sin(-angle) + circle_start.y * cos(-angle),
0, 0
);
} else {
head[i] = vec4(
circle_start.x * cos(angle) + circle_start.y * sin(angle),
circle_start.x * -sin(angle) + circle_start.y * cos(angle),
0, 0
);
}
}
}
void cross_head(in vec4 dir, in float size, out vec4 head[3]) {
vec4 nose = dir * size;
float c = cos(PI/2), s = sin(PI/2);
head[0] = nose;
head[1] = vec4(mat2(c, -s, +s, c) * nose.xy, 0, 0);
head[2] = vec4(mat2(c, +s, -s, c) * nose.xy, 0, 0);
}
// do_vertex_util is custom EmitVertex method to support line smoothing
// `e` - edge xy direction in win/clip space (directions are invariant in both spaces)
#define do_vertex(pos, e) (do_vertex_util(pos, vec2(-(e).y, (e).x) / winsize.xy))
#define do_vertex_win(pos, e) ( do_vertex( WIN2CLIP( pos ), e ) )
// if vertex is shared by two segments of the line still need to emit it twice
// to avoid smoothing artifacts
// don't forget to initialize `vec2 EDGE_DIR` for macro to work
// `pos0` / `pos1` - vertex position in clip space
#define do_edge_verts(pos0, pos1) ( EDGE_DIR = normalize( ( (pos1) - (pos0) ).xy ), do_vertex( pos0, EDGE_DIR ), do_vertex( pos1, EDGE_DIR) )
#define do_edge_verts_win(pos0, pos1) ( do_edge_verts( WIN2CLIP(pos0), WIN2CLIP(pos1) ) )
void do_vertex_util(vec4 pos, vec2 ofs)
{
float final_line_width = lineWidth + SMOOTH_WIDTH * float(lineSmooth);
ofs *= final_line_width;
smoothline = final_line_width * 0.5;
gl_Position = pos;
gl_Position.xy += ofs * pos.w;
EmitVertex();
smoothline = -final_line_width * 0.5;
gl_Position = pos;
gl_Position.xy -= ofs * pos.w;
EmitVertex();
}
// geometry utils
void triangle_head(in vec4 side, in vec4 dir, in float length, in float width, in float radius, out vec4 head[5]) {
vec4 nose = side * length;
vec4 ear = dir * width;
head[0] = side * -radius;
head[1] = nose * -.5;
head[2] = vec4(0) + ear;
head[3] = nose * .5;
head[4] = side * radius;
}
void do_triangle_head(vec4 pos_w, vec4 head[5]) {
DEFAULT_SETUP();
do_edge_verts_win(pos_w + head[0], pos_w + head[1]);
do_edge_verts_win(pos_w + head[1], pos_w + head[2]);
do_edge_verts_win(pos_w + head[2], pos_w + head[3]);
do_edge_verts_win(pos_w + head[3], pos_w + head[4]);
}
void do_circle_head(vec4 pos_w, vec4 head[CIRCLE_SEGS]) {
DEFAULT_SETUP();
for(int i=0; i<CIRCLE_SEGS-1; i++) {
do_edge_verts_win(pos_w + head[i], pos_w + head[i+1]);
EmitVertex();
}
do_edge_verts_win( pos_w + head[CIRCLE_SEGS-1], pos_w + head[0] );
}
"""
class BaseShader:
@@ -28,7 +195,7 @@ class BaseShader:
The Geometry shader works in clipping coords (aftre projecting before division and window scaling).
To make window-scale geometry, vectors should be calculated in window space and than back-projected to clipping spae.
Provide `winSize` uniform vector with halfsize of window, and use W2C and C2W macros.
Provide `half_winsize` uniform vector with halfsize of window, and use W2C and C2W macros.
Replace glsl code in derived classes.
Beware of unused attributes and uniforms: glsl compiler will optimize them out and blender fail with an exception.
@@ -43,9 +210,7 @@ class BaseShader:
TYPE = None # should be LINES|POINTS|etc
DEF_GLSL = """
#define PI 3.141592653589793
"""
DEF_GLSL = BASE_DEF_GLSL
VERT_GLSL = """
uniform mat4 viewMatrix;
@@ -63,46 +228,45 @@ class BaseShader:
"""
# prepended to geom_glsl
LIB_GLSL = """
uniform vec2 winSize;
// convert camera to window
#define C2W(v) vec4(v.x * winSize.x / v.w, v.y * winSize.y / v.w, v.z / v.w, 1)
// convert window to camera
#define W2C(v) vec4(v.x * v.w / winSize.x, v.y * v.w / winSize.y, v.z * v.w, 1)
void emitSegment(vec4 p0, vec4 p1) {
gl_Position = p0;
EmitVertex();
gl_Position = p1;
EmitVertex();
EndPrimitive();
}
void emitTriangle(vec4 p1, vec4 p2, vec4 p3) {
gl_Position = p1;
EmitVertex();
gl_Position = p2;
EmitVertex();
gl_Position = p3;
EmitVertex();
EndPrimitive();
}
"""
LIB_GLSL = BASE_LIB_GLSL
GEOM_GLSL = """
uniform float viewportDrawingScale;
layout(lines) in;
layout(triangle_strip, max_vertices = 4) out;
void main() {
// default setup for macro to work
vec4 clip2win = matCLIP2WIN();
vec4 win2clip = matWIN2CLIP();
vec2 EDGE_DIR;
vec4 p0 = gl_in[0].gl_Position, p1 = gl_in[1].gl_Position;
do_edge_verts(p0, p1);
EndPrimitive();
}
"""
FRAG_GLSL = """
uniform vec4 color;
uniform float lineWidth;
in float smoothline;
out vec4 fragColor;
void main() {
vec2 co = gl_FragCoord.xy;
fragColor = color;
if (lineSmooth) {
fragColor.a *= clamp((lineWidth + SMOOTH_WIDTH) * 0.5 - abs(smoothline), 0.0, 1.0);
}
}
"""
def __init__(self):
# NB: libcode arg doesn't work
# TODO: rename to .shader
self.prog = GPUShader(
vertexcode=self.VERT_GLSL,
fragcode=self.FRAG_GLSL,
@@ -120,27 +284,29 @@ class BaseShader:
self.prog.bind()
def glenable(self):
bgl.glEnable(bgl.GL_BLEND)
bgl.glBlendFunc(bgl.GL_SRC_ALPHA, bgl.GL_ONE_MINUS_SRC_ALPHA)
bgl.glBlendEquation(bgl.GL_FUNC_ADD)
# bgl.glEnable(bgl.GL_DEPTH_TEST)
# bgl.glDepthFunc(bgl.GL_LEQUAL)
# bgl.glDepthMask(True)
gpu.state.blend_set("ALPHA")
gpu.state.depth_test_set("LESS_EQUAL")
def uniform_region(self, ctx):
region = ctx.region
region3d = ctx.region_data
try:
self.prog.uniform_float("viewMatrix", region3d.perspective_matrix)
except ValueError: # unused uniform
pass
try:
self.prog.uniform_float("winSize", (region.width / 2, region.height / 2))
except ValueError: # unused uniform
pass
uniform_floats = {
"viewMatrix": region3d.perspective_matrix,
"winsize": (region.width, region.height),
"lineWidth": 2.5,
}
for name, value in uniform_floats.items():
try:
self.prog.uniform_float(name, value)
# TODO: shouldn't just try'n'catch them
# because they may indicate errors in code
except ValueError: # unused uniform
pass
# TODO: add smoothing if this shades is going to be used
class BaseLinesShader(BaseShader):
"""Draws line segments with gaps around vertices at endpoints"""
@@ -185,7 +351,6 @@ class BaseLinesShader(BaseShader):
def glenable(self):
super().glenable()
bgl.glEnable(bgl.GL_LINE_SMOOTH)
class GizmoShader(BaseShader):
@@ -207,6 +372,7 @@ class GizmoShader(BaseShader):
"""
# TODO: add smoothing if this shader is going to be used
class DotsGizmoShader(GizmoShader):
"""Draws circles of radius 1 around points"""
@@ -274,24 +440,26 @@ class ExtrusionGuidesShader(GizmoShader):
uniform mat4 ModelViewProjectionMatrix;
layout(lines) in;
layout(line_strip, max_vertices=10) out;
layout(triangle_strip, max_vertices=MAX_POINTS) out;
void main() {
vec4 p0 = gl_in[0].gl_Position, p1 = gl_in[1].gl_Position;
vec4 p0w = C2W(p0), p1w = C2W(p1);
// default setup for macro to work
vec2 EDGE_DIR;
emitSegment(p0, p1);
vec4 p0 = gl_in[0].gl_Position, p1 = gl_in[1].gl_Position;
do_edge_verts(p0, p1);
EndPrimitive();
vec4 bx = ModelViewProjectionMatrix[0] * CROSS_SIZE;
vec4 by = ModelViewProjectionMatrix[1] * CROSS_SIZE;
emitSegment(p0 - bx, p0 + bx);
emitSegment(p0 - by, p0 + by);
emitSegment(p1 - bx, p1 + bx);
emitSegment(p1 - by, p1 + by);
do_edge_verts(p0 - bx, p0 + bx);
EndPrimitive();
do_edge_verts(p0 - by, p0 + by);
EndPrimitive();
do_edge_verts(p1 - bx, p1 + bx);
EndPrimitive();
do_edge_verts(p1 - by, p1 + by);
EndPrimitive();
}
"""
def glenable(self):
super().glenable()
bgl.glEnable(bgl.GL_LINE_SMOOTH)
@@ -17,12 +17,15 @@
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import os
import bpy
import uuid
import shutil
import ntpath
import pystache
import urllib.parse
import xml.etree.ElementTree as ET
import blenderbim.tool as tool
from shutil import copy
import ifcopenshell.util.geolocation
from xml.dom import minidom
@@ -31,22 +34,29 @@ class SheetBuilder:
self.data_dir = None
self.scale = "NTS"
def create(self, sheet_path, titleblock_name):
def create(self, layout_path, titleblock_name):
root = ET.Element("svg")
root.attrib["xmlns"] = "http://www.w3.org/2000/svg"
root.attrib["xmlns:xlink"] = "http://www.w3.org/1999/xlink"
root.attrib["id"] = "root"
root.attrib["version"] = "1.1"
view_root = ET.parse(
os.path.join(self.data_dir, "templates", "titleblocks", titleblock_name + ".svg")
).getroot()
sheet_dir = os.path.dirname(layout_path)
ootb_titleblock_path = os.path.join(self.data_dir, "templates", "titleblocks", titleblock_name + ".svg")
titleblock_path = tool.Ifc.resolve_uri(tool.Drawing.get_default_titleblock_path(titleblock_name))
os.makedirs(sheet_dir, exist_ok=True)
os.makedirs(os.path.dirname(titleblock_path), exist_ok=True)
if not os.path.exists(titleblock_path):
shutil.copy(ootb_titleblock_path, titleblock_path)
view_root = ET.parse(titleblock_path).getroot()
view_width = self.convert_to_mm(view_root.attrib.get("width"))
view_height = self.convert_to_mm(view_root.attrib.get("height"))
view = ET.SubElement(root, "g")
view.attrib["data-type"] = "titleblock"
titleblock = ET.SubElement(view, "image")
titleblock.attrib["xlink:href"] = "../templates/titleblocks/" + titleblock_name + ".svg"
titleblock.attrib["xlink:href"] = os.path.relpath(titleblock_path, sheet_dir)
titleblock.attrib["x"] = "0"
titleblock.attrib["y"] = "0"
titleblock.attrib["width"] = str(view_width)
@@ -56,26 +66,25 @@ class SheetBuilder:
root.attrib["height"] = "{}mm".format(view_height)
root.attrib["viewBox"] = "0 0 {} {}".format(view_width, view_height)
with open(sheet_path, "w") as f:
with open(layout_path, "w") as f:
f.write(minidom.parseString(ET.tostring(root)).toprettyxml(indent=" "))
def add_drawing(self, reference, drawing, sheet):
filename = drawing.Name
sheet_name = os.path.splitext(os.path.basename(tool.Drawing.get_document_uri(sheet)))[0]
sheet_dir = os.path.join(self.data_dir, "sheets")
drawing_dir = os.path.join(self.data_dir, "diagrams")
sheet_path = os.path.join(sheet_dir, sheet_name + ".svg")
drawing_path = os.path.join(drawing_dir, filename + ".svg")
underlay_path = os.path.join(drawing_dir, filename + "-underlay.png")
layout_path = tool.Drawing.get_document_uri(sheet, "LAYOUT")
layout_dir = os.path.dirname(layout_path)
if not os.path.isfile(sheet_path):
drawing_path = tool.Drawing.get_document_uri(tool.Drawing.get_drawing_reference(drawing))
underlay_path = os.path.splitext(drawing_path)[0] + "-underlay.png"
if not os.path.exists(layout_path) or not os.path.exists(drawing_path):
raise FileNotFoundError
ET.register_namespace("", "http://www.w3.org/2000/svg")
ET.register_namespace("xlink", "http://www.w3.org/1999/xlink")
sheet_tree = ET.parse(sheet_path)
sheet_root = sheet_tree.getroot()
layout_tree = ET.parse(layout_path)
layout_root = layout_tree.getroot()
view_tree = ET.parse(drawing_path)
view_root = view_tree.getroot()
@@ -83,7 +92,7 @@ class SheetBuilder:
# The view is placed into a group with a background image element.
# Although the foreground SVG already has a background, it is duplicated
# here to accommodate browsers which do not nest images.
view = ET.SubElement(sheet_root, "g")
view = ET.SubElement(layout_root, "g")
view.attrib["data-type"] = "drawing"
view.attrib["data-id"] = str(reference.id())
view.attrib["data-drawing"] = drawing.GlobalId
@@ -93,7 +102,7 @@ class SheetBuilder:
if os.path.isfile(underlay_path):
background = ET.SubElement(view, "image")
background.attrib["data-type"] = "background"
background.attrib["xlink:href"] = os.path.relpath(underlay_path, sheet_dir)
background.attrib["xlink:href"] = os.path.relpath(underlay_path, layout_dir)
background.attrib["x"] = "30"
background.attrib["y"] = "30"
background.attrib["width"] = str(view_width)
@@ -102,100 +111,126 @@ class SheetBuilder:
if os.path.isfile(drawing_path):
foreground = ET.SubElement(view, "image")
foreground.attrib["data-type"] = "foreground"
foreground.attrib["xlink:href"] = os.path.relpath(drawing_path, sheet_dir)
foreground.attrib["xlink:href"] = os.path.relpath(drawing_path, layout_dir)
foreground.attrib["x"] = "30"
foreground.attrib["y"] = "30"
foreground.attrib["width"] = str(view_width)
foreground.attrib["height"] = str(view_height)
self.add_view_title(30, view_height + 35, view)
sheet_tree.write(sheet_path)
self.add_view_title(30, view_height + 35, view, layout_dir)
layout_tree.write(layout_path)
def remove_drawing(self, reference, sheet):
ET.register_namespace("", "http://www.w3.org/2000/svg")
sheet_path = tool.Drawing.get_document_uri(sheet)
sheet_tree = ET.parse(sheet_path)
sheet_root = sheet_tree.getroot()
layout_path = tool.Drawing.get_document_uri(sheet, "LAYOUT")
layout_tree = ET.parse(layout_path)
layout_root = layout_tree.getroot()
for g in sheet_root.findall("{http://www.w3.org/2000/svg}g"):
for g in layout_root.findall("{http://www.w3.org/2000/svg}g"):
if g.attrib.get("data-id") == str(reference.id()):
sheet_root.remove(g)
layout_root.remove(g)
break
sheet_tree.write(sheet_path)
layout_tree.write(layout_path)
def add_schedule(self, reference, schedule, sheet):
view_path = tool.Drawing.get_document_uri(schedule)
view_path = tool.Drawing.get_path_with_ext(tool.Drawing.get_document_uri(schedule), "svg")
if not os.path.exists(view_path):
tool.Drawing.create_svg_schedule(schedule)
schedule_name = os.path.splitext(os.path.basename(view_path))[0]
sheet_path = tool.Drawing.get_document_uri(sheet)
layout_path = tool.Drawing.get_document_uri(sheet, "LAYOUT")
layout_dir = os.path.dirname(layout_path)
ET.register_namespace("", "http://www.w3.org/2000/svg")
ET.register_namespace("xlink", "http://www.w3.org/1999/xlink")
sheet_tree = ET.parse(sheet_path)
sheet_root = sheet_tree.getroot()
layout_tree = ET.parse(layout_path)
layout_root = layout_tree.getroot()
view_tree = ET.parse(view_path)
view_root = view_tree.getroot()
view_width = self.convert_to_mm(view_root.attrib.get("width"))
view_height = self.convert_to_mm(view_root.attrib.get("height"))
view = ET.SubElement(sheet_root, "g")
view = ET.SubElement(layout_root, "g")
view.attrib["data-type"] = "schedule"
view.attrib["data-id"] = str(reference.id())
view.attrib["data-schedule"] = str(schedule.id())
foreground = ET.SubElement(view, "image")
foreground.attrib["data-type"] = "table"
foreground.attrib["xlink:href"] = "../schedules/{}.svg".format(schedule_name)
foreground.attrib["xlink:href"] = os.path.relpath(view_path, layout_dir)
foreground.attrib["x"] = "30"
foreground.attrib["y"] = "30"
foreground.attrib["width"] = str(view_width)
foreground.attrib["height"] = str(view_height)
self.add_view_title(30, view_height + 35, view)
sheet_tree.write(sheet_path)
self.add_view_title(30, view_height + 35, view, layout_dir)
layout_tree.write(layout_path)
def add_view_title(self, x, y, parent):
title_tree = ET.parse(os.path.join(self.data_dir, "templates", "view-title.svg"))
def add_view_title(self, x, y, parent, layout_dir):
title_path = os.path.join(layout_dir, "assets", "view-title.svg")
os.makedirs(os.path.dirname(title_path), exist_ok=True)
if not os.path.exists(title_path):
ootb_title = os.path.join(bpy.context.scene.BIMProperties.data_dir, "assets", "view-title.svg")
shutil.copy(ootb_title, title_path)
title_tree = ET.parse(title_path)
title_root = title_tree.getroot()
title = ET.SubElement(parent, "image")
title.attrib["data-type"] = "view-title"
title.attrib["xlink:href"] = "../templates/view-title.svg"
title.attrib["xlink:href"] = os.path.relpath(title_path, layout_dir)
title.attrib["x"] = str(x)
title.attrib["y"] = str(y)
title.attrib["width"] = str(self.convert_to_mm(title_root.attrib.get("width")))
title.attrib["height"] = str(self.convert_to_mm(title_root.attrib.get("height")))
def build(self, sheet):
sheet_name = os.path.splitext(os.path.basename(tool.Drawing.get_document_uri(sheet)))[0]
os.makedirs(os.path.join(self.data_dir, "build", sheet_name), exist_ok=True)
self.references = {"SHEET": None, "RASTER": []}
sheet_path = os.path.join(self.data_dir, "sheets", f"{sheet_name}.svg")
layout_path = tool.Drawing.get_document_uri(sheet, "LAYOUT")
self.layout_dir = os.path.dirname(layout_path)
sheet_path = tool.Ifc.resolve_uri(tool.Drawing.get_default_sheet_path(sheet[0], sheet.Name))
self.sheets_dir = os.path.dirname(sheet_path)
os.makedirs(self.sheets_dir, exist_ok=True)
ET.register_namespace("", "http://www.w3.org/2000/svg")
ET.register_namespace("xlink", "http://www.w3.org/1999/xlink")
tree = ET.parse(sheet_path)
tree = ET.parse(layout_path)
root = tree.getroot()
self.defs = ET.Element("defs")
root.append(self.defs)
self.build_titleblock(root, sheet)
self.build_drawings(root, sheet)
self.build_schedules(root)
self.build_schedules(root, sheet)
with open(os.path.join(self.data_dir, "build", sheet_name, f"{sheet_name}.svg"), "wb") as output:
with open(sheet_path, "wb") as output:
tree.write(output)
self.references["SHEET"] = sheet_path
return self.references
def build_titleblock(self, root, sheet):
titleblock = root.findall('{http://www.w3.org/2000/svg}g[@data-type="titleblock"]')[0]
image = titleblock.findall("{http://www.w3.org/2000/svg}image")[0]
titleblock.append(self.parse_embedded_svg(image, sheet.get_info()))
g = self.parse_embedded_svg(image, sheet.get_info())
grid_north = ifcopenshell.util.geolocation.get_grid_north(tool.Ifc.get()) * -1
true_north = ifcopenshell.util.geolocation.get_true_north(tool.Ifc.get()) * -1
for north in g.iterfind('.//{http://www.w3.org/2000/svg}g[@data-type="grid-north"]'):
north.attrib["transform"] = f"rotate({grid_north})"
for north in g.iterfind('.//{http://www.w3.org/2000/svg}g[@data-type="true-north"]'):
north.attrib["transform"] = f"rotate({true_north})"
titleblock.append(g)
titleblock.remove(image)
def build_drawings(self, root, sheet):
sheet_name = os.path.splitext(os.path.basename(tool.Drawing.get_document_uri(sheet)))[0]
for view in root.findall('{http://www.w3.org/2000/svg}g[@data-type="drawing"]'):
reference = tool.Ifc.get().by_id(int(view.attrib["data-id"]))
drawing = tool.Ifc.get().by_id(view.attrib["data-drawing"])
@@ -218,12 +253,15 @@ class SheetBuilder:
view.append(self.parse_embedded_svg(foreground, {}))
if background is not None:
background_path = os.path.join(self.data_dir, "sheets", self.get_href(background))
copy(background_path, os.path.join(self.data_dir, "build", sheet_name))
background_path = os.path.join(self.layout_dir, self.get_href(background))
raster_path = os.path.join(self.sheets_dir, os.path.basename(background_path))
shutil.copy(background_path, raster_path)
self.references["RASTER"].append(raster_path)
if view_title is not None:
foreground_path = self.get_href(foreground)
data = reference.get_info()
data.update({"Sheet" + k: v for k, v in sheet.get_info().items()})
if not data["Name"]:
data["Name"] = ntpath.basename(foreground_path)[0:-4]
data["Scale"] = tool.Drawing.get_drawing_human_scale(drawing)
@@ -232,7 +270,7 @@ class SheetBuilder:
for image in images:
view.remove(image)
def build_schedules(self, root):
def build_schedules(self, root, sheet):
for view in root.findall('{http://www.w3.org/2000/svg}g[@data-type="schedule"]'):
reference = tool.Ifc.get().by_id(int(view.attrib["data-id"]))
schedule = tool.Ifc.get().by_id(int(view.attrib["data-schedule"]))
@@ -254,6 +292,7 @@ class SheetBuilder:
if view_title is not None:
path = self.get_href(table)
data = reference.get_info()
data.update({"Sheet" + k: v for k, v in sheet.get_info().items()})
if not data["Name"]:
data["Name"] = schedule.Name or "Unnamed"
view.append(self.parse_embedded_svg(view_title, data))
@@ -269,8 +308,22 @@ class SheetBuilder:
group.attrib["transform"] = "translate({},{})".format(
self.convert_to_mm(image.attrib.get("x")), self.convert_to_mm(image.attrib.get("y"))
)
# Convert viewBox into a clip path
clip_id = str(uuid.uuid4())
group.attrib["clip-path"] = f"url(#{clip_id})"
clip_path = ET.Element("clipPath")
clip_path.attrib["id"] = clip_id
rect = ET.Element("rect")
rect.attrib["x"] = "0"
rect.attrib["y"] = "0"
rect.attrib["width"] = str(self.convert_to_mm(image.attrib.get("width")))
rect.attrib["height"] = str(self.convert_to_mm(image.attrib.get("height")))
clip_path.append(rect)
self.defs.append(clip_path)
svg_path = self.get_href(image)
with open(os.path.join(self.data_dir, "sheets", svg_path), "r") as template:
with open(os.path.join(self.layout_dir, svg_path), "r") as template:
embedded = ET.fromstring(pystache.render(template.read(), data))
# viewBox should not be nested
embedded.attrib["viewBox"] = ""
@@ -280,9 +333,45 @@ class SheetBuilder:
for image in images:
new_href = ntpath.basename(image.attrib.get("{http://www.w3.org/1999/xlink}href"))
image.attrib["{http://www.w3.org/1999/xlink}href"] = new_href
group.append(embedded)
for child in embedded:
if "namedview" in child.tag:
continue
group.append(child)
return group
def change_titleblock(self, sheet, titleblock_name):
ootb_titleblock_path = os.path.join(self.data_dir, "templates", "titleblocks", titleblock_name + ".svg")
titleblock_path = tool.Drawing.get_default_titleblock_path(titleblock_name)
sheet_path = tool.Drawing.get_document_uri(sheet, "LAYOUT")
sheet_dir = os.path.dirname(sheet_path)
os.makedirs(sheet_dir, exist_ok=True)
os.makedirs(os.path.dirname(titleblock_path), exist_ok=True)
if not os.path.exists(titleblock_path):
shutil.copy(ootb_titleblock_path, titleblock_path)
ET.register_namespace("", "http://www.w3.org/2000/svg")
ET.register_namespace("xlink", "http://www.w3.org/1999/xlink")
view_root = ET.parse(ootb_titleblock_path).getroot()
view_width = self.convert_to_mm(view_root.attrib.get("width"))
view_height = self.convert_to_mm(view_root.attrib.get("height"))
sheet_tree = ET.parse(sheet_path)
root = sheet_tree.getroot()
titleblock = sheet_tree.findall('{http://www.w3.org/2000/svg}g[@data-type="titleblock"]')[0]
image = titleblock.findall("{http://www.w3.org/2000/svg}image[@{http://www.w3.org/1999/xlink}href]")[0]
image.attrib["{http://www.w3.org/1999/xlink}href"] = os.path.relpath(titleblock_path, sheet_dir)
image.attrib["width"] = str(view_width)
image.attrib["height"] = str(view_height)
root.attrib["width"] = "{}mm".format(view_width)
root.attrib["height"] = "{}mm".format(view_height)
root.attrib["viewBox"] = "0 0 {} {}".format(view_width, view_height)
sheet_tree.write(sheet_path)
def convert_to_mm(self, value):
# CSS is what defines these possibilities
# https://www.w3.org/TR/SVG/refs.html#ref-css-values-3
File diff suppressed because it is too large Load Diff

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