mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-21 04:32:23 +00:00
Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3bdc969efb | |||
| 932817877c | |||
| cb255a18b9 | |||
| 183028570e | |||
| 341cdd4ef7 | |||
| fe68e16ca3 | |||
| a8f0c90d47 | |||
| 39f550529b | |||
| 25c1a14e64 | |||
| 9f7d710119 | |||
| ea592775e4 | |||
| 42be4eb064 | |||
| 8662e20c58 | |||
| da4ce3773f | |||
| 87c9bbe2a7 | |||
| a66ee06e3d | |||
| 9969de58cb | |||
| 6995cafe63 | |||
| eaa80ad08a | |||
| d001180082 | |||
| 3884403231 | |||
| 8d4b5d83e1 | |||
| e1250f2177 |
@@ -36,7 +36,9 @@ if(NOT CMAKE_BUILD_TYPE)
|
||||
endif()
|
||||
|
||||
# use extra version to make pre-release using eg semver
|
||||
set(EXTRA_VERSION "-alpha.3")
|
||||
if(NOT DEFINED EXTRA_VERSION)
|
||||
set(EXTRA_VERSION "-alpha.3")
|
||||
endif()
|
||||
|
||||
option(MINIMAL_BUILD "The build is to make a minimal version of IFC converter from OCCT into IFC." OFF)
|
||||
option(WASM_BUILD "Build a WebAssembly binary." OFF)
|
||||
@@ -1147,9 +1149,10 @@ endif()
|
||||
|
||||
# Packaging
|
||||
list(APPEND CPACK_SOURCE_IGNORE_FILES
|
||||
.git
|
||||
.gitignore
|
||||
"/\\\\.git"
|
||||
"/build/"
|
||||
)
|
||||
set(CPACK_SOURCE_INSTALLED_DIRECTORIES "${CMAKE_SOURCE_DIR}/..;/")
|
||||
set(CPACK_PACKAGE_NAME "${PROJECT_NAME}-${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}${EXTRA_VERSION}")
|
||||
set(CPACK_SOURCE_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}${EXTRA_VERSION}")
|
||||
SET(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}${EXTRA_VERSION}-${CMAKE_SYSTEM_NAME}")
|
||||
|
||||
@@ -13,7 +13,7 @@ readme = "README.md"
|
||||
requires-python = ">=3.8"
|
||||
keywords = ["IFC", "BCF", "BIM"]
|
||||
dependencies = [
|
||||
"xsdata",
|
||||
"xsdata>=24.4",
|
||||
"numpy",
|
||||
"ifcopenshell",
|
||||
]
|
||||
|
||||
@@ -10,14 +10,14 @@ from xsdata.formats.dataclass.serializers.config import SerializerConfig
|
||||
def build_xml_parser(context: Optional[XmlContext] = None) -> XmlParser:
|
||||
"""Return a parser for an XML file."""
|
||||
parser = XmlParser(context=context or XmlContext())
|
||||
parser.register_namespace("xs", "http://www.w3.org/2001/XMLSchema")
|
||||
parser.register_namespace(ns_map=parser.ns_map, prefix="xs", uri="http://www.w3.org/2001/XMLSchema")
|
||||
return parser
|
||||
|
||||
|
||||
def build_serializer(context: Optional[XmlContext] = None) -> XmlSerializer:
|
||||
"""Return a serializer for an XML file."""
|
||||
return XmlSerializer(
|
||||
config=SerializerConfig(pretty_print=True),
|
||||
config=SerializerConfig(indent=" "),
|
||||
context=context or XmlContext(),
|
||||
)
|
||||
|
||||
@@ -36,8 +36,9 @@ class AbstractXmlParserSerializer(Protocol):
|
||||
xml: The XML file as bytes.
|
||||
clazz: The class to parse to.
|
||||
"""
|
||||
...
|
||||
|
||||
def serialize(self, obj: T, ns_map: Optional[dict[str, str]] = None) -> str:
|
||||
def serialize(self, obj: object, ns_map: Optional[dict[str, str]] = None) -> str:
|
||||
"""
|
||||
Serialize an object to XML.
|
||||
|
||||
@@ -48,6 +49,7 @@ class AbstractXmlParserSerializer(Protocol):
|
||||
Returns:
|
||||
The XML as string.
|
||||
"""
|
||||
...
|
||||
|
||||
|
||||
class XmlParserSerializer:
|
||||
@@ -68,7 +70,7 @@ class XmlParserSerializer:
|
||||
"""
|
||||
return self.parser.from_bytes(xml, clazz)
|
||||
|
||||
def serialize(self, obj: T, ns_map: Optional[dict[str, str]] = None) -> str:
|
||||
def serialize(self, obj: object, ns_map: Optional[dict[Optional[str], str]] = None) -> str:
|
||||
"""
|
||||
Serialize an object to XML.
|
||||
|
||||
@@ -79,5 +81,5 @@ class XmlParserSerializer:
|
||||
Returns:
|
||||
The XML as string.
|
||||
"""
|
||||
ns_map = ns_map or {"xs": "http://www.w3.org/2001/XMLSchema"}
|
||||
ns_map = ns_map or self.parser.ns_map or {"xs": "http://www.w3.org/2001/XMLSchema"}
|
||||
return self.serializer.render(obj, ns_map)
|
||||
|
||||
@@ -286,9 +286,9 @@ endif
|
||||
|
||||
# Required by bcf
|
||||
mkdir dist/working
|
||||
cd dist/working && wget https://files.pythonhosted.org/packages/ad/c7/17c9d16320d8e2cfdb27d2fd298b5e8f7a3f211025b0c1bc7a39a85bd690/xsdata-22.11.tar.gz
|
||||
cd dist/working && wget https://files.pythonhosted.org/packages/b4/ef/35d8118f903510f9e028f8a6a4edb615fa69e28a30d955593425a88e587a/xsdata-24.5.tar.gz
|
||||
cd dist/working && tar -xzvf xsdata*
|
||||
cp -r dist/working/xsdata-22.11/xsdata dist/blenderbim/libs/site/packages/
|
||||
cp -r dist/working/xsdata-24.5/xsdata dist/blenderbim/libs/site/packages/
|
||||
rm -rf dist/working
|
||||
|
||||
# Required by bcf
|
||||
|
||||
@@ -122,7 +122,7 @@ if sys.modules.get("bpy", None):
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.label(text="BlenderBIM could not load.", icon="ERROR")
|
||||
layout.label(text="View the console for full logs.", icon="CONSOLE")
|
||||
layout.operator("wm.console_toggle", text="View the console for full logs.", icon="CONSOLE")
|
||||
box = layout.box()
|
||||
info = get_debug_info()
|
||||
py = ".".join(info["python_version"].split(".")[0:2])
|
||||
@@ -149,14 +149,7 @@ if sys.modules.get("bpy", None):
|
||||
|
||||
def execute(self, context):
|
||||
info = format_debug_info(get_debug_info())
|
||||
|
||||
if platform.system() == "Windows":
|
||||
command = "echo | set /p nul=" + info
|
||||
elif platform.system() == "Darwin": # for MacOS
|
||||
command = 'printf "' + info.replace("\n", "\\n").replace('"', "") + '" | pbcopy'
|
||||
else: # Linux
|
||||
command = 'printf "' + info.replace("\n", "\\n").replace('"', "") + '" | xclip -selection clipboard'
|
||||
subprocess.run(command, shell=True, check=True)
|
||||
context.window_manager.clipboard = info
|
||||
return {"FINISHED"}
|
||||
|
||||
class HiddenPanel:
|
||||
|
||||
@@ -26,6 +26,7 @@ import bmesh
|
||||
import logging
|
||||
import mathutils
|
||||
import numpy as np
|
||||
import numpy.typing as npt
|
||||
import multiprocessing
|
||||
import ifcopenshell
|
||||
import ifcopenshell.geom
|
||||
@@ -303,22 +304,22 @@ class IfcImporter:
|
||||
self.update_progress(100)
|
||||
bpy.context.window_manager.progress_end()
|
||||
|
||||
def is_element_far_away(self, element):
|
||||
def is_element_far_away(self, element: ifcopenshell.entity_instance) -> bool:
|
||||
try:
|
||||
placement = ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement)
|
||||
point = placement[:, 3][0:3]
|
||||
return self.is_point_far_away(point, is_meters=False)
|
||||
except:
|
||||
pass
|
||||
return False
|
||||
|
||||
def is_point_far_away(self, point, is_meters=True):
|
||||
def is_point_far_away(
|
||||
self, point: Union[ifcopenshell.entity_instance, npt.NDArray[np.float64]], is_meters: bool = True
|
||||
) -> bool:
|
||||
# Locations greater than 1km are not considered "small sites" according to the georeferencing guide
|
||||
# Users can configure this if they have to handle larger sites but beware of surveying precision
|
||||
limit = self.ifc_import_settings.distance_limit
|
||||
limit = limit if is_meters else (limit / self.unit_scale)
|
||||
coords = point
|
||||
if hasattr(point, "Coordinates"):
|
||||
coords = point.Coordinates
|
||||
coords = getattr(point, "Coordinates", point)
|
||||
return abs(coords[0]) > limit or abs(coords[1]) > limit or abs(coords[2]) > limit
|
||||
|
||||
def process_context_filter(self):
|
||||
@@ -683,7 +684,7 @@ class IfcImporter:
|
||||
props.blender_orthogonal_height = str(offset_point[2])
|
||||
props.has_blender_offset = True
|
||||
|
||||
def get_offset_point(self):
|
||||
def get_offset_point(self) -> Union[npt.NDArray[np.float64], None]:
|
||||
elements_checked = 0
|
||||
# If more than these elements aren't far away, the file probably isn't absolutely positioned
|
||||
element_checking_threshold = 10
|
||||
@@ -718,7 +719,7 @@ class IfcImporter:
|
||||
if self.is_point_far_away(point, is_meters=False):
|
||||
return point
|
||||
|
||||
def does_element_likely_have_geometry_far_away(self, element):
|
||||
def does_element_likely_have_geometry_far_away(self, element: ifcopenshell.entity_instance) -> bool:
|
||||
for representation in element.Representation.Representations:
|
||||
items = []
|
||||
for item in representation.Items:
|
||||
@@ -735,13 +736,14 @@ class IfcImporter:
|
||||
if subelement.is_a("IfcCartesianPoint"):
|
||||
if len(subelement.Coordinates) == 3 and self.is_point_far_away(subelement, is_meters=False):
|
||||
return True
|
||||
return False
|
||||
|
||||
def apply_blender_offset_to_matrix_world(self, obj: bpy.types.Object, matrix: np.ndarray) -> mathutils.Matrix:
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
if props.has_blender_offset:
|
||||
if obj.data and obj.data.get("has_cartesian_point_offset", None):
|
||||
obj.BIMObjectProperties.blender_offset_type = "CARTESIAN_POINT"
|
||||
elif self.is_point_far_away((matrix[0, 3], matrix[1, 3], matrix[2, 3])):
|
||||
elif self.is_point_far_away((matrix[:3, 3])):
|
||||
obj.BIMObjectProperties.blender_offset_type = "OBJECT_PLACEMENT"
|
||||
matrix = ifcopenshell.util.geolocation.global2local(
|
||||
matrix,
|
||||
@@ -754,7 +756,9 @@ class IfcImporter:
|
||||
|
||||
return mathutils.Matrix(matrix.tolist())
|
||||
|
||||
def find_decomposed_ifc_class(self, element, ifc_class):
|
||||
def find_decomposed_ifc_class(
|
||||
self, element: ifcopenshell.entity_instance, ifc_class: str
|
||||
) -> Union[ifcopenshell.entity_instance, None]:
|
||||
if element.is_a(ifc_class):
|
||||
return element
|
||||
rel_aggregates = element.IsDecomposedBy
|
||||
|
||||
@@ -60,13 +60,7 @@ class CopyDebugInformation(bpy.types.Operator):
|
||||
print(text)
|
||||
print("-" * 80)
|
||||
|
||||
if platform.system() == "Windows":
|
||||
command = "echo | set /p nul=" + text
|
||||
elif platform.system() == "Darwin": # for MacOS
|
||||
command = 'printf "' + text.replace("\n", "\\n").replace('"', "") + '" | pbcopy'
|
||||
else: # Linux
|
||||
command = 'printf "' + text.replace("\n", "\\n").replace('"', "") + '" | xclip -selection clipboard'
|
||||
subprocess.run(command, shell=True, check=True)
|
||||
context.window_manager.clipboard = text
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
|
||||
@@ -159,9 +159,7 @@ class BaseDecorator:
|
||||
objecttype = "NOTDEFINED"
|
||||
|
||||
def __init__(self):
|
||||
self.font_id = blf.load(
|
||||
os.path.join(bpy.context.scene.BIMProperties.data_dir, "fonts", "OpenGost Type B TT.ttf")
|
||||
)
|
||||
self.font_id = 0 # 0 is the default font
|
||||
|
||||
# POLYLINE_UNIFORM_COLOR is good for smoothed lines since `bgl.enable(GL_LINE_SMOOTH)` is deprecated
|
||||
self.line_shader = gpu.shader.from_builtin("POLYLINE_UNIFORM_COLOR")
|
||||
@@ -404,7 +402,6 @@ class BaseDecorator:
|
||||
line_no += 1 if multiline_to_bottom else -1
|
||||
return
|
||||
|
||||
# 0 is the default font, but we're fancier than that
|
||||
font_id = self.font_id
|
||||
|
||||
color = context.preferences.addons["blenderbim"].preferences.decorations_colour
|
||||
@@ -2014,6 +2011,13 @@ class DecorationsHandler:
|
||||
for object_type in ("SLOPE_ANGLE", "SLOPE_FRACTION", "SLOPE_PERCENT"):
|
||||
self.decorators[object_type] = self.decorators["FALL"]
|
||||
self.decorators["MULTI_SYMBOL"] = self.decorators["SYMBOL"]
|
||||
if drawing_font := bpy.context.scene.DocProperties.drawing_font:
|
||||
drawing_font_path = os.path.join(bpy.context.scene.BIMProperties.data_dir, "fonts", drawing_font)
|
||||
if os.path.exists(drawing_font_path):
|
||||
font_id = blf.load(drawing_font_path)
|
||||
for decorator in self.decorators.values():
|
||||
decorator.font_id = font_id
|
||||
|
||||
|
||||
def get_objects_and_decorators(self, collection):
|
||||
# TODO: do it in data instead of the handler for performance?
|
||||
|
||||
@@ -315,9 +315,7 @@ class RasterStyleProperty(enum.Enum):
|
||||
SPACE_SHADING = "space.shading"
|
||||
|
||||
|
||||
RASTER_STYLE_PROPERTIES_EXCLUDE = (
|
||||
"scene.render.filepath",
|
||||
)
|
||||
RASTER_STYLE_PROPERTIES_EXCLUDE = ("scene.render.filepath",)
|
||||
|
||||
|
||||
class DocProperties(PropertyGroup):
|
||||
@@ -371,6 +369,7 @@ class DocProperties(PropertyGroup):
|
||||
default=os.path.join("drawings", "assets", "shading_styles.json"), name="Default Shading Styles"
|
||||
)
|
||||
shadingstyle_default: StringProperty(default="Blender Default", name="Default Shading Style")
|
||||
drawing_font: StringProperty(default="OpenGost Type B TT.ttf", name="Drawing Font")
|
||||
|
||||
|
||||
class BIMCameraProperties(PropertyGroup):
|
||||
|
||||
@@ -400,7 +400,11 @@ class UpdateRepresentation(bpy.types.Operator, Operator):
|
||||
representation_data["profile_set_usage"] = tool.Geometry.get_profile_set_usage(product)
|
||||
representation_data["text_literal"] = tool.Geometry.get_text_literal(old_representation)
|
||||
|
||||
# TODO: replace with core.add_representation?
|
||||
new_representation = ifcopenshell.api.run("geometry.add_representation", self.file, **representation_data)
|
||||
if new_representation is None:
|
||||
self.report({"ERROR"}, "Error creating representation for Blender object.")
|
||||
return {"CANCELLED"}
|
||||
|
||||
if tool.Geometry.is_body_representation(new_representation):
|
||||
[
|
||||
|
||||
@@ -23,6 +23,7 @@ import blenderbim.bim.handler
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.core.misc as core
|
||||
import blenderbim.core.geometry as core_geometry
|
||||
import blenderbim.core.root
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from mathutils import Vector, Matrix, Euler
|
||||
|
||||
@@ -134,6 +135,10 @@ class ResizeToStorey(bpy.types.Operator, Operator):
|
||||
class SplitAlongEdge(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.split_along_edge"
|
||||
bl_label = "Split Along Edge"
|
||||
bl_description = (
|
||||
"Active object is considered to be a cutting object."
|
||||
"Will unassign element from a type if type has a representation."
|
||||
)
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
@@ -144,16 +149,25 @@ class SplitAlongEdge(bpy.types.Operator, Operator):
|
||||
cutter = context.active_object
|
||||
objs = [o for o in context.selected_objects if o != cutter]
|
||||
|
||||
objs_to_cut = []
|
||||
# Splitting only works on meshes
|
||||
for obj in objs:
|
||||
# You cannot split meshes if the representation is mapped.
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if element:
|
||||
relating_type = tool.Root.get_element_type(element)
|
||||
if relating_type and tool.Root.does_type_have_representations(relating_type):
|
||||
bpy.ops.bim.unassign_type(related_object=obj.name)
|
||||
if not element:
|
||||
continue
|
||||
|
||||
relating_type = tool.Root.get_element_type(element)
|
||||
if relating_type and tool.Root.does_type_have_representations(relating_type):
|
||||
bpy.ops.bim.unassign_type(related_object=obj.name)
|
||||
|
||||
# refresh representation
|
||||
representation = tool.Geometry.get_active_representation(obj)
|
||||
|
||||
# skip empty objects that might get in the way
|
||||
if not representation:
|
||||
continue
|
||||
|
||||
core_geometry.switch_representation(
|
||||
tool.Ifc,
|
||||
tool.Geometry,
|
||||
@@ -168,11 +182,13 @@ class SplitAlongEdge(bpy.types.Operator, Operator):
|
||||
if not tool.Geometry.is_meshlike(representation):
|
||||
bpy.ops.bim.update_representation(obj=obj.name, ifc_representation_class="IfcTessellatedFaceSet")
|
||||
|
||||
new_objs = tool.Misc.split_objects_with_cutter(objs, cutter)
|
||||
objs_to_cut.append(obj)
|
||||
|
||||
new_objs = tool.Misc.split_objects_with_cutter(objs_to_cut, cutter)
|
||||
for obj in new_objs:
|
||||
blenderbim.core.root.copy_class(tool.Ifc, tool.Collector, tool.Geometry, tool.Root, obj=obj)
|
||||
bpy.ops.bim.update_representation(obj=obj.name)
|
||||
for obj in objs:
|
||||
for obj in objs_to_cut:
|
||||
bpy.ops.bim.update_representation(obj=obj.name)
|
||||
|
||||
representation = tool.Geometry.get_active_representation(obj)
|
||||
@@ -187,6 +203,8 @@ class SplitAlongEdge(bpy.types.Operator, Operator):
|
||||
apply_openings=True,
|
||||
)
|
||||
|
||||
self.report({"INFO"}, f"Splitting finished, {len(new_objs)} new objects created.")
|
||||
|
||||
|
||||
class GetConnectedSystemElements(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.get_connected_system_elements"
|
||||
|
||||
@@ -931,13 +931,11 @@ class PatchNonParametricMepSegment(bpy.types.Operator, tool.Ifc.Operator):
|
||||
return context.active_object
|
||||
|
||||
def _execute(self, context):
|
||||
styles = tool.Geometry.get_styles(context.active_object)
|
||||
blenderbim.core.material.patch_non_parametric_mep_segment(
|
||||
tool.Ifc, tool.Material, tool.Profile, obj=context.active_object
|
||||
)
|
||||
bpy.ops.bim.enable_editing_extrusion_axis()
|
||||
bpy.ops.bim.edit_extrusion_axis()
|
||||
styles = tool.Geometry.get_styles(context.active_object)
|
||||
|
||||
|
||||
class EnableEditingExtrusionAxis(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
import bpy
|
||||
import blenderbim.bim.schema
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.attribute
|
||||
import ifcopenshell.util.element
|
||||
import blenderbim.tool as tool
|
||||
from blenderbim.bim.prop import Attribute, StrProperty
|
||||
|
||||
@@ -36,10 +36,14 @@ class PsetTemplatesData:
|
||||
@classmethod
|
||||
def load(cls):
|
||||
cls.is_loaded = True
|
||||
cls.data["pset_template_files"] = cls.pset_template_files()
|
||||
|
||||
# after pset_template_files
|
||||
cls.data["pset_templates"] = cls.pset_templates()
|
||||
|
||||
# after pset_template_files because it loads IfcStore.pset_template_file
|
||||
cls.data["primary_measure_type"] = cls.primary_measure_type()
|
||||
cls.data["property_template_type"] = cls.property_template_type()
|
||||
cls.data["pset_template_files"] = cls.pset_template_files()
|
||||
cls.data["pset_templates"] = cls.pset_templates()
|
||||
cls.data["pset_template"] = cls.pset_template()
|
||||
cls.data["prop_templates"] = cls.prop_templates()
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import bpy
|
||||
import json
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util.attribute
|
||||
import blenderbim.bim.helper
|
||||
import blenderbim.bim.handler
|
||||
import blenderbim.core.structural as core
|
||||
|
||||
@@ -290,8 +290,6 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences):
|
||||
row = layout.row()
|
||||
row.prop(self, "spatial_elements_unselectable")
|
||||
|
||||
|
||||
|
||||
row = layout.row()
|
||||
row.prop(context.scene.BIMProjectProperties, "should_disable_undo_on_save")
|
||||
row = layout.row()
|
||||
@@ -320,28 +318,30 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences):
|
||||
row.prop(context.scene.BIMProperties, "data_dir")
|
||||
row.operator("bim.select_data_dir", icon="FILE_FOLDER", text="")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row = self.layout.row()
|
||||
row.prop(context.scene.BIMProperties, "pset_dir")
|
||||
row = self.layout.row(align=True)
|
||||
row = self.layout.row()
|
||||
row.prop(context.scene.DocProperties, "sheets_dir")
|
||||
row = self.layout.row(align=True)
|
||||
row = self.layout.row()
|
||||
row.prop(context.scene.DocProperties, "layouts_dir")
|
||||
row = self.layout.row(align=True)
|
||||
row = self.layout.row()
|
||||
row.prop(context.scene.DocProperties, "titleblocks_dir")
|
||||
row = self.layout.row(align=True)
|
||||
row = self.layout.row()
|
||||
row.prop(context.scene.DocProperties, "drawings_dir")
|
||||
row = self.layout.row(align=True)
|
||||
row = self.layout.row()
|
||||
row.prop(context.scene.DocProperties, "stylesheet_path")
|
||||
row = self.layout.row(align=True)
|
||||
row = self.layout.row()
|
||||
row.prop(context.scene.DocProperties, "markers_path")
|
||||
row = self.layout.row(align=True)
|
||||
row = self.layout.row()
|
||||
row.prop(context.scene.DocProperties, "symbols_path")
|
||||
row = self.layout.row(align=True)
|
||||
row = self.layout.row()
|
||||
row.prop(context.scene.DocProperties, "patterns_path")
|
||||
row = self.layout.row(align=True)
|
||||
row = self.layout.row()
|
||||
row.prop(context.scene.DocProperties, "shadingstyles_path")
|
||||
row = self.layout.row(align=True)
|
||||
row = self.layout.row()
|
||||
row.prop(context.scene.DocProperties, "shadingstyle_default")
|
||||
row = self.layout.row()
|
||||
row.prop(context.scene.DocProperties, "drawing_font")
|
||||
|
||||
|
||||
# Scene panel groups
|
||||
@@ -410,7 +410,7 @@ class BIM_PT_tabs(Panel):
|
||||
row = box.row(align=True)
|
||||
row.label(text="BlenderBIM experienced an error :(", icon="ERROR")
|
||||
row.operator("bim.close_error", text="", icon="CANCEL")
|
||||
box.label(text="View the console for full logs.", icon="CONSOLE")
|
||||
box.operator("wm.console_toggle", text="View the console for full logs.", icon="CONSOLE")
|
||||
box.operator("bim.copy_debug_information", text="Copy Error Message To Clipboard")
|
||||
op = box.operator("bim.open_uri", text="How Can I Fix This?")
|
||||
op.uri = "https://docs.blenderbim.org/users/troubleshooting.html"
|
||||
|
||||
@@ -18,10 +18,11 @@
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
import blenderbim.core.tool as tool
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import blenderbim.tool as tool
|
||||
|
||||
|
||||
def edit_object_placement(
|
||||
@@ -37,8 +38,15 @@ def edit_object_placement(
|
||||
|
||||
|
||||
def add_representation(
|
||||
ifc, geometry, style, surveyor, obj=None, context=None, ifc_representation_class=None, profile_set_usage=None
|
||||
):
|
||||
ifc: tool.Ifc,
|
||||
geometry: tool.Geometry,
|
||||
style: tool.Style,
|
||||
surveyor: tool.Surveyor,
|
||||
obj: bpy.types.Object,
|
||||
context: ifcopenshell.entity_instance,
|
||||
ifc_representation_class: Optional[str] = None,
|
||||
profile_set_usage: Optional[ifcopenshell.entity_instance] = None,
|
||||
) -> ifcopenshell.entity_instance:
|
||||
element = ifc.get_entity(obj)
|
||||
if not element:
|
||||
return
|
||||
@@ -89,15 +97,15 @@ def add_representation(
|
||||
|
||||
|
||||
def switch_representation(
|
||||
ifc,
|
||||
geometry,
|
||||
obj=None,
|
||||
representation=None,
|
||||
should_reload=True,
|
||||
is_global=True,
|
||||
should_sync_changes_first=False,
|
||||
apply_openings=True,
|
||||
):
|
||||
ifc: tool.Ifc,
|
||||
geometry: tool.Geometry,
|
||||
obj: bpy.types.Object,
|
||||
representation: ifcopenshell.entity_instance,
|
||||
should_reload: bool = True,
|
||||
is_global: bool = True,
|
||||
should_sync_changes_first: bool = False,
|
||||
apply_openings: bool = True,
|
||||
) -> None:
|
||||
"""Function can switch to representation that wasn't yet assigned to that object. See #2766.
|
||||
|
||||
`should_sync_changes_first` - sync ifc representation with current state of `obj.data`;
|
||||
|
||||
@@ -16,8 +16,18 @@
|
||||
# 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 __future__ import annotations
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
def copy_class(ifc, collector, geometry, root, obj=None):
|
||||
if TYPE_CHECKING:
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import blenderbim.tool as tool
|
||||
|
||||
|
||||
def copy_class(
|
||||
ifc: tool.Ifc, collector: tool.Collector, geometry: tool.Geometry, root: tool.Root, obj: bpy.types.Object
|
||||
) -> ifcopenshell.entity_instance:
|
||||
element = ifc.get_entity(obj)
|
||||
if not element:
|
||||
return
|
||||
@@ -48,16 +58,16 @@ def copy_class(ifc, collector, geometry, root, obj=None):
|
||||
|
||||
|
||||
def assign_class(
|
||||
ifc,
|
||||
collector,
|
||||
root,
|
||||
obj=None,
|
||||
ifc_class=None,
|
||||
predefined_type=None,
|
||||
should_add_representation=True,
|
||||
context=None,
|
||||
ifc_representation_class=None,
|
||||
):
|
||||
ifc: tool.Ifc,
|
||||
collector: tool.Collector,
|
||||
root: tool.Root,
|
||||
obj: bpy.types.Object,
|
||||
ifc_class: str,
|
||||
context: ifcopenshell.entity_instance,
|
||||
predefined_type: Optional[str] = None,
|
||||
should_add_representation: bool = True,
|
||||
ifc_representation_class: Optional[str] = None,
|
||||
) -> ifcopenshell.entity_instance:
|
||||
if ifc.get_entity(obj):
|
||||
return
|
||||
|
||||
|
||||
@@ -18,11 +18,11 @@
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import TYPE_CHECKING, Optional, Union
|
||||
import blenderbim.core.tool as tool
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import blenderbim.tool as tool
|
||||
|
||||
|
||||
def reference_structure(
|
||||
|
||||
@@ -24,8 +24,10 @@ import logging
|
||||
import numpy as np
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.geom
|
||||
import ifcopenshell.guid
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.representation
|
||||
import ifcopenshell.util.system
|
||||
import blenderbim.core.tool
|
||||
import blenderbim.core.drawing
|
||||
@@ -319,7 +321,7 @@ class Geometry(blenderbim.core.tool.Geometry):
|
||||
return new_mesh
|
||||
|
||||
@classmethod
|
||||
def get_active_representation(cls, obj):
|
||||
def get_active_representation(cls, obj: bpy.types.Object) -> Union[ifcopenshell.entity_instance, None]:
|
||||
"""< IfcShapeRepresentation or None"""
|
||||
if obj.data and hasattr(obj.data, "BIMMeshProperties") and obj.data.BIMMeshProperties.ifc_definition_id:
|
||||
return tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
@@ -459,7 +461,9 @@ class Geometry(blenderbim.core.tool.Geometry):
|
||||
return f"{representation.ContextOfItems.id()}/{representation.id()}"
|
||||
|
||||
@classmethod
|
||||
def get_styles(cls, obj, only_assigned_to_faces=False):
|
||||
def get_styles(
|
||||
cls, obj: bpy.types.Object, only_assigned_to_faces: bool = False
|
||||
) -> list[Union[ifcopenshell.entity_instance, None]]:
|
||||
styles = [tool.Style.get_style(s.material) for s in obj.material_slots if s.material]
|
||||
if not only_assigned_to_faces:
|
||||
return styles
|
||||
@@ -467,8 +471,15 @@ class Geometry(blenderbim.core.tool.Geometry):
|
||||
usage_count = [0] * len(obj.material_slots)
|
||||
if not usage_count: # if there are no materials, polygons will still use index 0
|
||||
return []
|
||||
|
||||
for poly in obj.data.polygons:
|
||||
usage_count[poly.material_index] += 1
|
||||
|
||||
# remove usages for empty material slots
|
||||
for i, slot in reversed(list(enumerate(obj.material_slots))):
|
||||
if not slot.material:
|
||||
del usage_count[i]
|
||||
|
||||
styles = [style for style, usage in zip(styles, usage_count, strict=True) if usage > 0]
|
||||
return styles
|
||||
|
||||
@@ -560,11 +571,11 @@ class Geometry(blenderbim.core.tool.Geometry):
|
||||
new.value = element[i]
|
||||
|
||||
@classmethod
|
||||
def is_body_representation(cls, representation):
|
||||
def is_body_representation(cls, representation: ifcopenshell.entity_instance) -> bool:
|
||||
return representation.ContextOfItems.ContextIdentifier == "Body"
|
||||
|
||||
@classmethod
|
||||
def is_box_representation(cls, representation):
|
||||
def is_box_representation(cls, representation: ifcopenshell.entity_instance) -> bool:
|
||||
return representation.ContextOfItems.ContextIdentifier == "Box"
|
||||
|
||||
@classmethod
|
||||
@@ -572,11 +583,11 @@ class Geometry(blenderbim.core.tool.Geometry):
|
||||
return not all([tool.Cad.is_x(o, 1.0) for o in obj.scale]) or obj in IfcStore.edited_objs
|
||||
|
||||
@classmethod
|
||||
def is_mapped_representation(cls, representation):
|
||||
def is_mapped_representation(cls, representation: ifcopenshell.entity_instance) -> bool:
|
||||
return representation.RepresentationType == "MappedRepresentation"
|
||||
|
||||
@classmethod
|
||||
def is_meshlike(cls, representation):
|
||||
def is_meshlike(cls, representation: ifcopenshell.entity_instance) -> bool:
|
||||
if ifcopenshell.util.representation.resolve_representation(representation).RepresentationType in (
|
||||
"AdvancedBrep",
|
||||
"Annotation2D",
|
||||
@@ -656,7 +667,9 @@ class Geometry(blenderbim.core.tool.Geometry):
|
||||
bpy.data.objects.remove(obj)
|
||||
|
||||
@classmethod
|
||||
def resolve_mapped_representation(cls, representation):
|
||||
def resolve_mapped_representation(
|
||||
cls, representation: ifcopenshell.entity_instance
|
||||
) -> ifcopenshell.entity_instance:
|
||||
if representation.RepresentationType == "MappedRepresentation":
|
||||
return cls.resolve_mapped_representation(representation.Items[0].MappingSource.MappedRepresentation)
|
||||
return representation
|
||||
|
||||
@@ -97,7 +97,9 @@ class Misc(blenderbim.core.tool.Misc):
|
||||
IfcStore.edited_objs.add(obj)
|
||||
|
||||
@classmethod
|
||||
def split_objects_with_cutter(cls, objs, cutter):
|
||||
def split_objects_with_cutter(
|
||||
cls, objs: list[bpy.types.Object], cutter: bpy.types.Object
|
||||
) -> list[bpy.types.Object]:
|
||||
cutter_mesh = cutter.data
|
||||
|
||||
bm = bmesh.new()
|
||||
|
||||
@@ -21,6 +21,7 @@ import bpy
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.unit
|
||||
import blenderbim.core.tool
|
||||
import blenderbim.core.root
|
||||
import blenderbim.bim.schema
|
||||
import blenderbim.tool as tool
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
|
||||
@@ -29,6 +29,7 @@ import blenderbim.core.style
|
||||
import blenderbim.tool as tool
|
||||
from mathutils import Vector
|
||||
from blenderbim.bim.module.model.opening import FilledOpeningGenerator
|
||||
from typing import Union, Optional
|
||||
|
||||
|
||||
class Root(blenderbim.core.tool.Root):
|
||||
@@ -129,7 +130,7 @@ class Root(blenderbim.core.tool.Root):
|
||||
return ifcopenshell.util.representation.get_representation(element, context=context.ContextType)
|
||||
|
||||
@classmethod
|
||||
def get_element_type(cls, element):
|
||||
def get_element_type(cls, element: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
|
||||
return ifcopenshell.util.element.get_type(element)
|
||||
|
||||
@classmethod
|
||||
@@ -282,8 +283,12 @@ class Root(blenderbim.core.tool.Root):
|
||||
|
||||
@classmethod
|
||||
def run_geometry_add_representation(
|
||||
cls, obj=None, context=None, ifc_representation_class=None, profile_set_usage=None
|
||||
):
|
||||
cls,
|
||||
obj: bpy.types.Object,
|
||||
context: ifcopenshell.entity_instance,
|
||||
ifc_representation_class: Optional[str] = None,
|
||||
profile_set_usage: Optional[ifcopenshell.entity_instance] = None,
|
||||
) -> ifcopenshell.entity_instance:
|
||||
return blenderbim.core.geometry.add_representation(
|
||||
tool.Ifc,
|
||||
tool.Geometry,
|
||||
|
||||
@@ -105,6 +105,7 @@ For Linux or Mac:
|
||||
$ rm -r $BLENDER_ADDON_PATH/libs/site/packages/ifccsv.py
|
||||
$ rm -r $BLENDER_ADDON_PATH/libs/site/packages/ifcdiff.py
|
||||
$ rm -r $BLENDER_ADDON_PATH/libs/site/packages/bsdd.py
|
||||
$ rm -r $BLENDER_ADDON_PATH/libs/site/packages/bcf
|
||||
$ rm -r $BLENDER_ADDON_PATH/libs/site/packages/ifc4d
|
||||
$ rm -r $BLENDER_ADDON_PATH/libs/site/packages/ifc5d
|
||||
$ rm -r $BLENDER_ADDON_PATH/libs/site/packages/ifccityjson
|
||||
@@ -117,6 +118,7 @@ For Linux or Mac:
|
||||
$ ln -s $PWD/src/ifccsv/ifccsv.py $BLENDER_ADDON_PATH/libs/site/packages/ifccsv.py
|
||||
$ ln -s $PWD/src/ifcdiff/ifcdiff.py $BLENDER_ADDON_PATH/libs/site/packages/ifcdiff.py
|
||||
$ ln -s $PWD/src/bsdd/bsdd.py $BLENDER_ADDON_PATH/libs/site/packages/bsdd.py
|
||||
$ ln -s $PWD/src/bcf/src/bcf $BLENDER_ADDON_PATH/libs/site/packages/bcf
|
||||
$ ln -s $PWD/src/ifc4d/ifc4d $BLENDER_ADDON_PATH/libs/site/packages/ifc4d
|
||||
$ ln -s $PWD/src/ifc5d/ifc5d $BLENDER_ADDON_PATH/libs/site/packages/ifc5d
|
||||
$ ln -s $PWD/src/ifccityjson/ifccityjson $BLENDER_ADDON_PATH/libs/site/packages/ifccityjson
|
||||
@@ -172,6 +174,7 @@ Before running it follow the instructions descibed after `rem` tags.
|
||||
del "%blenderbim%\libs\site\packages\ifccsv.py"
|
||||
del "%blenderbim%\libs\site\packages\ifcdiff.py"
|
||||
del "%blenderbim%\libs\site\packages\bsdd.py"
|
||||
rd /S /Q "%blenderbim%\libs\site\packages\bcf"
|
||||
rd /S /Q "%blenderbim%\libs\site\packages\ifc4d"
|
||||
rd /S /Q "%blenderbim%\libs\site\packages\ifc5d"
|
||||
rd /S /Q "%blenderbim%\libs\site\packages\ifccityjson"
|
||||
@@ -184,6 +187,7 @@ Before running it follow the instructions descibed after `rem` tags.
|
||||
mklink "%blenderbim%\libs\site\packages\ifccsv.py" "%cd%\src\ifccsv\ifccsv.py"
|
||||
mklink "%blenderbim%\libs\site\packages\ifcdiff.py" "%cd%\src\ifcdiff\ifcdiff.py"
|
||||
mklink "%blenderbim%\libs\site\packages\bsdd.py" "%cd%\src\bsdd\bsdd.py"
|
||||
mklink /D "%blenderbim%\libs\site\packages\bcf" "%cd%\src\bcf\src\bcf"
|
||||
mklink /D "%blenderbim%\libs\site\packages\ifc4d" "%cd%\src\ifc4d\ifc4d"
|
||||
mklink /D "%blenderbim%\libs\site\packages\ifc5d" "%cd%\src\ifc5d\ifc5d"
|
||||
mklink /D "%blenderbim%\libs\site\packages\ifccityjson" "%cd%\src\ifccityjson\ifccityjson"
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
import bpy
|
||||
import math
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import blenderbim.core.tool
|
||||
import blenderbim.tool as tool
|
||||
from mathutils import Vector
|
||||
|
||||
@@ -59,7 +59,6 @@
|
||||
#include <GeomAPI_ProjectPointOnSurf.hxx>
|
||||
#include <Geom_Plane.hxx>
|
||||
#include <IntTools_FaceFace.hxx>
|
||||
#include <STEPConstruct_PointHasher.hxx>
|
||||
#include "clash_utils.h"
|
||||
|
||||
#ifdef WITH_HDF5
|
||||
|
||||
@@ -17,12 +17,12 @@
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import ifcopenshell
|
||||
from typing import Optional
|
||||
from typing import Optional, Literal
|
||||
|
||||
|
||||
def add_context(
|
||||
file: ifcopenshell.file,
|
||||
context_type: str,
|
||||
context_type: Optional[Literal["Model", "Plan"]] = None,
|
||||
context_identifier: Optional[str] = None,
|
||||
target_view: Optional[str] = None,
|
||||
parent: Optional[ifcopenshell.entity_instance] = None,
|
||||
@@ -96,7 +96,7 @@ def add_context(
|
||||
|
||||
:param context_type: The type of the context, must be one of "Model" or
|
||||
"Plan" only.
|
||||
:type context_type: str
|
||||
:type context_type: str, optional
|
||||
:param context_identifier: The identifier of the context, chosen from
|
||||
one of the common identifiers above or consult the IFC documentation
|
||||
(under the IfcShapeRepresentation page) for more details. Optional
|
||||
|
||||
@@ -21,7 +21,7 @@ import math
|
||||
import bmesh
|
||||
import ifcopenshell.util.unit
|
||||
from mathutils import Vector, Matrix
|
||||
from typing import Union, Optional, Literal
|
||||
from typing import Union, Optional, Literal, Any
|
||||
|
||||
|
||||
Z_AXIS = Vector((0, 0, 1))
|
||||
@@ -93,6 +93,9 @@ def add_representation(
|
||||
|
||||
|
||||
class Usecase:
|
||||
file: ifcopenshell.file
|
||||
settings: dict[str, Any]
|
||||
|
||||
def execute(self):
|
||||
self.is_manifold = None
|
||||
if (
|
||||
@@ -501,11 +504,10 @@ class Usecase:
|
||||
geom_data = self.settings["geometry"]
|
||||
|
||||
if isinstance(geom_data, bpy.types.Mesh):
|
||||
if not self.is_mesh_curve_consecutive(geom_data):
|
||||
return
|
||||
if self.file.schema == "IFC2X3":
|
||||
return self.create_curves_from_mesh_ifc2x3(should_exclude_faces=should_exclude_faces, is_2d=is_2d)
|
||||
return self.create_curves_from_mesh(should_exclude_faces=should_exclude_faces, is_2d=is_2d)
|
||||
if self.is_mesh_curve_consecutive(geom_data):
|
||||
if self.file.schema == "IFC2X3":
|
||||
return self.create_curves_from_mesh_ifc2x3(should_exclude_faces=should_exclude_faces, is_2d=is_2d)
|
||||
return self.create_curves_from_mesh(should_exclude_faces=should_exclude_faces, is_2d=is_2d)
|
||||
|
||||
import blenderbim.tool as tool
|
||||
|
||||
|
||||
@@ -16,8 +16,13 @@
|
||||
# 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 ifcopenshell.ifcopenshell_wrapper as ifcopenshell_wrapper
|
||||
from typing import Union
|
||||
|
||||
def get_primitive_type(attribute_or_data_type):
|
||||
|
||||
def get_primitive_type(
|
||||
attribute_or_data_type: Union[ifcopenshell_wrapper.attribute, ifcopenshell_wrapper.parameter_type]
|
||||
) -> Union[str, tuple[str, list[str]]]:
|
||||
if hasattr(attribute_or_data_type, "type_of_attribute"):
|
||||
data_type = str(attribute_or_data_type.type_of_attribute())
|
||||
else:
|
||||
|
||||
@@ -446,13 +446,13 @@ def get_predefined_type(element: ifcopenshell.entity_instance) -> str:
|
||||
return predefined_type
|
||||
|
||||
|
||||
def get_type(element: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance:
|
||||
def get_type(element: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
|
||||
"""Retrieves the construction type element of an element occurrence
|
||||
|
||||
:param element: The element occurrence
|
||||
:type: ifcopenshell.entity_instance
|
||||
:return: The related type element
|
||||
:rtype: ifcopenshell.entity_instance
|
||||
:rtype: Union[ifcopenshell.entity_instance, None]
|
||||
|
||||
Example:
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import lark
|
||||
import numpy as np
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util
|
||||
import ifcopenshell.util.attribute
|
||||
import ifcopenshell.util.fm
|
||||
import ifcopenshell.util.unit
|
||||
import ifcopenshell.util.element
|
||||
@@ -30,7 +31,7 @@ import ifcopenshell.util.classification
|
||||
import ifcopenshell.util.schema
|
||||
import ifcopenshell.util.shape
|
||||
from decimal import Decimal
|
||||
from typing import Optional, Any, Union
|
||||
from typing import Optional, Any, Union, Iterable
|
||||
|
||||
|
||||
filter_elements_grammar = lark.Lark(
|
||||
@@ -324,12 +325,16 @@ def filter_elements(
|
||||
return transformer.get_results()
|
||||
|
||||
|
||||
class SetElementValueException(Exception): ...
|
||||
|
||||
|
||||
def set_element_value(
|
||||
ifc_file: ifcopenshell.file,
|
||||
element: ifcopenshell.entity_instance,
|
||||
element: Union[ifcopenshell.entity_instance, Iterable[ifcopenshell.entity_instance], None],
|
||||
query: Union[str, list[str]],
|
||||
value: Optional[str],
|
||||
) -> Union[ifcopenshell.entity_instance, None]:
|
||||
value: Any,
|
||||
) -> None:
|
||||
original_element = element
|
||||
if isinstance(query, (list, tuple)):
|
||||
keys = query
|
||||
else:
|
||||
@@ -392,12 +397,21 @@ def set_element_value(
|
||||
ifcopenshell.api.run(
|
||||
"geometry.edit_object_placement", ifc_file, product=element, matrix=matrix, is_si=False
|
||||
)
|
||||
return
|
||||
elif isinstance(element, ifcopenshell.entity_instance):
|
||||
if key == "Name" and element.is_a("IfcMaterialLayerSet"):
|
||||
key = "LayerSetName" # This oddity in the IFC spec is annoying so we account for it.
|
||||
|
||||
if isinstance(key, str) and hasattr(element, key):
|
||||
if getattr(element, key) != value:
|
||||
if isinstance(key, str) and ((current_value := getattr(element, key, ...)) is not ...):
|
||||
# check if key is not last
|
||||
if len(keys) != i + 1:
|
||||
element = current_value
|
||||
continue
|
||||
|
||||
if current_value == value:
|
||||
return
|
||||
else:
|
||||
# check if key is not last
|
||||
try:
|
||||
# Try our luck
|
||||
return setattr(element, key, value)
|
||||
@@ -418,9 +432,13 @@ def set_element_value(
|
||||
if value in ("True", "true", "TRUE", "Yes", "1"):
|
||||
value = True
|
||||
elif value in ("False", "false", "FALSE", "No", "0"):
|
||||
value = True
|
||||
value = False
|
||||
else:
|
||||
value = bool(value)
|
||||
elif data_type == "entity":
|
||||
value = ifc_file.by_guid(value)
|
||||
if current_value == value:
|
||||
return
|
||||
return setattr(element, key, value)
|
||||
else:
|
||||
# Try to extract pset
|
||||
@@ -463,18 +481,20 @@ def set_element_value(
|
||||
except:
|
||||
pass
|
||||
return
|
||||
elif isinstance(element, (list, tuple)): # If we use regex
|
||||
elif isinstance(element, (list, tuple, set)): # If we use regex
|
||||
if key.isnumeric():
|
||||
try:
|
||||
element = element[int(key)]
|
||||
except IndexError:
|
||||
return
|
||||
else:
|
||||
results = []
|
||||
for v in element:
|
||||
cls.set_element_value(ifc_file, v, keys[i + 1 :], value)
|
||||
set_element_value(ifc_file, v, keys[i:], value)
|
||||
return
|
||||
|
||||
raise SetElementValueException(
|
||||
f"Failed to set value for element '{original_element}' with query '{query}' (invalid or unsupported query)."
|
||||
)
|
||||
|
||||
class FacetTransformer(lark.Transformer):
|
||||
def __init__(self, ifc_file: ifcopenshell.file, elements: Optional[set[ifcopenshell.entity_instance]] = None):
|
||||
|
||||
@@ -311,6 +311,13 @@ class TestSetElementValue(test.bootstrap.IFC4):
|
||||
subject.set_element_value(self.file, element, "Name", 123)
|
||||
assert element.Name == "123"
|
||||
|
||||
def test_set_attributes_attribute(self):
|
||||
material = self.file.create_entity("IfcMaterial")
|
||||
layer = self.file.create_entity("IfcMaterialLayer")
|
||||
layer.Material = material
|
||||
subject.set_element_value(self.file, layer, "Material.Name", "Foo")
|
||||
assert material.Name == "Foo"
|
||||
|
||||
|
||||
class TestSelector(test.bootstrap.IFC4):
|
||||
def test_selecting_from_specified_elements(self):
|
||||
|
||||
@@ -16,9 +16,29 @@
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcPatch. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import logging
|
||||
import numpy as np
|
||||
import numpy.typing as npt
|
||||
import ifcopenshell
|
||||
from typing import Literal, Optional, Union
|
||||
|
||||
|
||||
class Patcher:
|
||||
def __init__(self, src, file, logger, mode="geometry", a=None, b=None, c=None, d=None):
|
||||
def __init__(
|
||||
self,
|
||||
src: str,
|
||||
file: ifcopenshell.file,
|
||||
logger: logging.Logger,
|
||||
mode: Literal[
|
||||
"geometry",
|
||||
"placement",
|
||||
"both",
|
||||
] = "geometry",
|
||||
a: Optional[float] = None,
|
||||
b: Optional[float] = None,
|
||||
c: Optional[float] = None,
|
||||
d: Optional[float] = None,
|
||||
):
|
||||
"""Reset any large coordinates to smaller coordinates based on a threshold
|
||||
|
||||
If you find large coordinates in your model, the large coordinates may
|
||||
@@ -151,7 +171,7 @@ class Patcher:
|
||||
point.Coordinates[2] + offset_point[2],
|
||||
)
|
||||
|
||||
def is_point_far_away(self, point):
|
||||
def is_point_far_away(self, point: Union[ifcopenshell.entity_instance, npt.NDArray[np.float64]]) -> bool:
|
||||
if hasattr(point, "Coordinates"):
|
||||
return (
|
||||
abs(point.Coordinates[0]) > self.threshold
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util.representation
|
||||
from ifcopenshell import template
|
||||
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcsverchok.helper
|
||||
from sverchok.node_tree import SverchCustomTreeNode
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import bpy
|
||||
import ifcopenshell
|
||||
import ifcsverchok.helper
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util.representation
|
||||
from ifcsverchok.ifcstore import SvIfcStore
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.core.geometry as core
|
||||
|
||||
Reference in New Issue
Block a user