Compare commits

...

27 Commits

Author SHA1 Message Date
Bruno Perdigão a531bd8c1f Fix. Uninstall Bonsai Decorators if they are not selected at start 2025-05-30 16:20:42 -03:00
Andrej f44769c138 ifc2sql - add example for should_expand 2025-05-30 18:13:18 +05:00
Andrej a6e3cc667c ifc2sql - add missing should_get_inverses description 2025-05-30 18:13:17 +05:00
Andrej 59b17fc593 ifcopenshell_wrapper.pyi 2025-05-30 18:13:17 +05:00
Andrej fb1b5bea51 ifcopenshell.file - use IFC4X3 instead of IFC4X3_ADD2 for simplicity 2025-05-30 18:13:11 +05:00
Andrej e8aa508405 Add 'advanced' optional ifcopenshell dependencies
To list all other optional dependencies.
2025-05-30 18:13:11 +05:00
Andrej 363083104b Add 'tabulate' to optional ifcopenshell dev dependencies
Used in test_rules and test_wall_opening.
2025-05-30 18:13:11 +05:00
Andrej 2798bf3b68 ifcopenshell.api.geometry to fail more gracefully with fake-bpy
If user has fake-bpy-module for type hints, it will fail with `ImportError: cannot import name 'Vector' from 'mathutils' (unknown location)` instead of `ModuleNotFoundError`.
2025-05-30 18:13:11 +05:00
Andrej e41c4bf7ae Separate mathutils tests 2025-05-30 18:13:11 +05:00
Andrej 3bdce4fbc3 Make zip strict argument optional to support Python 3.9 2025-05-30 18:13:11 +05:00
Andrej ad7473ff1f Make dataclasses args optional to support Python 3.9 #6725 2025-05-30 18:13:10 +05:00
Andrej 3d77fc7c7d ifccityjson - more specific depdendency
API in cjio 0.10 changed (`load` method was moved elsewhere) and for now we just specify that we require older cjio version.
2025-05-30 18:13:10 +05:00
Andrej 92a222cda7 typing 2025-05-30 18:13:10 +05:00
Andrej cc60794293 edit style for rep item - fallback in case user has no styles #6764
Or if style was removed in the process of editing. And it seems now UI will never appear broken that way.

Previously - https://files.catbox.moe/exhkln.png
Currently - https://files.catbox.moe/r36be8.png
2025-05-30 17:01:11 +05:00
Kristoffer Andersen c7b4033f6f Update conda_build_config.yaml
try to fix conda daily
2025-05-29 21:18:19 +02:00
Andrej 1ab288de0b ifc5d - fix <py3.11 compatibility issue (6499ac4) 2025-05-29 19:14:43 +05:00
Andrej 48f62c0b5b ifcopenshell_wrapper.pyi - update from the latest build
I guess previously I used some of my local builds, so some symbols were missing.
2025-05-29 19:14:43 +05:00
Andrej 5fc549453c validate_stub - add wrappers constants 2025-05-29 19:14:43 +05:00
Andrej bd5f5a30b3 validate_stub - also skip object 2025-05-29 19:14:43 +05:00
Andrej baf3edca13 ifcopenshell.validate - fix Python 3.9 support (25eecde) 2025-05-29 19:14:42 +05:00
Andrej c23e973575 Fix moved cost.remove_cost_item_value (b272210) 2025-05-29 19:14:42 +05:00
Andrej a5801e439f edit_attributes - fix Python 3.9 support (39fd9ce) 2025-05-29 19:14:42 +05:00
Andrej 4e65c33bae Bonsai make - use ifcjson master instead of fork
https://github.com/IFCJSON-Team/IFC2JSON_python/pull/3 got merged
2025-05-29 19:14:42 +05:00
Andrej c3652a56ee dev_environment.py #6754 2025-05-29 19:14:42 +05:00
Andrej e1b3b68a8b gitignore jquery.min.js (320a4cf) 2025-05-29 19:14:42 +05:00
Andrej dbce4c5d98 bcf - add missing requests dependency 2025-05-29 19:14:42 +05:00
Andrej 0663c5130a typing 2025-05-29 19:14:42 +05:00
76 changed files with 717 additions and 257 deletions
+4
View File
@@ -136,3 +136,7 @@ jobs:
cd ../ifcpatch && make test
pip install -e ../ifctester --no-deps
cd ../ifctester && make test
# Run mathutils related tests at the end to ensure no other code is relying on mathutils.
cd ../ifcopenshell-python
pip install mathutils
make test-mathutils
+1
View File
@@ -86,6 +86,7 @@ src/bonsai/bonsai/bim/data/build/
src/bonsai/bonsai/bim/data/gantt/index.html
src/bonsai/bonsai/bim/data/gantt/jsgantt.js
src/bonsai/bonsai/bim/data/gantt/jsgantt.css
src/bonsai/bonsai/bim/data/webui/static/js/jquery.min.js
src/bonsai/drawings
src/bonsai/layouts
+5 -2
View File
@@ -1,3 +1,6 @@
python:
- 3.12
occt:
- 7.8.1
@@ -28,7 +31,7 @@ hdf5:
libboost_devel:
- '1.86'
libxml2:
- '2'
- 2.13
mpfr:
- '4'
gmp:
@@ -52,4 +55,4 @@ MACOSX_DEPLOYMENT_TARGET: # [osx]
- 10.13 # [osx and x86_64]
CONDA_BUILD_SYSROOT: # [osx]
- "/Users/runner/work/MacOSX10.13.sdk" # [osx and x86_64]
- "/Users/runner/work/MacOSX10.13.sdk" # [osx and x86_64]
+12 -8
View File
@@ -14,11 +14,15 @@
# Currently extensions support for v2 is only read-only.
import sys
from dataclasses import dataclass, field, fields
from typing import List, NamedTuple, Optional
@dataclass(slots=True, kw_only=True)
DATACLASS_KWARGS = {} if sys.version_info < (3, 10) else {"slots": True, "kw_only": True}
@dataclass(**DATACLASS_KWARGS)
class ExtensionsPriorities:
class Meta:
global_type = False
@@ -35,7 +39,7 @@ class ExtensionsPriorities:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class ExtensionsSnippetTypes:
class Meta:
global_type = False
@@ -52,7 +56,7 @@ class ExtensionsSnippetTypes:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class ExtensionsStages:
class Meta:
global_type = False
@@ -69,7 +73,7 @@ class ExtensionsStages:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class ExtensionsTopicLabels:
class Meta:
global_type = False
@@ -86,7 +90,7 @@ class ExtensionsTopicLabels:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class ExtensionsTopicStatuses:
class Meta:
global_type = False
@@ -103,7 +107,7 @@ class ExtensionsTopicStatuses:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class ExtensionsTopicTypes:
class Meta:
global_type = False
@@ -120,7 +124,7 @@ class ExtensionsTopicTypes:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class ExtensionsUsers:
class Meta:
global_type = False
@@ -137,7 +141,7 @@ class ExtensionsUsers:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class Extensions:
topic_types: Optional[ExtensionsTopicTypes] = field(
default=None,
+13 -10
View File
@@ -1,10 +1,13 @@
import sys
from dataclasses import dataclass, field
from typing import List, Optional
from xsdata.models.datatype import XmlDateTime
DATACLASS_KWARGS = {} if sys.version_info < (3, 10) else {"slots": True, "kw_only": True}
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class BimSnippet:
reference: str = field(
metadata={
@@ -38,7 +41,7 @@ class BimSnippet:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class CommentViewpoint:
class Meta:
global_type = False
@@ -53,7 +56,7 @@ class CommentViewpoint:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class HeaderFile:
class Meta:
global_type = False
@@ -109,7 +112,7 @@ class HeaderFile:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class TopicDocumentReference:
class Meta:
global_type = False
@@ -147,7 +150,7 @@ class TopicDocumentReference:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class TopicRelatedTopic:
class Meta:
global_type = False
@@ -162,7 +165,7 @@ class TopicRelatedTopic:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class ViewPoint:
viewpoint: Optional[str] = field(
default=None,
@@ -198,7 +201,7 @@ class ViewPoint:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class Comment:
date: XmlDateTime = field(
metadata={
@@ -258,7 +261,7 @@ class Comment:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class Header:
file: List[HeaderFile] = field(
default_factory=list,
@@ -271,7 +274,7 @@ class Header:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class Topic:
reference_link: List[str] = field(
default_factory=list,
@@ -425,7 +428,7 @@ class Topic:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class Markup:
header: Optional[Header] = field(
default=None,
+5 -2
View File
@@ -1,8 +1,11 @@
import sys
from dataclasses import dataclass, field
from typing import Optional
DATACLASS_KWARGS = {} if sys.version_info < (3, 10) else {"slots": True, "kw_only": True}
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class Project:
name: Optional[str] = field(
default=None,
@@ -21,7 +24,7 @@ class Project:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class ProjectExtension:
project: Optional[Project] = field(
default=None,
+4 -1
View File
@@ -1,8 +1,11 @@
import sys
from dataclasses import dataclass, field
from typing import Optional
DATACLASS_KWARGS = {} if sys.version_info < (3, 10) else {"slots": True, "kw_only": True}
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class Version:
detailed_version: Optional[str] = field(
default=None,
+21 -18
View File
@@ -1,14 +1,17 @@
import sys
from dataclasses import dataclass, field
from enum import Enum
from typing import List, Optional
DATACLASS_KWARGS = {} if sys.version_info < (3, 10) else {"slots": True, "kw_only": True}
class BitmapFormat(Enum):
PNG = "PNG"
JPG = "JPG"
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class Component:
originating_system: Optional[str] = field(
default=None,
@@ -35,7 +38,7 @@ class Component:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class Direction:
x: float = field(
metadata={
@@ -60,7 +63,7 @@ class Direction:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class Point:
x: float = field(
metadata={
@@ -85,7 +88,7 @@ class Point:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class ViewSetupHints:
spaces_visible: Optional[bool] = field(
default=None,
@@ -110,7 +113,7 @@ class ViewSetupHints:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class ClippingPlane:
location: Point = field(
metadata={
@@ -128,7 +131,7 @@ class ClippingPlane:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class ComponentColoringColor:
class Meta:
global_type = False
@@ -151,7 +154,7 @@ class ComponentColoringColor:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class ComponentSelection:
component: List[Component] = field(
default_factory=list,
@@ -163,7 +166,7 @@ class ComponentSelection:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class ComponentVisibilityExceptions:
class Meta:
global_type = False
@@ -178,7 +181,7 @@ class ComponentVisibilityExceptions:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class Line:
start_point: Point = field(
metadata={
@@ -196,7 +199,7 @@ class Line:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class OrthogonalCamera:
"""
Attributes
@@ -236,7 +239,7 @@ class OrthogonalCamera:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class PerspectiveCamera:
"""
Attributes
@@ -281,7 +284,7 @@ class PerspectiveCamera:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class VisualizationInfoBitmap:
class Meta:
global_type = False
@@ -330,7 +333,7 @@ class VisualizationInfoBitmap:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class ComponentColoring:
color: List[ComponentColoringColor] = field(
default_factory=list,
@@ -342,7 +345,7 @@ class ComponentColoring:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class ComponentVisibility:
exceptions: Optional[ComponentVisibilityExceptions] = field(
default=None,
@@ -360,7 +363,7 @@ class ComponentVisibility:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class VisualizationInfoClippingPlanes:
class Meta:
global_type = False
@@ -374,7 +377,7 @@ class VisualizationInfoClippingPlanes:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class VisualizationInfoLines:
class Meta:
global_type = False
@@ -389,7 +392,7 @@ class VisualizationInfoLines:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class Components:
view_setup_hints: Optional[ViewSetupHints] = field(
default=None,
@@ -421,7 +424,7 @@ class Components:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class VisualizationInfo:
"""
VisualizationInfo documentation.
+1 -1
View File
@@ -21,7 +21,7 @@ import http.server
import os
import tempfile
import time
import urllib
import urllib.parse
import uuid
import webbrowser
from re import A
+6 -3
View File
@@ -1,8 +1,11 @@
import sys
from dataclasses import dataclass, field
from typing import List, Optional
DATACLASS_KWARGS = {} if sys.version_info < (3, 10) else {"slots": True, "kw_only": True}
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class Document:
filename: str = field(
metadata={
@@ -34,7 +37,7 @@ class Document:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class DocumentInfoDocuments:
class Meta:
global_type = False
@@ -49,7 +52,7 @@ class DocumentInfoDocuments:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class DocumentInfo:
documents: Optional[DocumentInfoDocuments] = field(
default=None,
+11 -8
View File
@@ -1,8 +1,11 @@
import sys
from dataclasses import dataclass, field
from typing import List, Optional
DATACLASS_KWARGS = {} if sys.version_info < (3, 10) else {"slots": True, "kw_only": True}
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class ExtensionsPriorities:
class Meta:
global_type = False
@@ -19,7 +22,7 @@ class ExtensionsPriorities:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class ExtensionsSnippetTypes:
class Meta:
global_type = False
@@ -36,7 +39,7 @@ class ExtensionsSnippetTypes:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class ExtensionsStages:
class Meta:
global_type = False
@@ -53,7 +56,7 @@ class ExtensionsStages:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class ExtensionsTopicLabels:
class Meta:
global_type = False
@@ -70,7 +73,7 @@ class ExtensionsTopicLabels:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class ExtensionsTopicStatuses:
class Meta:
global_type = False
@@ -87,7 +90,7 @@ class ExtensionsTopicStatuses:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class ExtensionsTopicTypes:
class Meta:
global_type = False
@@ -104,7 +107,7 @@ class ExtensionsTopicTypes:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class ExtensionsUsers:
class Meta:
global_type = False
@@ -121,7 +124,7 @@ class ExtensionsUsers:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class Extensions:
topic_types: Optional[ExtensionsTopicTypes] = field(
default=None,
+20 -17
View File
@@ -1,10 +1,13 @@
import sys
from dataclasses import dataclass, field
from typing import List, Optional
from xsdata.models.datatype import XmlDateTime
DATACLASS_KWARGS = {} if sys.version_info < (3, 10) else {"slots": True, "kw_only": True}
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class BimSnippet:
reference: str = field(
metadata={
@@ -44,7 +47,7 @@ class BimSnippet:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class CommentViewpoint:
class Meta:
global_type = False
@@ -59,7 +62,7 @@ class CommentViewpoint:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class DocumentReference:
document_guid: Optional[str] = field(
default=None,
@@ -100,7 +103,7 @@ class DocumentReference:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class File:
filename: Optional[str] = field(
default=None,
@@ -157,7 +160,7 @@ class File:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class TopicLabels:
class Meta:
global_type = False
@@ -174,7 +177,7 @@ class TopicLabels:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class TopicReferenceLinks:
class Meta:
global_type = False
@@ -191,7 +194,7 @@ class TopicReferenceLinks:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class TopicRelatedTopicsRelatedTopic:
class Meta:
global_type = False
@@ -206,7 +209,7 @@ class TopicRelatedTopicsRelatedTopic:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class ViewPoint:
viewpoint: Optional[str] = field(
default=None,
@@ -246,7 +249,7 @@ class ViewPoint:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class Comment:
date: XmlDateTime = field(
metadata={
@@ -312,7 +315,7 @@ class Comment:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class HeaderFiles:
class Meta:
global_type = False
@@ -327,7 +330,7 @@ class HeaderFiles:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class TopicDocumentReferences:
class Meta:
global_type = False
@@ -342,7 +345,7 @@ class TopicDocumentReferences:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class TopicRelatedTopics:
class Meta:
global_type = False
@@ -357,7 +360,7 @@ class TopicRelatedTopics:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class TopicViewpoints:
class Meta:
global_type = False
@@ -372,7 +375,7 @@ class TopicViewpoints:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class Header:
files: Optional[HeaderFiles] = field(
default=None,
@@ -384,7 +387,7 @@ class Header:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class TopicComments:
class Meta:
global_type = False
@@ -399,7 +402,7 @@ class TopicComments:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class Topic:
reference_links: Optional[TopicReferenceLinks] = field(
default=None,
@@ -596,7 +599,7 @@ class Topic:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class Markup:
header: Optional[Header] = field(
default=None,
+5 -2
View File
@@ -1,8 +1,11 @@
import sys
from dataclasses import dataclass, field
from typing import Optional
DATACLASS_KWARGS = {} if sys.version_info < (3, 10) else {"slots": True, "kw_only": True}
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class Project:
name: Optional[str] = field(
default=None,
@@ -25,7 +28,7 @@ class Project:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class ProjectInfo:
project: Project = field(
metadata={
+4 -1
View File
@@ -1,7 +1,10 @@
import sys
from dataclasses import dataclass, field
DATACLASS_KWARGS = {} if sys.version_info < (3, 10) else {"slots": True, "kw_only": True}
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class Version:
version_id: str = field(
metadata={
+24 -20
View File
@@ -1,14 +1,18 @@
import sys
from dataclasses import dataclass, field
from enum import Enum
from typing import List, Optional
DATACLASS_KWARGS = {} if sys.version_info < (3, 10) else {"slots": True, "kw_only": True}
class BitmapFormat(Enum):
PNG = "png"
JPG = "jpg"
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class Component:
originating_system: Optional[str] = field(
default=None,
@@ -39,7 +43,7 @@ class Component:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class Direction:
x: float = field(
metadata={
@@ -64,7 +68,7 @@ class Direction:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class Point:
x: float = field(
metadata={
@@ -89,7 +93,7 @@ class Point:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class ViewSetupHints:
spaces_visible: bool = field(
default=False,
@@ -114,7 +118,7 @@ class ViewSetupHints:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class Bitmap:
format: BitmapFormat = field(
metadata={
@@ -162,7 +166,7 @@ class Bitmap:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class ClippingPlane:
location: Point = field(
metadata={
@@ -180,7 +184,7 @@ class ClippingPlane:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class ComponentColoringColorComponents:
class Meta:
global_type = False
@@ -195,7 +199,7 @@ class ComponentColoringColorComponents:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class ComponentSelection:
component: List[Component] = field(
default_factory=list,
@@ -206,7 +210,7 @@ class ComponentSelection:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class ComponentVisibilityExceptions:
class Meta:
global_type = False
@@ -220,7 +224,7 @@ class ComponentVisibilityExceptions:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class Line:
start_point: Point = field(
metadata={
@@ -238,7 +242,7 @@ class Line:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class OrthogonalCamera:
"""
Attributes
@@ -288,7 +292,7 @@ class OrthogonalCamera:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class PerspectiveCamera:
"""
Attributes
@@ -344,7 +348,7 @@ class PerspectiveCamera:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class ComponentColoringColor:
class Meta:
global_type = False
@@ -366,7 +370,7 @@ class ComponentColoringColor:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class ComponentVisibility:
view_setup_hints: Optional[ViewSetupHints] = field(
default=None,
@@ -391,7 +395,7 @@ class ComponentVisibility:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class VisualizationInfoBitmaps:
class Meta:
global_type = False
@@ -405,7 +409,7 @@ class VisualizationInfoBitmaps:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class VisualizationInfoClippingPlanes:
class Meta:
global_type = False
@@ -419,7 +423,7 @@ class VisualizationInfoClippingPlanes:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class VisualizationInfoLines:
class Meta:
global_type = False
@@ -433,7 +437,7 @@ class VisualizationInfoLines:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class ComponentColoring:
color: List[ComponentColoringColor] = field(
default_factory=list,
@@ -444,7 +448,7 @@ class ComponentColoring:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class Components:
selection: Optional[ComponentSelection] = field(
default=None,
@@ -469,7 +473,7 @@ class Components:
)
@dataclass(slots=True, kw_only=True)
@dataclass(**DATACLASS_KWARGS)
class VisualizationInfo:
"""
VisualizationInfo documentation.
+1
View File
@@ -16,6 +16,7 @@ dependencies = [
"xsdata>=24.4",
"numpy",
"ifcopenshell",
"requests",
]
version = "0.0.0"
classifiers = [
+1 -2
View File
@@ -201,8 +201,7 @@ endif
cd build/bonsai/bim/data/gantt/ && wget https://raw.githubusercontent.com/jsGanttImproved/jsgantt-improved/master/dist/jsgantt.css
# Provides IFCJSON functionality
# TODO: replace with main repo if https://github.com/IFCJSON-Team/IFC2JSON_python/pull/3 is merged.
cd build && wget -O ifc2json.zip https://github.com/Moult/IFC2JSON_python/archive/refs/heads/feature-ios-v0.8.0.zip
cd build && wget -O ifc2json.zip https://github.com/IFCJSON-Team/IFC2JSON_python/archive/refs/heads/master.zip
cd build && unzip ifc2json.zip && rm ifc2json.zip
# IFCJSON doesn't have pyproject.toml, so we use python command.
cd build && . env/$(VENV_ACTIVATE) && cd IFC2JSON_python-*/file_converters && \
+5
View File
@@ -350,6 +350,11 @@ def load_post(scene):
aggregate_props = tool.Aggregate.get_aggregate_props()
nest_props = tool.Nest.get_nest_props()
model_props = tool.Model.get_model_props()
GeoreferenceDecorator.uninstall()
AggregateDecorator.uninstall()
NestDecorator.uninstall()
WallAxisDecorator.uninstall()
SlabDirectionDecorator.uninstall()
if georeference_props.should_visualise:
GeoreferenceDecorator.install(bpy.context)
if aggregate_props.aggregate_decorator:
@@ -2558,7 +2558,8 @@ class EnableEditingRepresentationItemStyle(bpy.types.Operator, tool.Ifc.Operator
ifc_file = tool.Ifc.get()
# set dropdown to currently active style
representation_item_id = props.active_item.ifc_definition_id
assert (active_item := props.active_item)
representation_item_id = active_item.ifc_definition_id
representation_item = ifc_file.by_id(representation_item_id)
style = tool.Style.get_representation_item_style(representation_item)
if style:
@@ -2577,8 +2578,14 @@ class EditRepresentationItemStyle(bpy.types.Operator, tool.Ifc.Operator):
props.is_editing_item_style = False
ifc_file = tool.Ifc.get()
surface_style = ifc_file.by_id(int(props.representation_item_style))
representation_item_id = props.active_item.ifc_definition_id
surface_style_id = tool.Blender.get_enum_safe(props, "representation_item_style")
if surface_style_id in (None, "-"):
surface_style = None
else:
surface_style = ifc_file.by_id(int(props.representation_item_style))
assert (active_item := props.active_item)
representation_item_id = active_item.ifc_definition_id
representation_item = ifc_file.by_id(representation_item_id)
tool.Style.assign_style_to_representation_item(representation_item, surface_style)
@@ -101,7 +101,9 @@ class MaterialsData:
for s in tool.Ifc.get().by_type("IfcPresentationStyle")
if (style_name := s.Name) is not None
]
return natsorted(results, key=lambda i: i[1])
results = natsorted(results, key=lambda i: i[1])
results.insert(0, ("-", "No Surface Style", ""))
return results
@classmethod
def material_styles_data(cls) -> dict[int, list[dict[str, Any]]]:
@@ -22,9 +22,9 @@ import numpy as np
from math import sin
from mathutils import Vector
import ifcopenshell
import ifcopenshell.api
import ifcopenshell.util.attribute
import ifcopenshell.util.placement
import ifcopenshell.util.representation
import ifcopenshell.util.unit
import ifcopenshell.util.unit as ifcunit
import bonsai.tool as tool
from bonsai.bim.module.structural.shader import DecorationShader
+2
View File
@@ -21,7 +21,9 @@ import bpy
import bonsai.core.tool
import bonsai.tool as tool
import bonsai.bim.helper
import bonsai.core.geometry
import ifcopenshell
import ifcopenshell.util.representation
from typing import Iterable, TYPE_CHECKING
if TYPE_CHECKING:
@@ -0,0 +1,163 @@
"""Setup Bonsai Development Environment.
Script links existing Bonsai installation to the provided IfcOpenShell repository.
If you're on Windows, using Blender 4.4, Bonsai is installed from unstable repo (raw_githubusercontent_com)
and this script is already part of IfcOpenShell repo you want to link, then you can just run it and it will just work.
Otherwise, see the SETTINGS section below to validate script settings to ensure it fits your evnironment.
Example usage:
python /xxx/yyy/dev_environment.py
python dev_environment.py
"""
import sys
import subprocess
import shutil
import urllib.request
from pathlib import Path
if sys.platform != "win32":
print("Currently only available on Windows.")
exit(1)
# ---------------------------
# SETTINGS.
# ---------------------------
# REPO_PATH: Path to your local IfcOpenShell repository.
# By default, this script will automatically detect the repository path based on its own location,
# so you usually do NOT need to set this manually.
# If you want to specify it explicitly, set it to the full absolute path, e.g.:
# > REPO_PATH = r"C:\Path\To\Your\IfcOpenShell\Repository"
REPO_PATH = r""
# BLENDER_PATH: Path to Blender's configuration folder.
# Usually don't need to change, just ensure Blender version matches.
BLENDER_PATH = Path.home() / r"AppData/Roaming/Blender Foundation/Blender/4.4"
# BONSAI_PATH: Path to 'bonsai' extension folder inside BLENDER_PATH.
# Need to ensure extensions repo folder in the path below ('raw_githubusercontent_com') matches yours.
#
# Typical scenarios:
# - Bonsai is installed from Bonsai Unstalble Repo - use 'raw_githubusercontent_com' (as it is by default)
# - Bonsai is installed via offline installation - use 'user_default'
# - Bonsai is installed from Blender's official extensions platform - use 'blender_org'
BONSAI_PATH = BLENDER_PATH / r"extensions/raw_githubusercontent_com/bonsai"
# ---------------------------
# Never changed by user.
PACKAGE_PATH = BLENDER_PATH / r"extensions/.local/lib/python3.11/site-packages"
def main():
global REPO_PATH
if not REPO_PATH:
script_path = Path(__file__)
print(f"REPO_PATH is not set, deducing it from {script_path.name} location...")
repo_bonsai_path = script_path.parent.parent.parent
assert repo_bonsai_path.name == "bonsai"
REPO_PATH = repo_bonsai_path.parent.parent
print("-" * 10)
print("Script settings:")
print(f"REPO_PATH={REPO_PATH}")
print(f"BLENDER_PATH={BLENDER_PATH}")
print(f"BONSAI_PATH={BONSAI_PATH}")
print("-" * 10)
assert REPO_PATH.exists(), f"Path '{REPO_PATH=!s}' doesn't exist, ensure variable is set correctly."
assert BLENDER_PATH.exists(), f"Path '{BLENDER_PATH=!s}' doesn't exist, ensure variable is set correctly."
assert PACKAGE_PATH.exists(), f"Path '{PACKAGE_PATH=!s}' doesn't exist, ensure variable is set correctly."
assert BONSAI_PATH.exists(), f"Path '{BONSAI_PATH=!s}' doesn't exist, ensure variable is set correctly."
input("Confirm the settings above and press Enter to continue or Ctrl-C to cancel...")
# Handle symlinks
# (they could be disabled by default on Windows).
subprocess.run("git config --local core.symlinks true", cwd=REPO_PATH)
symlinks_glob = "src/bonsai/bonsai/bim/data/templates/projects/*.ifc"
# Delete and checkout is the only way to ensure files are added as symlinks.
for path in REPO_PATH.glob(symlinks_glob):
path.unlink()
subprocess.run((f"git checkout -- {symlinks_glob}"), cwd=REPO_PATH)
print("Copying compiled dependencies to the repo...")
dest = REPO_PATH / "src" / "ifcopenshell-python" / "ifcopenshell"
for path in PACKAGE_PATH.glob("ifcopenshell/*_wrapper*"):
if path.suffix.lower() == ".pyi":
continue
dest_ = dest / path.name
print(f"Copying {path} -> {dest_}")
try:
shutil.copy(path, dest_)
except shutil.SameFileError:
pass
print("Symlinking extension to the git repo...")
# fmt: off
symlinks = (
(BONSAI_PATH / "__init__.py", REPO_PATH / "src/bonsai/bonsai/__init__.py"),
(PACKAGE_PATH / "bonsai", REPO_PATH / "src/bonsai/bonsai"),
(PACKAGE_PATH / "ifcopenshell", REPO_PATH / "src/ifcopenshell-python/ifcopenshell"),
(PACKAGE_PATH / "ifccsv.py", REPO_PATH / "src/ifccsv/ifccsv.py"),
(PACKAGE_PATH / "ifcdiff.py", REPO_PATH / "src/ifcdiff/ifcdiff.py"),
(PACKAGE_PATH / "bsdd.py", REPO_PATH / "src/bsdd/bsdd.py"),
(PACKAGE_PATH / "bcf", REPO_PATH / "src/bcf/bcf"),
(PACKAGE_PATH / "ifc4d", REPO_PATH / "src/ifc4d/ifc4d"),
(PACKAGE_PATH / "ifc5d", REPO_PATH / "src/ifc5d/ifc5d"),
(PACKAGE_PATH / "ifccityjson", REPO_PATH / "src/ifccityjson/ifccityjson"),
(PACKAGE_PATH / "ifcclash", REPO_PATH / "src/ifcclash/ifcclash"),
(PACKAGE_PATH / "ifcpatch", REPO_PATH / "src/ifcpatch/ifcpatch"),
(PACKAGE_PATH / "ifctester", REPO_PATH / "src/ifctester/ifctester"),
(PACKAGE_PATH / "ifcfm", REPO_PATH / "src/ifcfm/ifcfm"),
)
# fmt: on
for path, dest in symlinks:
print(f"Linking {path} -> {dest}.")
if path.is_dir():
if path.is_symlink():
path.unlink()
else:
shutil.rmtree(path)
elif path.is_file():
path.unlink()
else:
pass
path.symlink_to(dest, dest.is_dir())
print("Download third party dependencies...")
BONSAI_DATA = PACKAGE_PATH / "bonsai" / "bim" / "data"
downloads = (
(
"https://raw.githubusercontent.com/jsGanttImproved/jsgantt-improved/master/dist/jsgantt.js",
BONSAI_DATA / "gantt" / "jsgantt.js",
),
(
"https://raw.githubusercontent.com/jsGanttImproved/jsgantt-improved/master/dist/jsgantt.css",
BONSAI_DATA / "gantt" / "jsgantt.css",
),
(
"https://github.com/BrickSchema/Brick/releases/download/nightly/Brick.ttl",
BONSAI_DATA / "brick" / "Brick.ttl",
),
(
"https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js",
BONSAI_DATA / "webui" / "static" / "js" / "jquery.min.js",
),
)
for url, filepath in downloads:
print(f"Downloading {url} -> {filepath}")
urllib.request.urlretrieve(url, filepath)
input("Dev environment is all set. 🎉🎉\nPress Enter to continue..." "")
if __name__ == "__main__":
main()
+1 -1
View File
@@ -19,7 +19,7 @@ from __future__ import annotations
import uuid
import time
import urllib
import urllib.parse
import requests
import webbrowser
import http.server
+2
View File
@@ -40,6 +40,8 @@ from .scriptCodeAster import CommandFileConstructor
class Ifc2CA:
file: ifcopenshell.file
folder_path = None
salome_path = None
model_keys = ["id", "type", "GlobalId", "Name", "LoadedBy", "HasResults"]
+1
View File
@@ -20,6 +20,7 @@
import csv
import ifcopenshell
import ifcopenshell.api
import ifcopenshell.util.date
import locale
import re
+1
View File
@@ -20,6 +20,7 @@ import uuid
import datetime
import ifcopenshell
import ifcopenshell.util.date
import ifcopenshell.util.sequence
import xml.etree.ElementTree as ET
+1
View File
@@ -20,6 +20,7 @@ import uuid
import datetime
import ifcopenshell
import ifcopenshell.util.date
import ifcopenshell.util.sequence
import xml.etree.ElementTree as ET
from .common import ScheduleIfcGenerator
+4 -1
View File
@@ -6,11 +6,14 @@ for each day as a key there is a list of working times with the format
"""
import sys
import re
from datetime import time, datetime
from typing import Any
from ifc4d.common import WorkSlot
ZIP_STRICT = {} if sys.version_info < (3, 10) else {"strict": True}
class AstaCalendarWorkPattern:
def get_keys(self, s: str) -> list[str]:
@@ -120,7 +123,7 @@ class AstaCalendarWorkPattern:
self.values = self.get_values(string)
self.dict_wp: list[WorkSlot] = []
for value, day_name in zip(self.values[1:], self.day_names, strict=True):
for value, day_name in zip(self.values[1:], self.day_names, **ZIP_STRICT):
splt_data = value.strip().split(",")
workhours: list[dict[str, Any]] = []
if len(splt_data) >= 7:
+2 -1
View File
@@ -27,7 +27,8 @@ import ifcopenshell.util.selector
import ifcopenshell.util.element
import locale
from pathlib import Path
from typing import Any, Union, Optional, TypedDict, NotRequired
from typing import Union, Optional, TypedDict
from typing_extensions import NotRequired
class CsvHeader(TypedDict):
+1 -1
View File
@@ -29,7 +29,7 @@ import ifcopenshell.util.cost
import ifcopenshell.util.date
import ifcopenshell.util.unit
from collections import Counter
from typing import Union, Optional, Any, TypedDict, NotRequired
from typing import Union, Optional, Any, TypedDict
class CostItem(TypedDict):
@@ -258,6 +258,7 @@ class Cityjson2ifc:
# TODO but maybe there is a better method.
file = self.properties["file_destination"] + lod + self.properties["file_extension"]
self.IFC_model.write(file)
IFC_copied_model: ifcopenshell.file
IFC_copied_model = ifcopenshell.open(file)
IFC_copied_model_sub_contexts = IFC_copied_model.by_type("IfcGeometricRepresentationSubContext")
for sub_context in IFC_copied_model_sub_contexts:
+1 -1
View File
@@ -18,7 +18,7 @@ classifiers = [
]
dependencies = [
"ifcopenshell",
"cjio>=0.8"
"cjio >=0.8, <0.10"
]
[project.urls]
+1
View File
@@ -559,6 +559,7 @@ if __name__ == "__main__":
)
elif getattr(args, "import"):
ifc_csv = IfcCsv()
ifc_file: ifcopenshell.file
ifc_file = ifcopenshell.open(args.ifc)
ifc_csv.Import(
ifc_file,
+5 -1
View File
@@ -60,7 +60,11 @@ IFCCONVERT_URL:=https://s3.amazonaws.com/ifcopenshell-builds/IfcConvert-v0.8.3-e
.PHONY: test
test:
pytest -p no:pytest-blender test
pytest -p no:pytest-blender test --ignore=test/util/test_shape_builder.py
.PHONY: test-mathutils
test-mathutils:
pytest -p no:pytest-blender test/util/test_shape_builder.py
.PHONY: build-ids-docs
build-ids-docs:
@@ -65,7 +65,7 @@ if TYPE_CHECKING:
import ifcopenshell.express.schema_class
if hasattr(os, "uname"):
if sys.platform != "win32":
platform_system = os.uname()[0].lower()
else:
platform_system = "windows"
@@ -43,9 +43,11 @@ import numpy
import inspect
import importlib
import ifcopenshell
from typing import Callable, Any, Optional
from typing import Callable, Any, Optional, TYPE_CHECKING
from functools import partial
if TYPE_CHECKING:
import ifcopenshell.api
pre_listeners: dict[str, dict] = {}
post_listeners: dict[str, dict] = {}
@@ -185,7 +187,7 @@ def extract_docs(module: str, usecase: str) -> dict[str, Any]:
type_hints = typing.get_type_hints(function_init)
for name, socket_data in inputs.items():
type_hint = type_hints[name]
if isinstance(type_hint, typing._UnionGenericAlias):
if isinstance(type_hint, typing._UnionGenericAlias): # pyright: ignore[reportAttributeAccessIssue]
inputs[name]["type"] = [t.__name__ for t in typing.get_args(type_hint)]
else:
inputs[name]["type"] = type_hint.__name__
@@ -19,6 +19,8 @@
import ifcopenshell
import ifcopenshell.api.alignment
import ifcopenshell.api.nest
import ifcopenshell.api.pset
import ifcopenshell.util.stationing
import ifcopenshell.guid
from ifcopenshell import entity_instance
@@ -21,6 +21,7 @@ import ifcopenshell.api.aggregate
import ifcopenshell.api.alignment
import ifcopenshell.api.geometry
import ifcopenshell.api.nest
import ifcopenshell.api.root
import ifcopenshell.guid
import ifcopenshell.util.element
import ifcopenshell.util.representation
@@ -17,7 +17,9 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
import ifcopenshell.api.aggregate
import ifcopenshell.api.alignment
import ifcopenshell.api.nest
from ifcopenshell import entity_instance
from typing import Sequence
@@ -17,11 +17,10 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
import ifcopenshell.util.element
import ifcopenshell.util.representation
import ifcopenshell.api.alignment
from ifcopenshell import entity_instance
from ifcopenshell import ifcopenshell_wrapper
import math
from typing import Sequence
def create_segment_representations(
@@ -17,9 +17,7 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
import ifcopenshell.api.alignment
import ifcopenshell.api.nest
import ifcopenshell.guid
import ifcopenshell.util.element
from ifcopenshell import entity_instance
@@ -49,8 +49,6 @@ def map_alignment_segments(
elif alignment.is_a("IfcAlignmentCant") and not composite_curve.is_a("IfcSegmentedReferenceCurve"):
raise TypeError(f"Expected to see IfcSegmentedReferenceCurve, instead received '{composite_curve.is_a()}'.")
settings = ifcopenshell.geom.settings()
composite_curve.SelfIntersect = False
for rel_nests in alignment.IsNestedBy:
@@ -64,6 +62,8 @@ def map_alignment_segments(
mapped_segments = ifcopenshell.api.alignment.map_alignment_cant_segment(
file, layout, alignment.RailHeadDistance
)
else:
assert False
for mapped_segment in mapped_segments:
if mapped_segment:
ifcopenshell.api.alignment.add_segment_to_curve(file, mapped_segment, composite_curve)
@@ -16,10 +16,15 @@
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import sys
import ifcopenshell.api.owner
import ifcopenshell.util.element
from typing import Any, Union
from types import EllipsisType
if sys.version_info >= (3, 10):
from types import EllipsisType
else:
EllipsisType = type(...)
def edit_attributes(file: ifcopenshell.file, product: ifcopenshell.entity_instance, attributes: dict[str, Any]) -> None:
@@ -51,7 +51,7 @@ def copy_cost_item_values(
ifcopenshell.api.cost.copy_cost_item_values(model, source=item1, destination=item2)
"""
for cost_value in destination.CostValues or []:
ifcopenshell.api.cost.remove_cost_item_value(file, cost_value=cost_value)
ifcopenshell.api.cost.remove_cost_value(file, source, cost_value=cost_value)
copied_cost_values = []
for cost_value in source.CostValues or []:
copied_cost_values.append(ifcopenshell.util.element.copy_deep(file, cost_value))
@@ -34,7 +34,8 @@ from .add_railing_representation import add_railing_representation
try:
from .add_representation import add_representation
except ModuleNotFoundError:
except (ModuleNotFoundError, ImportError):
# ImportError - in case if user has fake-bpy modules.
pass # Silently fail. This is Blender / Bonsai specific and on its way out.
from .add_shape_aspect import add_shape_aspect
from .add_slab_representation import add_slab_representation
@@ -17,6 +17,7 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
from __future__ import annotations
import sys
import ifcopenshell.util.unit
import ifcopenshell.api.geometry
import dataclasses
@@ -26,6 +27,8 @@ from ifcopenshell.api.geometry.add_window_representation import create_ifc_windo
from math import cos, radians
from typing import Any, Optional, Literal, Union, get_args, overload
DATACLASS_SLOTS = {} if sys.version_info < (3, 10) else {"slots": True}
DOOR_TYPE = Literal[
"SINGLE_SWING_LEFT",
@@ -96,7 +99,7 @@ def create_ifc_box(
# we use dataclass as we need default values for arguments
# it's okay to use slots since we don't need dynamic attributes
@dataclasses.dataclass(slots=True)
@dataclasses.dataclass(**DATACLASS_SLOTS)
class DoorLiningProperties:
LiningDepth: Optional[float] = None
"""Optional, defaults to 50mm."""
@@ -170,7 +173,7 @@ class DoorLiningProperties:
setattr(self, attr, default_value * si_conversion)
@dataclasses.dataclass(slots=True)
@dataclasses.dataclass(**DATACLASS_SLOTS)
class DoorPanelProperties:
PanelDepth: Optional[float] = None
"""Frame thickness by Y axis. Optional, defaults to 35 mm."""
@@ -17,13 +17,17 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
from __future__ import annotations
import sys
import numpy as np
import dataclasses
import ifcopenshell.api.geometry
import ifcopenshell.util.unit
from itertools import chain
from ifcopenshell.util.shape_builder import ShapeBuilder, V
from typing import Any, Optional, Literal, Union, overload
DATACLASS_SLOTS = {} if sys.version_info < (3, 10) else {"slots": True}
ZIP_STRICT = {} if sys.version_info < (3, 10) else {"strict": True}
# SCHEMAS describe panels setup
# where:
@@ -165,7 +169,7 @@ def window_l_shape_check(
"""`lining_thickness` and `lining_to_panel_offset_x` expected to be defined as a list,
similarly to `create_ifc_window_frame_simple` `thickness` argument"""
l_shape_check = lining_to_panel_offset_y_full < lining_depth and any(
x_offset < th for th, x_offset in zip(lining_thickness, lining_to_panel_offset_x, strict=True)
x_offset < th for th, x_offset in zip(lining_thickness, lining_to_panel_offset_x, **ZIP_STRICT)
)
return l_shape_check
@@ -207,7 +211,7 @@ def create_ifc_window(
second_lining_size = lining_size.copy()
second_lining_size[np_Y] = lining_size[np_Y] - lining_to_panel_offset_y_full
second_lining_position = V(0, lining_to_panel_offset_y_full, 0)
second_lining_thickness = [min(th, x_offset) for th, x_offset in zip(lining_thickness, x_offsets, strict=True)]
second_lining_thickness = [min(th, x_offset) for th, x_offset in zip(lining_thickness, x_offsets, **ZIP_STRICT)]
second_lining_items = create_ifc_window_frame_simple(
builder, second_lining_size, second_lining_thickness, second_lining_position
@@ -237,7 +241,7 @@ def create_ifc_window(
# we use dataclass as we need default values for arguments
# it's okay to use slots since we don't need dynamic attributes
@dataclasses.dataclass(slots=True)
@dataclasses.dataclass(**DATACLASS_SLOTS)
class WindowLiningProperties:
LiningDepth: Optional[float] = None
"""Optional, defaults to 50mm."""
@@ -320,7 +324,7 @@ class WindowLiningProperties:
setattr(self, attr, default_value * si_conversion)
@dataclasses.dataclass(slots=True)
@dataclasses.dataclass(**DATACLASS_SLOTS)
class WindowPanelProperties:
FrameDepth: Optional[float] = None
"""Frame thickness by Y axis. Optional, defaults to 35 mm."""
@@ -18,11 +18,10 @@
import numpy as np
import ifcopenshell
import ifcopenshell.api.owner
import ifcopenshell.guid
import ifcopenshell.util.element
import ifcopenshell.api.geometry
import ifcopenshell.util.placement
import ifcopenshell.util.representation
import ifcopenshell.util.shape_builder
from typing import Optional
@@ -19,11 +19,13 @@
import json
import numpy as np
import ifcopenshell
import ifcopenshell.api.context
import ifcopenshell.api.geometry
import ifcopenshell.util.unit
import ifcopenshell.util.element
import ifcopenshell.util.placement
import ifcopenshell.util.representation
import ifcopenshell.util.shape_builder
import ifcopenshell.util.unit
from collections import namedtuple
from math import sin, cos
from typing import Optional
@@ -19,6 +19,7 @@
import ifcopenshell
import ifcopenshell.util.element
import ifcopenshell.util.geolocation
import ifcopenshell.util.unit
import numpy as np
from math import sin, cos, radians
@@ -18,6 +18,75 @@
from typing import Any
# `std::vector<xxx>` usually translated to `tuple[xxx, ...]`.
ON_SLABS_AND_WALLS: Any
ON_SLABS_AT_FLOORPLANS: Any
ALWAYS: Any
ATPATH: Any
ATSTART: Any
ATEND: Any
NOTDEFINED: Any
MATRIX4: Any
POINT3: Any
DIRECTION3: Any
LINE: Any
CIRCLE: Any
ELLIPSE: Any
BSPLINE_CURVE: Any
OFFSET_CURVE: Any
PLANE: Any
CYLINDER: Any
SPHERE: Any
TORUS: Any
BSPLINE_SURFACE: Any
EDGE: Any
LOOP: Any
FACE: Any
SHELL: Any
SOLID: Any
LOFT: Any
EXTRUSION: Any
REVOLVE: Any
SWEEP_ALONG_CURVE: Any
NODE: Any
COLLECTION: Any
BOOLEAN_RESULT: Any
FUNCTION_ITEM: Any
FUNCTOR_ITEM: Any
PIECEWISE_FUNCTION: Any
GRADIENT_FUNCTION: Any
CANT_FUNCTION: Any
OFFSET_FUNCTION: Any
COLOUR: Any
STYLE: Any
CARTESIAN_DOUBLE: Any
CARTESIAN_QUOTIENT: Any
FILTERED_CARTESIAN_QUOTIENT: Any
EXACT_PREDICATES: Any
EXACT_CONSTRUCTIONS: Any
CURVES: Any
SURFACES_AND_SOLIDS: Any
CURVES_SURFACES_AND_SOLIDS: Any
TRIANGULATED: Any
NATIVE: Any
SERIALIZED: Any
MAXSTEPSIZE: Any
MINSTEPS: Any
TRIANGLE_MESH: Any
POLYHEDRON_WITHOUT_HOLES: Any
POLYHEDRON_WITH_HOLES: Any
SHARED_PTR_DISOWN: Any
cvar: Any
class FileDescription(HeaderEntity):
description: tuple[str, ...]
implementation_level: str
@@ -65,11 +134,6 @@ class BRepElement(Element):
@property
def volume(self): ...
class CacheShapes:
name: Any
description: Any
defaultvalue: Any
class CgalEmitOriginalEdges:
name: Any
description: Any
@@ -195,6 +259,15 @@ class GeometrySerializer:
def settings(self, *args): ...
def write(self, *args): ...
class GltfSerializer(WriteOnlyGeometrySerializer):
def finalize(self): ...
def isTesselated(self): ...
def ready(self): ...
def setFile(self, arg2): ...
def setUnitNameAndMagnitude(self, arg2, arg3): ...
def write(self, *args): ...
def writeHeader(self): ...
class HdfSerializer(GeometrySerializer):
def finalize(self): ...
def isTesselated(self): ...
@@ -226,9 +299,13 @@ class IfcEntityInstanceData: ...
class IfcLateBoundEntity(IfcBaseEntity):
def declaration(self): ...
class InstanceReference: ...
class InstanceReference:
file_offset: Any
v: Any
class Iterator:
initialization_outcome_: Any
processed_: Any
def bounds_max(self): ...
def bounds_min(self): ...
def compute_bounds(self, with_geometry): ...
@@ -249,11 +326,6 @@ class Iterator:
def unit_magnitude(self): ...
def unit_name(self): ...
class OcctNoCleanTriangulation:
name: Any
description: Any
defaultvalue: Any
class OpaqueCoordinate_3:
def get(self, i): ...
def set(self, i, n): ...
@@ -267,11 +339,6 @@ class OpaqueNumber:
def to_double(self): ...
def to_string(self): ...
class PermissiveShapeReuse:
name: Any
description: Any
defaultvalue: Any
class Representation:
def entity(self): ...
@property
@@ -477,6 +544,8 @@ class XmlSerializerFactory:
@staticmethod
def implementations(): ...
class _SwigNonDynamicMeta(type): ...
class abstract_arrangement:
def get_face_pairs(self): ...
def merge(self, edge_indices): ...
@@ -497,9 +566,9 @@ class aggregation_type(parameter_type):
def type_of_element(self): ...
class attribute:
def name(self): ...
def optional(self): ...
def type_of_attribute(self): ...
def name(self) -> str: ...
def optional(self) -> bool: ...
def type_of_attribute(self) -> parameter_type: ...
class attribute_value_derived: ...
@@ -507,6 +576,7 @@ class boolean_result:
INTERSECTION: Any
SUBTRACTION: Any
UNION: Any
operation: Any
def calc_hash(self): ...
@property
def children(self): ...
@@ -516,11 +586,21 @@ class boolean_result:
def operation_str(op): ...
class bspline_curve(curve):
control_points: Any
degree: Any
knots: Any
multiplicities: Any
weights: Any
def calc_hash(self): ...
def clone_(self): ...
def kind(self): ...
class bspline_surface(surface):
control_points: Any
degree: Any
knots: Any
multiplicities: Any
weights: Any
def calc_hash(self): ...
def clone_(self): ...
def kind(self): ...
@@ -540,6 +620,7 @@ class cant_function(function_item):
def start(self): ...
class circle(curve):
radius: Any
def calc_hash(self): ...
def clone_(self): ...
@staticmethod
@@ -548,7 +629,13 @@ class circle(curve):
@property
def matrix(self): ...
class clash: ...
class clash:
a: Any
b: Any
clash_type: Any
distance: Any
p1: Any
p2: Any
class clashes:
def append(self, x): ...
@@ -606,6 +693,7 @@ class curve(geom_item):
def print_impl(self, o, classname, indent): ...
class cylinder(surface):
radius: Any
def calc_hash(self): ...
def clone_(self): ...
def kind(self): ...
@@ -613,12 +701,13 @@ class cylinder(surface):
def matrix(self): ...
class declaration:
def _is(self, *args: str) -> bool: ...
def as_entity(self): ...
def as_enumeration_type(self): ...
def as_select_type(self): ...
def as_type_declaration(self): ...
def index_in_schema(self): ...
def name(self): ...
def name(self) -> str: ...
def name_uc(self): ...
def schema(self): ...
def type(self): ...
@@ -630,7 +719,9 @@ class direction3:
def components(self): ...
def kind(self): ...
class drawing_meta: ...
class drawing_meta:
matrix_3: Any
pln_3d: Any
class edge(trimmed_curve):
def calc_hash(self): ...
@@ -638,6 +729,8 @@ class edge(trimmed_curve):
def kind(self): ...
class ellipse(curve):
radius: Any
radius2: Any
def calc_hash(self): ...
def clone_(self): ...
def kind(self): ...
@@ -649,11 +742,14 @@ class entity(declaration):
def all_inverse_attributes(self): ...
def argument_types(self): ...
def as_entity(self): ...
def attribute_by_index(self, index): ...
def attribute_count(self): ...
def attribute_by_index(self, index: int) -> attribute: ...
def attribute_count(self) -> int: ...
def attribute_index(self, *args): ...
def attributes(self): ...
def derived(self): ...
def derived(self) -> tuple[bool, ...]:
"""Return a tuple of booleans indicating whether each direct attribute is derived."""
...
def is_abstract(self): ...
def set_attributes(self, attributes, derived): ...
def set_inverse_attributes(self, inverse_attributes): ...
@@ -662,6 +758,8 @@ class entity(declaration):
def supertype(self): ...
class entity_instance:
file_: Any
id_: Any
def data(self, *args): ...
def declaration(self): ...
def file_pointer(self): ...
@@ -701,9 +799,9 @@ class enumeration_type(declaration):
def lookup_enum_offset(self, string): ...
def lookup_enum_value(self, i): ...
class equal_functor: ...
class extrusion(sweep):
depth: Any
direction: Any
def calc_hash(self): ...
def clone_(self): ...
def kind(self): ...
@@ -711,6 +809,7 @@ class extrusion(sweep):
def matrix(self): ...
class face:
basis: Any
def calc_hash(self): ...
@property
def children(self): ...
@@ -724,6 +823,8 @@ class file:
INSTANCE_ID: Any
INSTANCE_TYPE: Any
ATTRIBUTE_INDEX: Any
guid_map_: Any
stream: Any
def FreshId(self): ...
def add(self, entity, id): ...
def addEntities(self, entities): ...
@@ -764,7 +865,13 @@ class file:
@staticmethod
def traverse_breadth_first(instance, max_level): ...
def try_read_semicolon(self): ...
def types(self): ...
def types(self) -> tuple[str, ...]:
"""Return a tuple of classes present in the file.
E.g. `("IfcWallType", "IfcWall", "IfcArbitraryClosedProfileDef", ...)`.
"""
...
def types_begin(self): ...
def types_end(self): ...
def unbatch(self): ...
@@ -779,6 +886,7 @@ class file_open_status:
def value(self): ...
class fn_evaluator:
settings_: Any
def clone(self): ...
def end(self): ...
def evaluate(self, u): ...
@@ -803,7 +911,9 @@ class functor_item(function_item):
def kind(self): ...
def start(self): ...
class geom_item(item): ...
class geom_item(item):
matrix: Any
surface_style: Any
class geometry_exception:
def what(self): ...
@@ -817,7 +927,6 @@ class gradient_function(function_item):
def kind(self): ...
def start(self): ...
class hash_functor: ...
class horizontal_plan_at_element: ...
class implicit_item(geom_item): ...
@@ -834,6 +943,8 @@ class inverse_attribute:
def type_of_aggregation_string(self): ...
class item:
instance: Any
orientation: Any
def calc_hash(self): ...
def clone_(self): ...
def hash(self): ...
@@ -864,6 +975,7 @@ class line_segment:
def swap(self, v): ...
class loft:
axis: Any
def calc_hash(self): ...
@property
def children(self): ...
@@ -872,6 +984,9 @@ class loft:
def print_impl(self, o, indent): ...
class loop:
closed: Any
external: Any
fi: Any
def calc_hash(self): ...
def calculate_linear_edge_curves(self): ...
@property
@@ -887,6 +1002,7 @@ class matrix4(item):
AFFINE_W_UNIFORM_SCALE: Any
AFFINE_W_NONUNIFORM_SCALE: Any
OTHER: Any
tag: Any
def calc_hash(self): ...
def clone_(self): ...
@property
@@ -896,6 +1012,7 @@ class matrix4(item):
def translation_part(self): ...
class named_type(parameter_type):
def _is(self, *args): ...
def as_named_type(self): ...
def declared_type(self): ...
@@ -905,6 +1022,9 @@ class node(item):
def kind(self): ...
class offset_curve(curve):
basis: Any
offset: Any
reference: Any
def calc_hash(self): ...
def clone_(self): ...
def kind(self): ...
@@ -919,6 +1039,7 @@ class offset_function(function_item):
def start(self): ...
class parameter_type:
def _is(self, *args): ...
def as_aggregation_type(self): ...
def as_named_type(self): ...
def as_simple_type(self): ...
@@ -949,8 +1070,19 @@ class point3:
def components(self): ...
def kind(self): ...
class polygon_2: ...
class ray_intersection_result: ...
class polygon_2:
boundary: Any
inner_boundaries: Any
point_inside: Any
class ray_intersection_result:
distance: Any
dot_product: Any
instance: Any
normal: Any
position: Any
ray_distance: Any
style_index: Any
class ray_intersection_results:
def append(self, x): ...
@@ -977,6 +1109,9 @@ class ray_intersection_results:
def swap(self, v): ...
class revolve(sweep):
angle: Any
axis_origin: Any
direction: Any
def calc_hash(self): ...
def clone_(self): ...
def kind(self): ...
@@ -984,8 +1119,8 @@ class revolve(sweep):
def matrix(self): ...
class schema_definition:
def declaration_by_name(self, *args): ...
def declarations(self): ...
def declaration_by_name(self, *args: str) -> declaration: ...
def declarations(self) -> tuple[declaration, ...]: ...
def entities(self): ...
def enumeration_types(self): ...
def instantiate(self, decl, data): ...
@@ -998,6 +1133,7 @@ class select_type(declaration):
def select_list(self): ...
class shell:
closed: Any
def calc_hash(self): ...
@property
def children(self): ...
@@ -1027,6 +1163,7 @@ class solid:
def matrix(self): ...
class sphere(surface):
radius: Any
def calc_hash(self): ...
def clone_(self): ...
def kind(self): ...
@@ -1034,6 +1171,13 @@ class sphere(surface):
def matrix(self): ...
class style(item):
diffuse: Any
name: Any
specular: Any
specularity: Any
surface: Any
transparency: Any
use_surface_color: Any
def calc_hash(self): ...
def clone_(self): ...
def get_color(self): ...
@@ -1201,9 +1345,12 @@ class svg_polygons:
def size(self): ...
def swap(self, v): ...
class sweep(geom_item): ...
class sweep(geom_item):
basis: Any
class sweep_along_curve(sweep):
curve: Any
surface: Any
def calc_hash(self): ...
def clone_(self): ...
def kind(self): ...
@@ -1212,6 +1359,8 @@ class too_many_faces_exception(geometry_exception): ...
class topology_error: ...
class torus(surface):
radius1: Any
radius2: Any
def calc_hash(self): ...
def clone_(self): ...
def kind(self): ...
@@ -1239,6 +1388,11 @@ class tree:
def write_h5(self): ...
class trimmed_curve(geom_item):
basis: Any
curve_sense: Any
start: Any
end: Any
def reverse(self): ...
class type_by_kind:
@@ -1249,12 +1403,7 @@ class type_declaration(declaration):
def as_type_declaration(self): ...
def declared_type(self): ...
def Triangulation_box_project_uvs(vertices, normals): ...
def Triangulation_empty(settings): ...
def XmlSerializerFactory_implementations(): ...
def arrange_polygons(polygons): ...
def boolean_result_operation_str(op): ...
def circle_from_3_points(p1, p2, p3): ...
def clear_schemas(): ...
def construct_iterator_with_include_exclude(geometry_library, settings, file, elems, include, num_threads): ...
def construct_iterator_with_include_exclude_globalid(geometry_library, settings, file, elems, include, num_threads): ...
@@ -1262,10 +1411,6 @@ def construct_iterator_with_include_exclude_id(geometry_library, settings, file,
def create_box(*args): ...
def create_epeck(*args): ...
def create_shape(*args): ...
def file_createTimestamp(): ...
def file_guid_map(*args): ...
def file_traverse(instance, max_level): ...
def file_traverse_breadth_first(instance, max_level): ...
def flatten(deep): ...
def get_feature(x): ...
def get_info_cpp(v, include_identifier): ...
@@ -1282,7 +1427,7 @@ def parse_ifcxml(filename): ...
def polygons_to_svg(*args): ...
def read(data): ...
def register_schema(arg1): ...
def schema_by_name(arg1): ...
def schema_by_name(arg1: str) -> schema_definition: ...
def schema_names(): ...
def serialise(schema_name, shape_str, advanced): ...
def set_feature(x, v): ...
@@ -1292,8 +1437,6 @@ def svg_to_line_segments(data, class_name): ...
def svg_to_polygons(data, class_name): ...
def taxonomy_item_repr(i): ...
def tesselate(schema_name, shape_str, d): ...
def tree_is_manifold(fs): ...
def tree_vector_to_list(ps): ...
def turn_off_detailed_logging(): ...
def turn_on_detailed_logging(): ...
def version(): ...
@@ -19,8 +19,9 @@
import os
import json
import ifcopenshell
import ifcopenshell.util.element
import ifcopenshell.util.classification
import ifcopenshell.util.element
import ifcopenshell.util.system
from typing import Union
@@ -19,8 +19,9 @@
import numpy as np
import numpy.typing as npt
import ifcopenshell
import ifcopenshell.util.shape
import ifcopenshell.util.representation
import ifcopenshell.util.placement
import ifcopenshell.util.shape
from typing import Optional, Union, TypedDict, Literal, Generator, Sequence
@@ -73,7 +73,7 @@ def get_declaration(element: ifcopenshell.entity_instance):
return element.wrapped_data.declaration().as_entity()
def is_a(declaration: ifcopenshell.ifcopenshell_wrapper.entity, ifc_class: str) -> bool:
def is_a(declaration: ifcopenshell.ifcopenshell_wrapper.declaration, ifc_class: str) -> bool:
"""Checks if a schema declaration is a class
:param declaration: The declaration from the schema.
@@ -57,7 +57,7 @@ def get_function_node_name(node: ast.FunctionDef) -> Union[SubnameType, None]:
:return: Function node name as ``SubnameType`` or ``None``, if function wasn't processed and can be skipped.
"""
node_name = node.name
if node_name.startswith("_"):
if node_name.startswith("_") and node_name not in ("_is",):
return None
args = [a.arg for a in node.args.args]
if node.args.vararg:
@@ -81,7 +81,7 @@ def get_names_tree_lines(tree: ast.Module) -> list[str]:
if isinstance(node, ast.ClassDef):
# Skip `object_` as it's just a reference to `object`,
# which is implied by default.
bases = [b.id for b in node.bases if isinstance(b, ast.Name) and b.id != "_object"]
bases = [b.id for b in node.bases if isinstance(b, ast.Name) and b.id not in ("_object", "object")]
bases_str = f"({', '.join(bases)})" if bases else ""
node_name = f"class {node.name}{bases_str}:"
@@ -102,63 +102,73 @@ def get_names_tree_lines(tree: ast.Module) -> list[str]:
continue
subname_ = target.id
if subname_.startswith("_"):
if subname_.startswith(("_", "thisown")):
continue
value = subnode.value
# Catching wrappers like:
# - `matrix = property(matrix_getter)`
# - `matrix = property(matrix_getter, matrix_setter)`
# - `operation_str = staticmethod(operation_str)`
if isinstance(value, ast.Call):
if not isinstance(value, ast.Call):
subname = subname_
else:
# Catching wrappers like:
# - `matrix = property(matrix_getter)`
# - `matrix = property(matrix_getter, matrix_setter)`
# - `operation_str = staticmethod(operation_str)`
func = value.func
if not isinstance(func, ast.Name) or ((func_id := func.id) not in ("property", "staticmethod")):
continue
args = [arg.id for arg in value.args if isinstance(arg, ast.Name)]
len_args = len(args)
assert len_args in (1, 2)
if len_args in (1, 2):
def find_method_by_name(name: str) -> Union[str, None]:
function_def = f"def {name}("
return next(
(
func_
for func_ in subnames
if isinstance(func_, str) and func_.startswith(function_def)
),
None,
)
def find_method_by_name(name: str) -> Union[str, None]:
function_def = f"def {name}("
return next(
(
func_
for func_ in subnames
if isinstance(func_, str) and func_.startswith(function_def)
),
None,
)
# Use `set` for cases like `description = property(description, description)`.
wrapped_function = None
for arg in set(args):
assert (wrapped_function := find_method_by_name(arg))
subnames.remove(wrapped_function)
# Use `set` for cases like `description = property(description, description)`.
wrapped_function = None
for arg in set(args):
assert (wrapped_function := find_method_by_name(arg))
subnames.remove(wrapped_function)
# TODO: sort it out in wrapper.py
# There's one annoying case in Element.product
# when property is overriding existing function, without using it.
# We should probably just exclude that function from the wrapper.
overridden_name = find_method_by_name(subname_)
if overridden_name:
subnames.remove(overridden_name)
# TODO: sort it out in wrapper.py
# There's one annoying case in Element.product
# when property is overriding existing function, without using it.
# We should probably just exclude that function from the wrapper.
overridden_name = find_method_by_name(subname_)
if overridden_name:
subnames.remove(overridden_name)
if len_args == 2:
# Has both getter and setter, can be defined as a simple attribute.
subname = subname_
elif len_args == 1:
if func_id == "property":
# Has just getter, read-only, need to define it using a wrapper.
subname = (f"@{func_id}", f"def {subname_}(self): ...")
elif func_id == "staticmethod":
assert wrapped_function is not None
subname = (f"@{func_id}", f"def {subname_}({wrapped_function.split('(')[1]}")
if len_args == 2:
# Has both getter and setter, can be defined as a simple attribute.
subname = subname_
elif len_args == 1:
if func_id == "property":
# Has just getter, read-only, need to define it using a wrapper.
subname = (f"@{func_id}", f"def {subname_}(self): ...")
elif func_id == "staticmethod":
assert wrapped_function is not None
subname = (f"@{func_id}", f"def {subname_}({wrapped_function.split('(')[1]}")
else:
assert_never(func_id)
else:
assert_never(func_id)
assert_never(len_args)
else:
assert_never(len_args)
else:
subname = subname_
attr_args = [
arg
for arg in value.args
if isinstance(arg, ast.Attribute)
and isinstance(arg.value, ast.Name)
and arg.value.id == "_ifcopenshell_wrapper"
]
assert len(attr_args) == 2
subname = subname_
if subname is not None:
subnames.add(subname)
@@ -171,6 +181,17 @@ def get_names_tree_lines(tree: ast.Module) -> list[str]:
node_name = get_function_node_name(node)
assert isinstance(node_name, str)
elif isinstance(node, ast.Assign):
targets = node.targets
if not len(targets) == 1 or not isinstance(target := targets[0], ast.Name):
continue
node_name = target.id
elif isinstance(node, ast.AnnAssign):
target = node.target
assert isinstance(target, ast.Name)
node_name = target.id
if node_name is not None:
names_tree[node_name] = subnames
@@ -215,6 +236,7 @@ def main() -> None:
fromfile="stub.pyi classes",
tofile="wrapper.py classes",
lineterm="",
n=10,
)
diff = list(diff)
@@ -53,6 +53,11 @@ from collections import namedtuple
from typing import Union, Iterator, Any, Optional
from logging import Logger, Handler
if sys.version_info >= (3, 10):
from types import EllipsisType
else:
EllipsisType = type(...)
import ifcopenshell
import ifcopenshell.simple_spf
import ifcopenshell.ifcopenshell_wrapper
@@ -439,7 +444,7 @@ def validate(f: Union[ifcopenshell.file, str], logger: Logger, express_rules=Fal
if hasattr(logger, "set_state"):
logger.set_state("instance", inst)
guid: Union[str, None, types.EllipsisType]
guid: Union[str, None, EllipsisType]
if (guid := getattr(inst, "GlobalId", ...)) is not ...:
if guid is not None and guid in used_guids:
rule = "Rule IfcRoot.UR1:\n The attribute GlobalId should be unique"
+7 -1
View File
@@ -25,7 +25,13 @@ dependencies = [
]
[project.optional-dependencies]
dev = ["pytest"]
advanced = [
"networkx",
]
dev = [
"pytest",
"tabulate",
]
[project.urls]
"Homepage" = "http://ifcopenshell.org"
@@ -22,7 +22,7 @@ import ifcopenshell.api.context
def test_add_segment_to_curve():
file = ifcopenshell.file(schema="IFC4X3_ADD2")
file = ifcopenshell.file(schema="IFC4X3")
project = file.createIfcProject(Name="Test")
geometric_representation_context = ifcopenshell.api.context.add_context(file, context_type="Model")
axis_model_representation_subcontext = ifcopenshell.api.context.add_context(
@@ -22,7 +22,7 @@ import ifcopenshell.api.context
def test_add_segment_to_layout():
file = ifcopenshell.file(schema="IFC4X3_ADD2")
file = ifcopenshell.file(schema="IFC4X3")
project = file.createIfcProject(Name="Test")
geometric_representation_context = ifcopenshell.api.context.add_context(file, context_type="Model")
axis_model_representation_subcontext = ifcopenshell.api.context.add_context(
@@ -22,7 +22,7 @@ import ifcopenshell.api.context
def test_add_stationing_to_alignment():
file = ifcopenshell.file(schema="IFC4X3_ADD2")
file = ifcopenshell.file(schema="IFC4X3")
project = file.createIfcProject(Name="Test")
geometric_representation_context = ifcopenshell.api.context.add_context(file, context_type="Model")
axis_model_representation_subcontext = ifcopenshell.api.context.add_context(
@@ -22,7 +22,7 @@ import ifcopenshell.api.context
def test_add_vertical_by_pi_method():
file = ifcopenshell.file(schema="IFC4X3_ADD2")
file = ifcopenshell.file(schema="IFC4X3")
project = file.createIfcProject(Name="Test")
geometric_representation_context = ifcopenshell.api.context.add_context(file, context_type="Model")
axis_model_representation_subcontext = ifcopenshell.api.context.add_context(
@@ -22,7 +22,7 @@ import ifcopenshell.api.context
def test_add_stationing_to_alignment():
file = ifcopenshell.file(schema="IFC4X3_ADD2")
file = ifcopenshell.file(schema="IFC4X3")
project = file.createIfcProject(Name="Test")
geometric_representation_context = ifcopenshell.api.context.add_context(file, context_type="Model")
axis_model_representation_subcontext = ifcopenshell.api.context.add_context(
@@ -25,7 +25,7 @@ import ifcopenshell.api.context
# class TestGetBasisCurve(test.bootstrap.IFC4X3):
def test_horizontal():
file = ifcopenshell.file(schema="IFC4X3_ADD2")
file = ifcopenshell.file(schema="IFC4X3")
project = file.createIfcProject(Name="Test")
geometric_representation_context = ifcopenshell.api.context.add_context(file, context_type="Model")
axis_model_representation_subcontext = ifcopenshell.api.context.add_context(
@@ -46,7 +46,7 @@ def test_horizontal():
def test_horizontal_and_vertical():
file = ifcopenshell.file(schema="IFC4X3_ADD2")
file = ifcopenshell.file(schema="IFC4X3")
project = file.createIfcProject(Name="Test")
geometric_representation_context = ifcopenshell.api.context.add_context(file, context_type="Model")
axis_model_representation_subcontext = ifcopenshell.api.context.add_context(
@@ -25,7 +25,7 @@ import ifcopenshell.api.context
# class TestGetCurve(test.bootstrap.IFC4X3):
def test_horizontal():
file = ifcopenshell.file(schema="IFC4X3_ADD2")
file = ifcopenshell.file(schema="IFC4X3")
project = file.createIfcProject(Name="Test")
geometric_representation_context = ifcopenshell.api.context.add_context(file, context_type="Model")
axis_model_representation_subcontext = ifcopenshell.api.context.add_context(
@@ -46,7 +46,7 @@ def test_horizontal():
def test_horizontal_and_vertical():
file = ifcopenshell.file(schema="IFC4X3_ADD2")
file = ifcopenshell.file(schema="IFC4X3")
project = file.createIfcProject(Name="Test")
geometric_representation_context = ifcopenshell.api.context.add_context(file, context_type="Model")
axis_model_representation_subcontext = ifcopenshell.api.context.add_context(
@@ -26,7 +26,7 @@ import ifcopenshell.api.nest
def _test_business_definition():
file = ifcopenshell.file(schema="IFC4X3_ADD2")
file = ifcopenshell.file(schema="IFC4X3")
project = file.createIfcProject(Name="Test")
geometric_representation_context = ifcopenshell.api.context.add_context(file, context_type="Model")
axis_model_representation_subcontext = ifcopenshell.api.context.add_context(
@@ -70,7 +70,7 @@ def _test_business_definition():
def _test_geometric_definition():
file = ifcopenshell.file(schema="IFC4X3_ADD2")
file = ifcopenshell.file(schema="IFC4X3")
project = file.createIfcProject(Name="Test")
geometric_representation_context = ifcopenshell.api.context.add_context(file, context_type="Model")
axis_model_representation_subcontext = ifcopenshell.api.context.add_context(
@@ -1606,7 +1606,7 @@ def _SineCurve_100_0__inf__300_1_Meter(file):
def test_map_alignment_cant_segment():
file = ifcopenshell.file(schema="IFC4X3_ADD2")
file = ifcopenshell.file(schema="IFC4X3")
_BlossCurve_100_0_300_1000_1_Meter(file)
_BlossCurve_100_0__300__1000_1_Meter(file)
_BlossCurve_100_0_300_inf_1_Meter(file)
@@ -2032,7 +2032,7 @@ def _SineCurve_100_0__inf__300_1_Meter(file):
def test_map_alignment_horizontal_segment():
file = ifcopenshell.file(schema="IFC4X3_ADD2")
file = ifcopenshell.file(schema="IFC4X3")
_BlossCurve_100_0_300_1000_1_Meter(file)
_BlossCurve_100_0__300__1000_1_Meter(file)
_BlossCurve_100_0_300_inf_1_Meter(file)
@@ -766,7 +766,7 @@ def _ParabolicArc_100_0_10_0__1_0__0_5_1_Meter(file):
def test_map_alignment_vertical_segment():
file = ifcopenshell.file(schema="IFC4X3_ADD2")
file = ifcopenshell.file(schema="IFC4X3")
_CircularArc_100_0_10_0_0_0_0_5_1_Meter(file)
_CircularArc_100_0_10_0_0_0__0_5_1_Meter(file)
_CircularArc_100_0_10_0_0_5_0_0_1_Meter(file)
@@ -22,7 +22,7 @@ import ifcopenshell.api.context
def test_name_segments():
file = ifcopenshell.file(schema="IFC4X3_ADD2")
file = ifcopenshell.file(schema="IFC4X3")
project = file.createIfcProject(Name="Test")
geometric_representation_context = ifcopenshell.api.context.add_context(file, context_type="Model")
axis_model_representation_subcontext = ifcopenshell.api.context.add_context(
@@ -22,7 +22,7 @@ import ifcopenshell.api.context
def _test1():
file = ifcopenshell.file(schema="IFC4X3_ADD2")
file = ifcopenshell.file(schema="IFC4X3")
project = file.createIfcProject(Name="Test")
geometric_representation_context = ifcopenshell.api.context.add_context(file, context_type="Model")
axis_model_representation_subcontext = ifcopenshell.api.context.add_context(
@@ -84,7 +84,7 @@ def _test1():
def _test2():
file = ifcopenshell.file(schema="IFC4X3_ADD2")
file = ifcopenshell.file(schema="IFC4X3")
project = file.createIfcProject(Name="Test")
geometric_representation_context = ifcopenshell.api.context.add_context(file, context_type="Model")
axis_model_representation_subcontext = ifcopenshell.api.context.add_context(
@@ -18,6 +18,9 @@
import ifcopenshell
import ifcopenshell.util.schema
import ifcopenshell.util.shape_builder
import ifcopenshell.util.unit
import logging
from typing import Optional
+31 -10
View File
@@ -24,6 +24,7 @@ import time
import tempfile
import typing
import itertools
import logging
import numpy as np
import multiprocessing
import ifcopenshell
@@ -62,8 +63,8 @@ DEFAULT_DATABASE_NAME = "database"
class Patcher:
def __init__(
self,
file,
logger,
file: ifcopenshell.file,
logger: logging.Logger,
sql_type: SQLTypes = "SQLite",
host: str = "localhost",
username: str = "root",
@@ -93,6 +94,8 @@ class Patcher:
entities will be separated into multiple rows. This means the ifc_id
is no longer a unique primary key. If False, lists will be stored as
JSON.
:param should_get_inverses: if True, a list of entity inverses ids will be stored
in a separate column as a json string.
:param should_get_psets: if True, a separate psets table will be created to
make it easy to query properties. This is in addition to regular IFC
tables like IfcPropertySet.
@@ -140,7 +143,7 @@ class Patcher:
# Assume it's a filepath - existing or not.
pass
self.schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(self.file.schema_identifier)
self.schema = ifcopenshell.schema_by_name(self.file.schema_identifier)
if self.sql_type == "sqlite":
self.db = sqlite3.connect(database)
@@ -278,6 +281,8 @@ class Patcher:
PRIMARY KEY (`ifc_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci;
"""
else:
assert False
self.c.execute(statement)
def create_metadata(self) -> None:
@@ -353,6 +358,7 @@ class Patcher:
else:
statement += "ifc_id INTEGER PRIMARY KEY NOT NULL UNIQUE"
assert isinstance(declaration, ifcopenshell.ifcopenshell_wrapper.entity)
total_attributes = declaration.attribute_count()
if total_attributes:
@@ -393,6 +399,7 @@ class Patcher:
statement = f"CREATE TABLE IF NOT EXISTS {ifc_class} ("
statement += "`ifc_id` int(10) unsigned NOT NULL,"
assert isinstance(declaration, ifcopenshell.ifcopenshell_wrapper.entity)
derived = declaration.derived()
for attribute in declaration.all_attributes():
primitive = ifcopenshell.util.attribute.get_primitive_type(attribute)
@@ -434,13 +441,13 @@ class Patcher:
def insert_data(self, ifc_class: str) -> None:
elements = self.file.by_type(ifc_class, include_subtypes=False)
rows = []
id_map_rows = []
pset_rows = []
rows: list[Any] = []
id_map_rows: list[tuple[int, str]] = []
pset_rows: list[tuple[int, str, str, Any]] = []
for element in elements:
nested_indices = []
values = [element.id()]
nested_indices: list[int] = []
values: list[Any] = [element.id()]
for i, attribute in enumerate(element):
if isinstance(attribute, ifcopenshell.entity_instance):
if attribute.id():
@@ -473,7 +480,7 @@ class Patcher:
else:
rows.append(values)
id_map_rows.append([element.id(), ifc_class])
id_map_rows.append((element.id(), ifc_class))
if self.should_get_psets:
psets = ifcopenshell.util.element.get_psets(element)
@@ -483,7 +490,7 @@ class Patcher:
continue
if isinstance(value, list):
value = json.dumps(value)
pset_rows.append([element.id(), pset_name, prop_name, value])
pset_rows.append((element.id(), pset_name, prop_name, value))
if self.should_get_geometry:
if element.id() not in self.shape_rows and (placement := getattr(element, "ObjectPlacement", None)):
@@ -512,6 +519,20 @@ class Patcher:
)
def get_permutations(self, lst: list[Any], indexes: list[int]) -> list[Any]:
"""
Original row (`lst`):
```
ifc_id, x, (a, b), (c,d)
```
Resulting permutations:
```
ifc_id, x, a, c
ifc_id, x, a, d
ifc_id, x, b, c
ifc_id, x, b, d
```
"""
nested_lists = [lst[i] for i in indexes]
# Generate the Cartesian product of the nested lists
@@ -18,6 +18,7 @@
import ifcopenshell
import ifcopenshell.guid
import ifcopenshell.util.element
class Patcher:
@@ -18,6 +18,7 @@
import ifcopenshell
import ifcopenshell.api.georeference
import ifcopenshell.util.geolocation
from ifcpatch.recipes import OffsetObjectPlacements, SetWorldCoordinateSystem
import typing
+2 -2
View File
@@ -23,8 +23,9 @@ import sys
import math
import datetime
import ifcopenshell
import ifcopenshell.util.unit
import ifcopenshell.util.element
import ifcopenshell.util.placement
import ifcopenshell.util.unit
from .ids import Specification, Ids
from .facet import Facet, FacetFailure
from typing import TypedDict, Union, Literal, Optional
@@ -766,7 +767,6 @@ class Bcf(Json):
def to_file(self, filepath: str) -> None:
import numpy as np
import ifcopenshell.util.placement
from bcf.v2.bcfxml import BcfXml
unit_scale = None