Merge branch 'IfcOpenShell:v0.7.0' into v0.7.0

This commit is contained in:
Carlos Villagrasa
2022-07-09 01:19:15 +02:00
committed by GitHub
52 changed files with 549 additions and 354 deletions
+95
View File
@@ -0,0 +1,95 @@
name: ci_py_only
on:
push:
paths:
- 'src/**'
- 'test/**'
- 'conda/**'
- 'cmake/**'
- '.github/workflows/ci_py_only.yml'
pull_request:
jobs:
activate:
runs-on: ubuntu-latest
if: |
github.repository == 'IfcOpenShell/IfcOpenShell' &&
!contains(github.event.head_commit.message, 'skip ci')
steps:
- run: echo ok go
build:
runs-on: ubuntu-20.04
needs: activate
steps:
- uses: actions/checkout@v2
with:
submodules: recursive
- name: Install C++ dependencies
run: |
sudo apt update
sudo apt-get install --no-install-recommends \
git cmake gcc g++ \
libboost-date-time-dev \
libboost-filesystem-dev \
libboost-iostreams-dev \
libboost-program-options-dev \
libboost-regex-dev \
libboost-system-dev \
libboost-thread-dev \
python3-all-dev python3-pip \
swig libpcre3-dev libxml2-dev \
libtbb-dev nlohmann-json3-dev \
libocct-foundation-dev libocct-modeling-algorithms-dev libocct-modeling-data-dev libocct-ocaf-dev libocct-visualization-dev libocct-data-exchange-dev \
libhdf5-dev libcgal-dev
- name: ccache
uses: hendrikmuhs/ccache-action@v1
- name: Build ifcopenshell
run: |
mkdir build && cd build
cmake \
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_PREFIX_PATH=/usr \
-DCMAKE_SYSTEM_PREFIX_PATH=/usr \
-DOCC_INCLUDE_DIR=/usr/include/opencascade \
-DOCC_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu \
-DPYTHON_EXECUTABLE:FILEPATH=/usr/bin/python3 \
-DPYTHON_INCLUDE_DIR:PATH=/usr/include/python3.8 \
-DPYTHON_LIBRARY:FILEPATH=/usr/lib/x86_64-linux-gnu/libpython3.8.so \
-DCOLLADA_SUPPORT=Off \
"-DSCHEMA_VERSIONS=2x3;4" \
-DBUILD_CONVERT=Off \
-DGLTF_SUPPORT=On \
-DJSON_INCLUDE_DIR=/usr/include \
-DCGAL_INCLUDE_DIR=/usr/include \
-DGMP_INCLUDE_DIR=/usr/include \
-DMPFR_INCLUDE_DIR=/usr/include \
-DGMP_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu \
-DMPFR_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu \
-DHDF5_INCLUDE_DIR=/usr/include/hdf5/serial \
../cmake
sudo make -j $(nproc)
sudo make install
- name: Install Python dependencies
run: |
sudo /usr/bin/python -m pip install -U pip
sudo /usr/bin/python -m pip install xmlschema numpy lxml
sudo /usr/bin/python -m pip install src/bcf
sudo /usr/bin/python -m pip install pytest
sudo /usr/bin/python -m pip install isodate
sudo /usr/bin/python -m pip install lark
sudo /usr/bin/python -m pip install networkx
- name: Test
run: |
cd test
sudo /usr/bin/python tests.py
cd ../src/ifcopenshell-python
mv ifcopenshell ifcopenshell-local # Force testing on installed module
make test
+1 -1
View File
@@ -10,4 +10,4 @@
url = https://github.com/IfcOpenShell/svgfill url = https://github.com/IfcOpenShell/svgfill
[submodule "src/ifcopenshell-python/test/Sample-BIM-Files"] [submodule "src/ifcopenshell-python/test/Sample-BIM-Files"]
path = src/ifcopenshell-python/test/Sample-BIM-Files path = src/ifcopenshell-python/test/Sample-BIM-Files
url = https://github.com/atomczak/Sample-BIM-Files url = https://github.com/IfcOpenShell/ids-test-files
+6 -2
View File
@@ -666,7 +666,7 @@ if (BUILD_IFCGEOM)
endforeach() endforeach()
set(IFCOPENSHELL_LIBRARIES ${IFCOPENSHELL_LIBRARIES} IfcGeom ${IFCGEOM_SCHEMA_LIBRARIES} IfcGeom ${IFCGEOM_SCHEMA_LIBRARIES}) set(IFCOPENSHELL_LIBRARIES ${IFCOPENSHELL_LIBRARIES} IfcGeom ${IFCGEOM_SCHEMA_LIBRARIES} IfcGeom ${IFCGEOM_SCHEMA_LIBRARIES})
endif() endif()
if (BUILD_CONVERT) if (BUILD_CONVERT OR BUILD_IFCPYTHON)
foreach(s ${SCHEMA_VERSIONS}) foreach(s ${SCHEMA_VERSIONS})
set(SERIALIZER_SCHEMA_LIBRARIES ${SERIALIZER_SCHEMA_LIBRARIES} Serializers_ifc${s}) set(SERIALIZER_SCHEMA_LIBRARIES ${SERIALIZER_SCHEMA_LIBRARIES} Serializers_ifc${s})
endforeach() endforeach()
@@ -739,7 +739,7 @@ TARGET_LINK_LIBRARIES(IfcGeom IfcParse ${IFCGEOM_SCHEMA_LIBRARIES} ${CMAKE_THREA
endif(BUILD_IFCGEOM) endif(BUILD_IFCGEOM)
if (BUILD_CONVERT) if (BUILD_CONVERT OR BUILD_IFCPYTHON)
# Serializers # Serializers
file(GLOB SERIALIZERS_H_FILES ../src/serializers/*.h) file(GLOB SERIALIZERS_H_FILES ../src/serializers/*.h)
@@ -760,6 +760,10 @@ set_target_properties(Serializers PROPERTIES COMPILE_FLAGS "-DIFC_GEOM_EXPORTS $
TARGET_LINK_LIBRARIES(Serializers ${SERIALIZER_SCHEMA_LIBRARIES} ${OPENCOLLADA_LIBRARIES}) TARGET_LINK_LIBRARIES(Serializers ${SERIALIZER_SCHEMA_LIBRARIES} ${OPENCOLLADA_LIBRARIES})
endif(BUILD_CONVERT or BUILD_IFCPYTHON)
if (BUILD_CONVERT)
# IfcConvert # IfcConvert
file(GLOB IFCCONVERT_CPP_FILES ../src/ifcconvert/*.cpp) file(GLOB IFCCONVERT_CPP_FILES ../src/ifcconvert/*.cpp)
file(GLOB IFCCONVERT_H_FILES ../src/ifcconvert/*.h) file(GLOB IFCCONVERT_H_FILES ../src/ifcconvert/*.h)
@@ -93,6 +93,7 @@ classes = [
operator.SelectSchemaDir, operator.SelectSchemaDir,
operator.SelectURIAttribute, operator.SelectURIAttribute,
prop.StrProperty, prop.StrProperty,
operator.BIM_OT_enum_property_search, # /!\ Register AFTER prop.StrProperty
prop.ObjProperty, prop.ObjProperty,
prop.Attribute, prop.Attribute,
prop.ModuleVisibility, prop.ModuleVisibility,
+12 -9
View File
@@ -38,15 +38,8 @@ def draw_attribute(attribute, layout, copy_operator=None):
if not value_name: if not value_name:
layout.label(text=attribute.name) layout.label(text=attribute.name)
return return
if len(attribute.enumerated_values) != 0: if value_name == "enum_value":
layout.label(text=attribute.name) prop_with_search(layout, attribute, "enum_value", text=attribute.name)
grid = layout.column_flow(columns=3)
for e in attribute.enumerated_values:
grid.prop(
e,
"is_selected",
text=str(e[value_name])
)
else: else:
layout.prop( layout.prop(
attribute, attribute,
@@ -115,6 +108,16 @@ def export_attributes(props, callback=None):
return attributes return attributes
def prop_with_search(layout, data, prop_name, **kwargs):
# kwargs are layout.prop arguments (text, icon, etc.)
row = layout.row(align=True)
# Magick courtesy of https://blender.stackexchange.com/a/203443/86891
row.context_pointer_set(name="data", data=data)
row.prop(data, prop_name, **kwargs)
op = row.operator("bim.enum_property_search", text="", icon="VIEWZOOM")
op.prop_name = prop_name
class IfcHeaderExtractor: class IfcHeaderExtractor:
def __init__(self, filepath: str): def __init__(self, filepath: str):
self.filepath = filepath self.filepath = filepath
@@ -18,7 +18,7 @@
import blenderbim.tool as tool import blenderbim.tool as tool
from bpy.types import Panel, UIList from bpy.types import Panel, UIList
from blenderbim.bim.ui import prop_with_search from blenderbim.bim.helper import prop_with_search
from blenderbim.bim.module.brick.data import BrickschemaData, BrickschemaReferencesData from blenderbim.bim.module.brick.data import BrickschemaData, BrickschemaReferencesData
@@ -112,6 +112,7 @@ class CreateAllShapes(bpy.types.Operator):
def execute(self, context): def execute(self, context):
self.file = IfcStore.get_file() self.file = IfcStore.get_file()
elements = self.file.by_type("IfcElement") + self.file.by_type("IfcSpace") elements = self.file.by_type("IfcElement") + self.file.by_type("IfcSpace")
total = len(elements) total = len(elements)
settings = ifcopenshell.geom.settings() settings = ifcopenshell.geom.settings()
failures = [] failures = []
@@ -392,6 +392,7 @@ class BIMTextProperties(PropertyGroup):
name="Font Size", name="Font Size",
) )
class BIMAssignedProductProperties(PropertyGroup): class BIMAssignedProductProperties(PropertyGroup):
is_editing_product: BoolProperty(name="Is Editing Product", default=False) is_editing_product: BoolProperty(name="Is Editing Product", default=False)
relating_product: PointerProperty(name="Relating Product", type=bpy.types.Object) relating_product: PointerProperty(name="Relating Product", type=bpy.types.Object)
@@ -118,7 +118,7 @@ class SheetBuilder:
sheet_tree = ET.parse(sheet_path) sheet_tree = ET.parse(sheet_path)
sheet_root = sheet_tree.getroot() sheet_root = sheet_tree.getroot()
for g in sheet_root.findall('{http://www.w3.org/2000/svg}g'): for g in sheet_root.findall("{http://www.w3.org/2000/svg}g"):
if g.attrib.get("data-id") == str(reference.id()): if g.attrib.get("data-id") == str(reference.id()):
sheet_root.remove(g) sheet_root.remove(g)
break break
@@ -70,7 +70,7 @@ class SvgWriter:
viewBox=("0 0 {} {}".format(self.width, self.height)), viewBox=("0 0 {} {}".format(self.width, self.height)),
id="root", id="root",
data_scale=self.human_scale, data_scale=self.human_scale,
debug=False, # Disable validation so that we can insert the IFC namespace debug=False, # Disable validation so that we can insert the IFC namespace
) )
self.svg.attribs["xmlns:ifc"] = "http://www.ifcopenshell.org/ns" self.svg.attribs["xmlns:ifc"] = "http://www.ifcopenshell.org/ns"
return self return self
@@ -81,7 +81,9 @@ class SvgWriter:
def draw_underlay(self, image): def draw_underlay(self, image):
self.svg.add( self.svg.add(
self.svg.image( self.svg.image(
os.path.join("..", "diagrams", os.path.basename(image)), width=self.width, height=self.height, os.path.join("..", "diagrams", os.path.basename(image)),
width=self.width,
height=self.height,
) )
) )
return self return self
@@ -252,7 +254,9 @@ class SvgWriter:
end = Vector(((x_offset + v1.x), (y_offset - v1.y))) end = Vector(((x_offset + v1.x), (y_offset - v1.y)))
vector = end - start vector = end - start
line = self.svg.add( line = self.svg.add(
self.svg.line(start=tuple(start * self.svg_scale), end=tuple(end * self.svg_scale), class_=" ".join(classes)) self.svg.line(
start=tuple(start * self.svg_scale), end=tuple(end * self.svg_scale), class_=" ".join(classes)
)
) )
line["stroke-dasharray"] = "12.5, 3, 3, 3" line["stroke-dasharray"] = "12.5, 3, 3, 3"
axis_tag = tool.Ifc.get_entity(obj).Name axis_tag = tool.Ifc.get_entity(obj).Name
@@ -361,7 +365,9 @@ class SvgWriter:
end = Vector(((x_offset + v1.x), (y_offset - v1.y))) end = Vector(((x_offset + v1.x), (y_offset - v1.y)))
vector = end - start vector = end - start
line = self.svg.add( line = self.svg.add(
self.svg.line(start=tuple(start * self.svg_scale), end=tuple(end * self.svg_scale), class_=" ".join(classes)) self.svg.line(
start=tuple(start * self.svg_scale), end=tuple(end * self.svg_scale), class_=" ".join(classes)
)
) )
def draw_leader_annotation(self, obj): def draw_leader_annotation(self, obj):
@@ -427,7 +433,9 @@ class SvgWriter:
(symbol_position * self.svg_scale)[1], (symbol_position * self.svg_scale)[1],
) )
self.svg.add(self.svg.use("#elevation-arrow", insert=tuple(symbol_position * self.svg_scale), transform=transform)) self.svg.add(
self.svg.use("#elevation-arrow", insert=tuple(symbol_position * self.svg_scale), transform=transform)
)
self.svg.add(self.svg.use("#elevation-tag", insert=tuple(symbol_position * self.svg_scale))) self.svg.add(self.svg.use("#elevation-tag", insert=tuple(symbol_position * self.svg_scale)))
reference_id, sheet_id = self.get_reference_and_sheet_id_from_annotation(tool.Ifc.get_entity(obj)) reference_id, sheet_id = self.get_reference_and_sheet_id_from_annotation(tool.Ifc.get_entity(obj))
@@ -666,7 +674,7 @@ class SvgWriter:
dir2 = ((arc_matrix.inverted() @ arc_end_pts[1]) - (arc_matrix.inverted() @ center)).normalized() dir2 = ((arc_matrix.inverted() @ arc_end_pts[1]) - (arc_matrix.inverted() @ center)).normalized()
angle = -dir1.xy.angle_signed(dir2.xy) angle = -dir1.xy.angle_signed(dir2.xy)
#if is_reflex: # if is_reflex:
# angle = angle % (math.pi * 2) # angle = angle % (math.pi * 2)
# Center of gravity of all vertices, used to help position the text # Center of gravity of all vertices, used to help position the text
@@ -680,7 +688,9 @@ class SvgWriter:
arc_midpoint = center + ((cog - center).normalized() * radius) arc_midpoint = center + ((cog - center).normalized() * radius)
text_position = self.project_point_onto_camera(arc_midpoint) text_position = self.project_point_onto_camera(arc_midpoint)
text_position = Vector(((x_offset + text_position.x) * self.svg_scale, (y_offset - text_position.y) * self.svg_scale)) text_position = Vector(
((x_offset + text_position.x) * self.svg_scale, (y_offset - text_position.y) * self.svg_scale)
)
center_projected = self.project_point_onto_camera(center) center_projected = self.project_point_onto_camera(center)
center_position = Vector( center_position = Vector(
@@ -704,8 +714,12 @@ class SvgWriter:
# Draw SVG arc, see for details: http://xahlee.info/js/svg_circle_arc.html # Draw SVG arc, see for details: http://xahlee.info/js/svg_circle_arc.html
arc_proj_end_pts = [self.project_point_onto_camera(v) for v in arc_end_pts] arc_proj_end_pts = [self.project_point_onto_camera(v) for v in arc_end_pts]
p1 = Vector(((x_offset + arc_proj_end_pts[0].x) * self.svg_scale, (y_offset - arc_proj_end_pts[0].y) * self.svg_scale)) p1 = Vector(
p2 = Vector(((x_offset + arc_proj_end_pts[1].x) * self.svg_scale, (y_offset - arc_proj_end_pts[1].y) * self.svg_scale)) ((x_offset + arc_proj_end_pts[0].x) * self.svg_scale, (y_offset - arc_proj_end_pts[0].y) * self.svg_scale)
)
p2 = Vector(
((x_offset + arc_proj_end_pts[1].x) * self.svg_scale, (y_offset - arc_proj_end_pts[1].y) * self.svg_scale)
)
r = radius * self.svg_scale r = radius * self.svg_scale
# reflex = 1 if angle > math.pi else 0 # reflex = 1 if angle > math.pi else 0
reflex = is_reflex reflex = is_reflex
@@ -735,10 +749,16 @@ class SvgWriter:
path = self.svg.add(self.svg.path(d=d, class_=" ".join(classes))) path = self.svg.add(self.svg.path(d=d, class_=" ".join(classes)))
p0 = Vector( p0 = Vector(
((x_offset + projected_points[0].x) * self.svg_scale, (y_offset - projected_points[0].y) * self.svg_scale) (
(x_offset + projected_points[0].x) * self.svg_scale,
(y_offset - projected_points[0].y) * self.svg_scale,
)
) )
p1 = Vector( p1 = Vector(
((x_offset + projected_points[1].x) * self.svg_scale, (y_offset - projected_points[1].y) * self.svg_scale) (
(x_offset + projected_points[1].x) * self.svg_scale,
(y_offset - projected_points[1].y) * self.svg_scale,
)
) )
text_offset = (p0 - p1).xy.normalized() * 5 text_offset = (p0 - p1).xy.normalized() * 5
text_position = projected_points[0] text_position = projected_points[0]
@@ -823,7 +843,9 @@ class SvgWriter:
text_position = (mid * self.svg_scale) + perpendicular text_position = (mid * self.svg_scale) + perpendicular
rotation = math.degrees(vector.angle_signed(Vector((1, 0)))) rotation = math.degrees(vector.angle_signed(Vector((1, 0))))
line = self.svg.add( line = self.svg.add(
self.svg.line(start=tuple(start * self.svg_scale), end=tuple(end * self.svg_scale), class_=" ".join(classes)) self.svg.line(
start=tuple(start * self.svg_scale), end=tuple(end * self.svg_scale), class_=" ".join(classes)
)
) )
if text_override is not None: if text_override is not None:
text = text_override text = text_override
@@ -207,7 +207,10 @@ class UpdateRepresentation(bpy.types.Operator):
new_representation = ifcopenshell.api.run("geometry.add_representation", self.file, **representation_data) new_representation = ifcopenshell.api.run("geometry.add_representation", self.file, **representation_data)
if tool.Geometry.is_body_representation(new_representation): if tool.Geometry.is_body_representation(new_representation):
[tool.Geometry.run_style_add_style(obj=mat) for mat in tool.Geometry.get_object_materials_without_styles(obj)] [
tool.Geometry.run_style_add_style(obj=mat)
for mat in tool.Geometry.get_object_materials_without_styles(obj)
]
ifcopenshell.api.run( ifcopenshell.api.run(
"style.assign_representation_styles", "style.assign_representation_styles",
self.file, self.file,
@@ -19,7 +19,7 @@
import bpy import bpy
from bpy.types import Panel from bpy.types import Panel
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.ui import prop_with_search from blenderbim.bim.helper import prop_with_search
from blenderbim.bim.module.geometry.data import RepresentationsData, DerivedPlacementsData from blenderbim.bim.module.geometry.data import RepresentationsData, DerivedPlacementsData
@@ -84,7 +84,9 @@ class ObjectMaterialData:
@classmethod @classmethod
def materials(cls): def materials(cls):
return sorted([(str(m.id()), m.Name or "Unnamed", "") for m in tool.Ifc.get().by_type("IfcMaterial")], key=lambda x: x[1]) return sorted(
[(str(m.id()), m.Name or "Unnamed", "") for m in tool.Ifc.get().by_type("IfcMaterial")], key=lambda x: x[1]
)
@classmethod @classmethod
def type_material(cls): def type_material(cls):
@@ -137,3 +137,5 @@ class BIMObjectMaterialProperties(PropertyGroup):
parameterized_profile_classes: EnumProperty( parameterized_profile_classes: EnumProperty(
items=getParameterizedProfileClasses, name="Parameterized Profile Classes" items=getParameterizedProfileClasses, name="Parameterized Profile Classes"
) )
getter_enum = {"material": get_materials}
@@ -22,6 +22,7 @@ from ifcopenshell.api.material.data import Data
from ifcopenshell.api.profile.data import Data as ProfileData from ifcopenshell.api.profile.data import Data as ProfileData
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.helper import draw_attributes from blenderbim.bim.helper import draw_attributes
from blenderbim.bim.helper import prop_with_search
from blenderbim.bim.module.material.data import MaterialsData, ObjectMaterialData from blenderbim.bim.module.material.data import MaterialsData, ObjectMaterialData
@@ -190,7 +191,7 @@ class BIM_PT_object_material(Panel):
row = self.layout.row(align=True) row = self.layout.row(align=True)
row.prop(self.props, "material_type", text="") row.prop(self.props, "material_type", text="")
if self.props.material_type == "IfcMaterial" or self.props.material_type == "IfcMaterialList": if self.props.material_type == "IfcMaterial" or self.props.material_type == "IfcMaterialList":
row.prop(self.props, "material", text="") prop_with_search(row, self.props, "material", text="")
row.operator("bim.assign_material", icon="ADD", text="") row.operator("bim.assign_material", icon="ADD", text="")
def draw_material_ui(self): def draw_material_ui(self):
@@ -220,8 +221,7 @@ class BIM_PT_object_material(Panel):
return self.draw_read_only_single_ui() return self.draw_read_only_single_ui()
def draw_editable_single_ui(self): def draw_editable_single_ui(self):
row = self.layout.row(align=True) prop_with_search(self.layout, self.props, "material", text="")
row.prop(self.props, "material", text="")
def draw_read_only_single_ui(self): def draw_read_only_single_ui(self):
material = Data.materials[self.product_data["id"]] material = Data.materials[self.product_data["id"]]
@@ -242,7 +242,7 @@ class BIM_PT_object_material(Panel):
row.prop(attribute, "string_value", text=attribute.name) row.prop(attribute, "string_value", text=attribute.name)
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="") row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
row = self.layout.row(align=True) row = self.layout.row(align=True)
row.prop(self.props, "material", text="") prop_with_search(row, self.props, "material", text="")
op = row.operator(f"bim.add_{self.set_item_name}", icon="ADD", text="") op = row.operator(f"bim.add_{self.set_item_name}", icon="ADD", text="")
setattr(op, f"{self.set_item_name}_set", self.material_set_id) setattr(op, f"{self.set_item_name}_set", self.material_set_id)
@@ -65,14 +65,14 @@ class MepGenerator:
def create_rectangle_segment(self): def create_rectangle_segment(self):
verts = [ verts = [
Vector((- self.width / 2, self.height / 2, 0)), Vector((-self.width / 2, self.height / 2, 0)),
Vector((- self.width / 2, - self.height / 2, 0)), Vector((-self.width / 2, -self.height / 2, 0)),
Vector((- self.width / 2, self.height / 2, self.length)), Vector((-self.width / 2, self.height / 2, self.length)),
Vector((- self.width / 2, - self.height / 2, self.length)), Vector((-self.width / 2, -self.height / 2, self.length)),
Vector((self.width / 2, self.height / 2, 0)), Vector((self.width / 2, self.height / 2, 0)),
Vector((self.width / 2, - self.height / 2, 0)), Vector((self.width / 2, -self.height / 2, 0)),
Vector((self.width / 2, self.height / 2, self.length)), Vector((self.width / 2, self.height / 2, self.length)),
Vector((self.width / 2, - self.height / 2, self.length)), Vector((self.width / 2, -self.height / 2, self.length)),
] ]
faces = [ faces = [
[1, 3, 2, 0], [1, 3, 2, 0],
@@ -121,9 +121,7 @@ class AddTypeInstance(bpy.types.Operator):
collection_obj = bpy.data.objects.get(collection.name) collection_obj = bpy.data.objects.get(collection.name)
bpy.ops.bim.assign_class(obj=obj.name, ifc_class=instance_class) bpy.ops.bim.assign_class(obj=obj.name, ifc_class=instance_class)
element = tool.Ifc.get_entity(obj) element = tool.Ifc.get_entity(obj)
blenderbim.core.type.assign_type( blenderbim.core.type.assign_type(tool.Ifc, tool.Type, element=element, type=relating_type)
tool.Ifc, tool.Type, element=element, type=relating_type
)
if building_obj: if building_obj:
if instance_class in ["IfcWindow", "IfcDoor"]: if instance_class in ["IfcWindow", "IfcDoor"]:
@@ -253,10 +253,7 @@ class ObjectActorData:
@classmethod @classmethod
def load(cls): def load(cls):
cls.is_loaded = True cls.is_loaded = True
cls.data = { cls.data = {"actor": cls.actor(), "actors": cls.actors()}
"actor": cls.actor(),
"actors": cls.actors()
}
@classmethod @classmethod
def actor(cls): def actor(cls):
@@ -279,9 +276,9 @@ class ObjectActorData:
roles = cls.get_roles(actor.TheActor.ThePerson) roles = cls.get_roles(actor.TheActor.ThePerson)
roles.extend(cls.get_roles(actor.TheActor.TheOrganization)) roles.extend(cls.get_roles(actor.TheActor.TheOrganization))
role = ", ".join(roles) role = ", ".join(roles)
results.append({ results.append(
"id": actor.id(), "name": actor.Name or "Unnamed", "role": role, "ifc_class": actor.is_a() {"id": actor.id(), "name": actor.Name or "Unnamed", "role": role, "ifc_class": actor.is_a()}
}) )
return results return results
@classmethod @classmethod
@@ -16,7 +16,7 @@
# You should have received a copy of the GNU General Public License # 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/>. # along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
from blenderbim.bim.ui import prop_with_search from blenderbim.bim.helper import prop_with_search
import bpy import bpy
from blenderbim.bim.helper import draw_attributes from blenderbim.bim.helper import draw_attributes
@@ -732,7 +732,7 @@ class ToggleLinkVisibility(bpy.types.Operator):
def toggle_wireframe(self, link): def toggle_wireframe(self, link):
objs = filter(lambda obj: "IfcOpeningElement" not in obj.name, link.collection.all_objects) objs = filter(lambda obj: "IfcOpeningElement" not in obj.name, link.collection.all_objects)
for i,obj in enumerate(objs): for i, obj in enumerate(objs):
if i == 0: if i == 0:
if obj.display_type == "WIRE": if obj.display_type == "WIRE":
display_type = "TEXTURED" display_type = "TEXTURED"
@@ -17,7 +17,7 @@
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>. # along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import os import os
from blenderbim.bim.ui import prop_with_search from blenderbim.bim.helper import prop_with_search
from bpy.types import Panel, UIList from bpy.types import Panel, UIList
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.module.project.data import ProjectData from blenderbim.bim.module.project.data import ProjectData
@@ -35,8 +35,8 @@ classes = (
operator.BIM_OT_rename_parameters, operator.BIM_OT_rename_parameters,
operator.BIM_OT_add_edit_custom_property, operator.BIM_OT_add_edit_custom_property,
operator.BIM_OT_bulk_remove_psets, operator.BIM_OT_bulk_remove_psets,
prop.EnumerationValues, prop.IfcPropertyEnumeratedValue,
prop.IfcSimpleProperty, prop.IfcProperty,
prop.PsetProperties, prop.PsetProperties,
prop.MaterialPsetProperties, prop.MaterialPsetProperties,
prop.TaskPsetProperties, prop.TaskPsetProperties,
@@ -122,85 +122,91 @@ class EnablePsetEditing(bpy.types.Operator):
for prop_template in pset_template.HasPropertyTemplates: for prop_template in pset_template.HasPropertyTemplates:
if not prop_template.is_a("IfcSimplePropertyTemplate"): if not prop_template.is_a("IfcSimplePropertyTemplate"):
continue # Other types not yet supported continue # Other types not yet supported
if prop_template.TemplateType == "P_SINGLEVALUE": if prop_template.TemplateType == "P_SINGLEVALUE":
try: self.load_single_value(prop_template, data)
data_type = ifcopenshell.util.attribute.get_primitive_type(
IfcStore.get_schema().declaration_by_name(prop_template.PrimaryMeasureType or "IfcLabel")
)
except:
# TODO: Occurs if the data type is something that exists in
# IFC4 and not in IFC2X3. To fully fix this we need to
# generate the IFC2X3 pset template definitions.
continue
elif prop_template.TemplateType in ["Q_LENGTH", "Q_AREA", "Q_VOLUME", "Q_WEIGHT", "Q_TIME"]:
data_type = "float"
elif prop_template.TemplateType == "Q_COUNT":
data_type = "integer"
elif prop_template.TemplateType == "P_ENUMERATEDVALUE": elif prop_template.TemplateType == "P_ENUMERATEDVALUE":
enum_items = [v.wrappedValue for v in prop_template.Enumerators.EnumerationValues] self.load_enumerated_value(prop_template, data)
#selected_enum_items = [e.EnumerationValues.wrappedValue for e in Data.properties[e]]
data_type = ""
else:
continue # Other types not yet supported
new = self.props.properties.add() def load_single_value(self, prop_template, data):
new.name = prop_template.Name try:
new.is_null = data.get(prop_template.Name, None) is None data_type = ifcopenshell.util.attribute.get_primitive_type(
new.is_optional = True IfcStore.get_schema().declaration_by_name(prop_template.PrimaryMeasureType or "IfcLabel")
new.is_uri = prop_template.PrimaryMeasureType == "IfcURIReference" )
new.data_type = data_type except:
# TODO: Occurs if the data type is something that exists in
# IFC4 and not in IFC2X3. To fully fix this we need to
# generate the IFC2X3 pset template definitions.
return
if data_type == "string": prop = self.props.properties.add()
new.string_value = "" if new.is_null else data[prop_template.Name] prop.value_type = "IfcPropertySingleValue"
elif data_type == "integer": metadata = prop.metadata
new.int_value = 0 if new.is_null else data[prop_template.Name] metadata.name = prop_template.Name
elif data_type == "float": metadata.is_null = data.get(prop_template.Name, None) is None
new.float_value = 0.0 if new.is_null else data[prop_template.Name] metadata.is_optional = True
elif data_type == "boolean": metadata.is_uri = prop_template.PrimaryMeasureType == "IfcURIReference"
new.bool_value = False if new.is_null else data[prop_template.Name] metadata.data_type = data_type
if prop_template.TemplateType == "P_ENUMERATEDVALUE":
new.set_value(prop_template.Enumerators.EnumerationValues[0].wrappedValue) if data_type == "string":
metadata.string_value = "" if metadata.is_null else data[prop_template.Name]
elif data_type == "integer":
metadata.int_value = 0 if metadata.is_null else data[prop_template.Name]
elif data_type == "float":
metadata.float_value = 0.0 if metadata.is_null else data[prop_template.Name]
elif data_type == "boolean":
metadata.bool_value = False if metadata.is_null else data[prop_template.Name]
for enum in enum_items: def load_enumerated_value(self, prop_template, data):
new_enum = new.enumerated_values.add() enum_items = [v.wrappedValue for v in prop_template.Enumerators.EnumerationValues]
data_type = new.get_value_name() selected_enum_items = data.get(prop_template.Name, [])
setattr(new_enum, data_type, enum)
if data.get(prop_template.Name): prop = self.props.properties.add()
new_enum.is_selected = enum in data[prop_template.Name] prop.value_type = "IfcPropertyEnumeratedValue"
metadata = prop.metadata
metadata.name = prop_template.Name
metadata.is_null = data.get(prop_template.Name, None) is None
metadata.is_optional = True
metadata.is_uri = prop_template.PrimaryMeasureType == "IfcURIReference"
# Cute hack to abuse the metadata to find the Blender data_type
metadata.set_value(enum_items[0])
data_type = metadata.get_value_name()
for enum in enum_items:
new = prop.enumerated_value.enumerated_values.add()
setattr(new, data_type, enum)
new.is_selected = enum in selected_enum_items
def load_from_pset_data(self, pset_data): def load_from_pset_data(self, pset_data):
for prop_id in pset_data["Properties"]: for prop_id in pset_data["Properties"]:
prop = Data.properties[prop_id] prop = Data.properties[prop_id]
if prop["type"] == "IfcPropertyEnumeratedValue": if prop["type"] == "IfcPropertyEnumeratedValue":
new = self.props.properties.add() simple_prop = self.props.properties.add()
new.name = prop["Name"] simple_prop.value_type = "IfcPropertyEnumeratedValue"
new.is_null = new.enumerated_values is None metadata = simple_prop.metadata
new.is_optional = True metadata.name = prop["Name"]
new.set_value(prop["EnumerationReference"].EnumerationValues[0].wrappedValue) metadata.is_null = len(simple_prop.enumerated_value.enumerated_values) == 0
metadata.is_optional = True
metadata.set_value(prop["EnumerationReference"].EnumerationValues[0].wrappedValue)
enum_ref = [v.wrappedValue for v in prop["EnumerationReference"].EnumerationValues] enum_items = [v.wrappedValue for v in prop["EnumerationReference"].EnumerationValues]
enum_vals = [v.wrappedValue for v in prop["EnumerationValues"]] selected_enum_items = [v.wrappedValue for v in prop["EnumerationValues"]]
for enum in enum_ref: data_type = metadata.get_value_name()
new_enum = new.enumerated_values.add()
data_type = new.get_value_name()
setattr(new_enum, data_type, enum)
if enum in enum_vals:
new_enum.is_selected = True
else:
new_enum.is_selected = False
for enum in enum_items:
new = simple_prop.enumerated_value.enumerated_values.add()
setattr(new, data_type, enum)
new.is_selected = enum in selected_enum_items
else: else:
value = prop["NominalValue"] value = prop["NominalValue"]
new = self.props.properties.add() prop = self.props.properties.add()
new.set_value(value) metadata = prop.metadata
new.name = prop["Name"] metadata.set_value(value)
new.is_null = value is None metadata.name = prop["Name"]
new.is_optional = True metadata.is_null = value is None
new.set_value(new.get_value_default() if new.is_null else value) metadata.is_optional = True
metadata.set_value(metadata.get_value_default() if metadata.is_null else value)
class DisablePsetEditing(bpy.types.Operator, Operator): class DisablePsetEditing(bpy.types.Operator, Operator):
@@ -236,11 +242,13 @@ class EditPset(bpy.types.Operator, Operator):
else: else:
data = Data.psets if pset_id in Data.psets else Data.qtos data = Data.psets if pset_id in Data.psets else Data.qtos
for prop in props.properties: for prop in props.properties:
if len(prop.enumerated_values) != 0: if prop.value_type == "IfcPropertySingleValue":
value_name = prop.get_value_name() properties[prop.metadata.name] = prop.metadata.get_value()
properties[prop.name] = [e[value_name] for e in prop.enumerated_values if e.is_selected] elif prop.value_type == "IfcPropertyEnumeratedValue":
else: value_name = prop.metadata.get_value_name()
properties[prop.name] = prop.get_value() properties[prop.metadata.name] = [
e[value_name] for e in prop.enumerated_value.enumerated_values if e.is_selected
]
if pset_id in Data.psets: if pset_id in Data.psets:
ifcopenshell.api.run( ifcopenshell.api.run(
@@ -527,18 +535,22 @@ class BIM_OT_add_edit_custom_property(bpy.types.Operator):
def generate_enum_entity(self, prop): def generate_enum_entity(self, prop):
prop_type = prop.get_value_name() prop_type = prop.get_value_name()
prop_enum = self.file.create_entity( prop_enum = self.file.create_entity(
"IFCPROPERTYENUMERATION", "IFCPROPERTYENUMERATION",
Name=prop.property_name, Name=prop.property_name,
EnumerationValues=tuple(self.file.create_entity( EnumerationValues=tuple(
prop.primary_measure_type, ev[prop_type]) for ev in prop.enum_values) self.file.create_entity(prop.primary_measure_type, ev[prop_type]) for ev in prop.enum_values
) ),
)
prop_enum_value = self.file.create_entity( prop_enum_value = self.file.create_entity(
"IFCPROPERTYENUMERATEDVALUE", "IFCPROPERTYENUMERATEDVALUE",
Name=prop.property_name, Name=prop.property_name,
EnumerationValues=tuple(self.file.create_entity( EnumerationValues=tuple(
prop.primary_measure_type, ev[prop_type]) for ev in prop.enum_values if ev.is_selected == True), self.file.create_entity(prop.primary_measure_type, ev[prop_type])
EnumerationReference=prop_enum for ev in prop.enum_values
) if ev.is_selected == True
),
EnumerationReference=prop_enum,
)
return prop_enum_value return prop_enum_value
@@ -139,26 +139,22 @@ def get_primary_measure_type(self, context):
return AddEditCustomPropertiesData.data["primary_measure_type"] return AddEditCustomPropertiesData.data["primary_measure_type"]
class EnumerationValues(PropertyGroup): class IfcPropertyEnumeratedValue(PropertyGroup):
string_value: StringProperty(name="Value") enumerated_values: CollectionProperty(type=Attribute)
bool_value: BoolProperty(name="Value")
int_value: IntProperty(name="Value")
float_value: FloatProperty(name="Value")
is_selected: BoolProperty(default=False)
class IfcSimpleProperty(Attribute): class IfcProperty(PropertyGroup):
# bounded_values: metadata: PointerProperty(type=Attribute)
enumerated_values: CollectionProperty(type=EnumerationValues) value_type: EnumProperty(
# list_values: items=[(v, v, v) for v in ("IfcPropertySingleValue", "IfcPropertyEnumeratedValue")], name="Value Type"
# reference_values: )
# table_values: enumerated_value: PointerProperty(type=IfcPropertyEnumeratedValue)
class PsetProperties(PropertyGroup): class PsetProperties(PropertyGroup):
active_pset_id: IntProperty(name="Active Pset ID") active_pset_id: IntProperty(name="Active Pset ID")
active_pset_name: StringProperty(name="Pset Name") active_pset_name: StringProperty(name="Pset Name")
properties: CollectionProperty(name="Properties", type=IfcSimpleProperty) properties: CollectionProperty(name="Properties", type=IfcProperty)
pset_name: EnumProperty(items=get_pset_names, name="Pset Name") pset_name: EnumProperty(items=get_pset_names, name="Pset Name")
qto_name: EnumProperty(items=get_qto_names, name="Qto Name") qto_name: EnumProperty(items=get_qto_names, name="Qto Name")
@@ -225,7 +221,7 @@ class AddEditProperties(PropertyGroup):
], ],
name="Template Type", name="Template Type",
) )
enum_values: CollectionProperty(name="Enum Values", type=EnumerationValues) enum_values: CollectionProperty(name="Enum Values", type=Attribute)
getter_enum = { getter_enum = {
"primary_measure_type": get_primary_measure_type, "primary_measure_type": get_primary_measure_type,
@@ -18,8 +18,7 @@
from bpy.types import Panel from bpy.types import Panel
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.ui import prop_with_search from blenderbim.bim.helper import prop_with_search
from blenderbim.bim.helper import draw_attribute
from blenderbim.bim.module.pset.data import ( from blenderbim.bim.module.pset.data import (
ObjectPsetsData, ObjectPsetsData,
ObjectQtosData, ObjectQtosData,
@@ -32,6 +31,45 @@ from blenderbim.bim.module.pset.data import (
) )
def draw_property(prop, layout, copy_operator=None):
if prop.value_type == "IfcPropertySingleValue":
draw_single_property(prop, layout, copy_operator)
elif prop.value_type == "IfcPropertyEnumeratedValue":
draw_enumerated_property(prop, layout, copy_operator)
def draw_single_property(prop, layout, copy_operator=None):
value_name = prop.metadata.get_value_name()
if not value_name:
layout.label(text=prop.metadata.name)
return
layout.prop(
prop.metadata,
value_name,
text=prop.metadata.name,
)
if prop.metadata.is_optional:
layout.prop(prop.metadata, "is_null", icon="RADIOBUT_OFF" if prop.metadata.is_null else "RADIOBUT_ON", text="")
if copy_operator:
op = layout.operator(f"{copy_operator}", text="", icon="COPYDOWN")
op.name = prop.metadata.name
if prop.metadata.is_uri:
op = layout.operator("bim.select_uri_prop", text="", icon="FILE_FOLDER")
op.data_path = prop.metadata.path_from_id("string_value")
def draw_enumerated_property(prop, layout, copy_operator=None):
value_name = prop.metadata.get_value_name()
if not value_name:
layout.label(text=prop.metadata.name)
return
if len(prop.enumerated_value.enumerated_values) != 0:
layout.label(text=prop.metadata.name)
grid = layout.column_flow(columns=3)
for e in prop.enumerated_value.enumerated_values:
grid.prop(e, "is_selected", text=str(e[value_name]))
def get_active_pset_obj_name(context, obj_type): def get_active_pset_obj_name(context, obj_type):
if obj_type == "Object": if obj_type == "Object":
return context.active_object.name return context.active_object.name
@@ -96,7 +134,7 @@ def draw_psetqto_ui(context, pset_id, pset, props, layout, obj_type):
def draw_psetqto_editable_ui(box, props, prop): def draw_psetqto_editable_ui(box, props, prop):
row = box.row(align=True) row = box.row(align=True)
draw_attribute(prop, row, copy_operator="bim.copy_property_to_selection") draw_property(prop, row, copy_operator="bim.copy_property_to_selection")
if ( if (
"length" in prop.name.lower() "length" in prop.name.lower()
or "width" in prop.name.lower() or "width" in prop.name.lower()
@@ -28,8 +28,8 @@ from ifcopenshell.api.pset_template.data import Data
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
#This is just a temporary operator until # This is just a temporary operator until
#@Moult performs some of his refactor-magic ;) - vulevukusej # @Moult performs some of his refactor-magic ;) - vulevukusej
class RefreshPsetTemplates(bpy.types.Operator): class RefreshPsetTemplates(bpy.types.Operator):
bl_idname = "bim.refresh_psettemplates" bl_idname = "bim.refresh_psettemplates"
bl_label = "Refresh the data for PsetTemplates" bl_label = "Refresh the data for PsetTemplates"
@@ -68,7 +68,7 @@ class AddPsetFile(bpy.types.Operator):
"Name": "Name", "Name": "Name",
"Description": "Description", "Description": "Description",
"TemplateType": "PSET_TYPEDRIVENONLY", "TemplateType": "PSET_TYPEDRIVENONLY",
"ApplicableEntity": "IfcTypeObject" "ApplicableEntity": "IfcTypeObject",
} }
) )
template.write(filepath) template.write(filepath)
@@ -200,6 +200,7 @@ class EnableEditingPropTemplate(bpy.types.Operator):
return {"FINISHED"} return {"FINISHED"}
class DeletePropEnum(bpy.types.Operator): class DeletePropEnum(bpy.types.Operator):
bl_idname = "bim.delete_prop_enum" bl_idname = "bim.delete_prop_enum"
bl_label = "delete property enumeration" bl_label = "delete property enumeration"
@@ -211,6 +212,7 @@ class DeletePropEnum(bpy.types.Operator):
active_prop.enum_values.remove(self.index) active_prop.enum_values.remove(self.index)
return {"FINISHED"} return {"FINISHED"}
class AddPropEnum(bpy.types.Operator): class AddPropEnum(bpy.types.Operator):
bl_idname = "bim.add_prop_enum" bl_idname = "bim.add_prop_enum"
bl_label = "add property enumeration" bl_label = "add property enumeration"
@@ -380,7 +382,7 @@ class EditPropTemplate(bpy.types.Operator):
"Description": props.active_prop_template.description, "Description": props.active_prop_template.description,
"PrimaryMeasureType": props.active_prop_template.primary_measure_type, "PrimaryMeasureType": props.active_prop_template.primary_measure_type,
"TemplateType": props.active_prop_template.template_type, "TemplateType": props.active_prop_template.template_type,
"Enumerators": enumerator "Enumerators": enumerator,
}, },
} }
) )
@@ -388,18 +390,19 @@ class EditPropTemplate(bpy.types.Operator):
bpy.ops.bim.disable_editing_prop_template() bpy.ops.bim.disable_editing_prop_template()
return {"FINISHED"} return {"FINISHED"}
#TODO -This will need to go into the # TODO -This will need to go into the
# api code at some point - vulevukusej # api code at some point - vulevukusej
def generate_prop_enum(self, props): def generate_prop_enum(self, props):
self.file = IfcStore.pset_template_file self.file = IfcStore.pset_template_file
data_type = props.active_prop_template.get_value_name() data_type = props.active_prop_template.get_value_name()
prop = props.active_prop_template prop = props.active_prop_template
prop_enum = self.file.create_entity( prop_enum = self.file.create_entity(
"IFCPROPERTYENUMERATION", "IFCPROPERTYENUMERATION",
Name=prop.name, Name=prop.name,
EnumerationValues=tuple(self.file.create_entity( EnumerationValues=tuple(
prop.primary_measure_type, ev[data_type]) for ev in prop.enum_values) self.file.create_entity(prop.primary_measure_type, ev[data_type]) for ev in prop.enum_values
) ),
)
return prop_enum return prop_enum
def rollback(self, data): def rollback(self, data):
@@ -94,57 +94,62 @@ def get_primary_measure_type(self, context):
return PsetTemplatesData.data["primary_measure_type"] return PsetTemplatesData.data["primary_measure_type"]
def get_template_type(self, context):
return [
(
"PSET_TYPEDRIVENONLY",
"Pset - IfcTypeObject",
"The property sets defined by this IfcPropertySetTemplate can only be assigned to subtypes of IfcTypeObject.",
),
(
"PSET_TYPEDRIVENOVERRIDE",
"Pset - IfcTypeObject - Override",
"The property sets defined by this IfcPropertySetTemplate can only be assigned to subtypes of IfcTypeObject.",
),
(
"PSET_OCCURRENCEDRIVEN",
"Pset - IfcObject",
"The property sets defined by this IfcPropertySetTemplate can only be assigned to subtypes of IfcObject.",
),
(
"PSET_PERFORMANCEDRIVEN",
"Pset - IfcPerformanceHistory",
"The property sets defined by this IfcPropertySetTemplate can only be assigned to IfcPerformanceHistory.",
),
(
"QTO_TYPEDRIVENONLY",
"Qto - IfcTypeObject",
"The element quantity defined by this IfcPropertySetTemplate can only be assigned to subtypes of IfcTypeObject.",
),
(
"QTO_TYPEDRIVENOVERRIDE",
"Qto - IfcTypeObject - Override",
"The element quantity defined by this IfcPropertySetTemplate can be assigned to subtypes of IfcTypeObject and can be overridden by an element quantity with same name at subtypes of IfcObject.",
),
(
"QTO_OCCURRENCEDRIVEN",
"Qto - IfcObject",
"The element quantity defined by this IfcPropertySetTemplate can only be assigned to subtypes of IfcObject.",
),
(
"NOTDEFINED",
"Not defined",
"No restriction provided, the property sets defined by this IfcPropertySetTemplate can be assigned to any entity, if not otherwise restricted by the ApplicableEntity attribute.",
),
]
class PsetTemplate(PropertyGroup): class PsetTemplate(PropertyGroup):
global_id: StringProperty(name="Global ID") global_id: StringProperty(name="Global ID")
name: StringProperty(name="Name") name: StringProperty(name="Name")
description: StringProperty(name="Description") description: StringProperty(name="Description")
template_type: EnumProperty( template_type: EnumProperty(items=get_template_type, name="Template Type")
items=[
(
"PSET_TYPEDRIVENONLY",
"Pset - IfcTypeObject",
"The property sets defined by this IfcPropertySetTemplate can only be assigned to subtypes of IfcTypeObject.",
),
(
"PSET_TYPEDRIVENOVERRIDE",
"Pset - IfcTypeObject - Override",
"The property sets defined by this IfcPropertySetTemplate can only be assigned to subtypes of IfcTypeObject.",
),
(
"PSET_OCCURRENCEDRIVEN",
"Pset - IfcObject",
"The property sets defined by this IfcPropertySetTemplate can only be assigned to subtypes of IfcObject.",
),
(
"PSET_PERFORMANCEDRIVEN",
"Pset - IfcPerformanceHistory",
"The property sets defined by this IfcPropertySetTemplate can only be assigned to IfcPerformanceHistory.",
),
(
"QTO_TYPEDRIVENONLY",
"Qto - IfcTypeObject",
"The element quantity defined by this IfcPropertySetTemplate can only be assigned to subtypes of IfcTypeObject.",
),
(
"QTO_TYPEDRIVENOVERRIDE",
"Qto - IfcTypeObject - Override",
"The element quantity defined by this IfcPropertySetTemplate can be assigned to subtypes of IfcTypeObject and can be overridden by an element quantity with same name at subtypes of IfcObject.",
),
(
"QTO_OCCURRENCEDRIVEN",
"Qto - IfcObject",
"The element quantity defined by this IfcPropertySetTemplate can only be assigned to subtypes of IfcObject.",
),
(
"NOTDEFINED",
"Not defined",
"No restriction provided, the property sets defined by this IfcPropertySetTemplate can be assigned to any entity, if not otherwise restricted by the ApplicableEntity attribute.",
),
],
name="Template Type",
)
applicable_entity: StringProperty(name="Applicable Entity") applicable_entity: StringProperty(name="Applicable Entity")
getter_enum = {
"template_type": get_template_type,
}
class EnumerationValues(PropertyGroup): class EnumerationValues(PropertyGroup):
string_value: StringProperty(name="Value") string_value: StringProperty(name="Value")
@@ -159,18 +164,14 @@ class PropTemplate(PropertyGroup):
description: StringProperty(name="Description") description: StringProperty(name="Description")
primary_measure_type: EnumProperty(items=get_primary_measure_type, name="Primary Measure Type") primary_measure_type: EnumProperty(items=get_primary_measure_type, name="Primary Measure Type")
template_type: EnumProperty( template_type: EnumProperty(
items=[ items=[("P_SINGLEVALUE", "P_SINGLEVALUE", ""), ("P_ENUMERATEDVALUE", "P_ENUMERATEDVALUE", "")],
("P_SINGLEVALUE","P_SINGLEVALUE",""), name="Template Type",
("P_ENUMERATEDVALUE","P_ENUMERATEDVALUE","")
],
name="Template Type"
) )
enum_values: CollectionProperty(type=EnumerationValues) enum_values: CollectionProperty(type=EnumerationValues)
getter_enum = { getter_enum = {
"primary_measure_type": get_primary_measure_type, "primary_measure_type": get_primary_measure_type,
} }
def get_value_name(self): def get_value_name(self):
ifc_data_type = IfcStore.get_schema().declaration_by_name(self.primary_measure_type) ifc_data_type = IfcStore.get_schema().declaration_by_name(self.primary_measure_type)
data_type = ifcopenshell.util.attribute.get_primitive_type(ifc_data_type) data_type = ifcopenshell.util.attribute.get_primitive_type(ifc_data_type)
@@ -19,7 +19,7 @@
import bpy import bpy
from bpy.types import Panel from bpy.types import Panel
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.ui import prop_with_search from blenderbim.bim.helper import prop_with_search
from blenderbim.bim.module.pset_template.prop import ( from blenderbim.bim.module.pset_template.prop import (
getPsetTemplates, getPsetTemplates,
) )
@@ -42,7 +42,7 @@ class BIM_PT_pset_template(Panel):
row = layout.row(align=True) row = layout.row(align=True)
prop_with_search(row, props, "pset_template_files", text="") prop_with_search(row, props, "pset_template_files", text="")
row.operator("bim.save_pset_template_file", text="", icon="EXPORT") row.operator("bim.save_pset_template_file", text="", icon="EXPORT")
row.operator("bim.add_pset_file",icon="ADD",text="") row.operator("bim.add_pset_file", icon="ADD", text="")
row.operator("bim.refresh_psettemplates", icon="FILE_REFRESH", text="") row.operator("bim.refresh_psettemplates", icon="FILE_REFRESH", text="")
row = layout.row(align=True) row = layout.row(align=True)
@@ -72,8 +72,7 @@ class BIM_PT_pset_template(Panel):
row.prop(props.active_pset_template, "name") row.prop(props.active_pset_template, "name")
row = layout.row() row = layout.row()
row.prop(props.active_pset_template, "description") row.prop(props.active_pset_template, "description")
row = layout.row() prop_with_search(layout, props.active_pset_template, "template_type")
row.prop(props.active_pset_template, "template_type")
row = layout.row() row = layout.row()
row.prop(props.active_pset_template, "applicable_entity") row.prop(props.active_pset_template, "applicable_entity")
else: else:
@@ -105,22 +104,23 @@ class BIM_PT_pset_template(Panel):
if props.active_prop_template.template_type == "P_ENUMERATEDVALUE": if props.active_prop_template.template_type == "P_ENUMERATEDVALUE":
row = layout.row(align=True) row = layout.row(align=True)
split = row.split(factor=0.2) split = row.split(factor=0.2)
c=split.column() c = split.column()
c.label(text="Enumerations:",) c.label(
text="Enumerations:",
)
c=split.column() c = split.column()
box = c.box() box = c.box()
grid = box.column_flow(columns=4) grid = box.column_flow(columns=4)
value_name = props.active_prop_template.get_value_name() value_name = props.active_prop_template.get_value_name()
r = grid.row() r = grid.row()
r.operator("bim.add_prop_enum", text="Add Enum") r.operator("bim.add_prop_enum", text="Add Enum")
for k,v in enumerate(props.active_prop_template.enum_values): for k, v in enumerate(props.active_prop_template.enum_values):
r = grid.row(align=True) r = grid.row(align=True)
r.prop(v, value_name, text="") r.prop(v, value_name, text="")
op = r.operator("bim.delete_prop_enum", icon="X", text="") op = r.operator("bim.delete_prop_enum", icon="X", text="")
op.index = k op.index = k
else: else:
row.label(text=prop_template["Name"]) row.label(text=prop_template["Name"])
row.label(text=prop_template["Description"]) row.label(text=prop_template["Description"])
@@ -26,7 +26,6 @@ classes = (
operator.EnableReassignClass, operator.EnableReassignClass,
operator.ReassignClass, operator.ReassignClass,
operator.UnlinkObject, operator.UnlinkObject,
operator.BIM_OT_enum_property_search,
prop.BIMRootProperties, prop.BIMRootProperties,
ui.BIM_PT_class, ui.BIM_PT_class,
) )
@@ -31,8 +31,6 @@ import blenderbim.core.root as core
import blenderbim.tool as tool import blenderbim.tool as tool
from ifcopenshell.api.void.data import Data as VoidData from ifcopenshell.api.void.data import Data as VoidData
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.module.root.prop import get_contexts
from blenderbim.bim.prop import StrProperty
class Operator: class Operator:
@@ -128,12 +126,13 @@ class AssignClass(bpy.types.Operator, Operator):
ifc_representation_class: bpy.props.StringProperty() ifc_representation_class: bpy.props.StringProperty()
def _execute(self, context): def _execute(self, context):
props = context.scene.BIMRootProperties
objects = [bpy.data.objects.get(self.obj)] if self.obj else context.selected_objects objects = [bpy.data.objects.get(self.obj)] if self.obj else context.selected_objects
ifc_class = self.ifc_class or context.scene.BIMRootProperties.ifc_class ifc_class = self.ifc_class or props.ifc_class
predefined_type = self.userdefined_type if self.predefined_type == "USERDEFINED" else self.predefined_type predefined_type = self.userdefined_type if self.predefined_type == "USERDEFINED" else self.predefined_type
ifc_context = self.context_id ifc_context = self.context_id
if not ifc_context and get_contexts(self, context): if not ifc_context and props.getter_enum["contexts"](props, context):
ifc_context = int(context.scene.BIMRootProperties.contexts or "0") or None ifc_context = int(props.contexts or "0") or None
if ifc_context: if ifc_context:
ifc_context = tool.Ifc.get().by_id(ifc_context) ifc_context = tool.Ifc.get().by_id(ifc_context)
active_object = context.active_object active_object = context.active_object
@@ -190,27 +189,3 @@ class CopyClass(bpy.types.Operator, Operator):
for obj in objects: for obj in objects:
core.copy_class(tool.Ifc, tool.Collector, tool.Geometry, tool.Root, obj=obj) core.copy_class(tool.Ifc, tool.Collector, tool.Geometry, tool.Root, obj=obj)
blenderbim.bim.handler.purge_module_data() blenderbim.bim.handler.purge_module_data()
class BIM_OT_enum_property_search(bpy.types.Operator):
bl_idname = "bim.enum_property_search"
bl_label = "Search For Property"
bl_options = {"REGISTER", "UNDO"}
prop_name: bpy.props.StringProperty()
collection: bpy.props.CollectionProperty(type=StrProperty)
def invoke(self, context, event):
self.data = context.data
getter = self.data.getter_enum.get(self.prop_name, None)
if getter is None:
return {"FINISHED"}
self.collection.clear()
for item in getter(self.data, context):
self.collection.add().name = item[0]
return context.window_manager.invoke_props_dialog(self)
def execute(self, context):
return {"FINISHED"}
def draw(self, context):
self.layout.prop_search(self.data, self.prop_name, self, "collection")
@@ -21,7 +21,6 @@ import ifcopenshell
import ifcopenshell.util.schema import ifcopenshell.util.schema
from blenderbim.bim.module.root.data import IfcClassData from blenderbim.bim.module.root.data import IfcClassData
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.prop import StrProperty
from bpy.types import PropertyGroup from bpy.types import PropertyGroup
from bpy.props import ( from bpy.props import (
PointerProperty, PointerProperty,
@@ -20,7 +20,7 @@ import bpy
import blenderbim.bim.module.root.prop as root_prop import blenderbim.bim.module.root.prop as root_prop
from bpy.types import Panel from bpy.types import Panel
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.ui import prop_with_search from blenderbim.bim.helper import prop_with_search
from blenderbim.bim.module.root.data import IfcClassData from blenderbim.bim.module.root.data import IfcClassData
@@ -624,6 +624,7 @@ class EditTaskTime(bpy.types.Operator):
if prop.is_null: if prop.is_null:
attributes[prop.name] = None attributes[prop.name] = None
return True return True
# TODO make this parse PT32 as P4D
attributes[prop.name] = helper.parse_duration(prop.string_value) attributes[prop.name] = helper.parse_duration(prop.string_value)
return True return True
@@ -54,7 +54,7 @@ class BIM_PT_styles(Panel):
row = self.layout.row(align=True) row = self.layout.row(align=True)
row.alignment = "RIGHT" row.alignment = "RIGHT"
#row.operator("bim.add_presentation_style", text="", icon="ADD") # row.operator("bim.add_presentation_style", text="", icon="ADD")
if self.props.styles and self.props.active_style_index < len(self.props.styles): if self.props.styles and self.props.active_style_index < len(self.props.styles):
style = self.props.styles[self.props.active_style_index] style = self.props.styles[self.props.active_style_index]
op = row.operator("bim.select_by_style", text="", icon="RESTRICT_SELECT_OFF") op = row.operator("bim.select_by_style", text="", icon="RESTRICT_SELECT_OFF")
@@ -37,7 +37,7 @@ class UnitsData:
"unit_classes": cls.unit_classes(), "unit_classes": cls.unit_classes(),
"named_unit_types": cls.named_unit_types(), "named_unit_types": cls.named_unit_types(),
"conversion_unit_types": cls.conversion_unit_types(), "conversion_unit_types": cls.conversion_unit_types(),
"total_units": cls.get_total_units() "total_units": cls.get_total_units(),
} }
cls.is_loaded = True cls.is_loaded = True
@@ -19,7 +19,7 @@
import blenderbim.bim.helper import blenderbim.bim.helper
from bpy.types import Panel, UIList from bpy.types import Panel, UIList
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.ui import prop_with_search from blenderbim.bim.helper import prop_with_search
from blenderbim.bim.module.unit.data import UnitsData from blenderbim.bim.module.unit.data import UnitsData
+39
View File
@@ -25,6 +25,7 @@ import blenderbim.bim.handler
import blenderbim.tool as tool import blenderbim.tool as tool
from . import schema from . import schema
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.prop import StrProperty
from blenderbim.bim.ui import IFCFileSelector from blenderbim.bim.ui import IFCFileSelector
from mathutils import Vector, Matrix, Euler from mathutils import Vector, Matrix, Euler
from math import radians from math import radians
@@ -523,3 +524,41 @@ class ConfigureVisibility(bpy.types.Operator):
def execute(self, context): def execute(self, context):
return {"FINISHED"} return {"FINISHED"}
def update_enum_property_search_prop(self, context):
for i, prop in enumerate(self.collection_name):
if prop.name == self.dummy_name:
setattr(context.data, self.prop_name, self.collection_identifier[i].name)
class BIM_OT_enum_property_search(bpy.types.Operator):
bl_idname = "bim.enum_property_search"
bl_label = "Search For Property"
bl_options = {"REGISTER", "UNDO"}
dummy_name: bpy.props.StringProperty(name="Property", update=update_enum_property_search_prop)
collection_name: bpy.props.CollectionProperty(type=StrProperty)
collection_identifier: bpy.props.CollectionProperty(type=StrProperty)
prop_name: bpy.props.StringProperty()
def invoke(self, context, event):
self.data = context.data
getter = self.data.getter_enum.get(self.prop_name, None)
if getter is None:
return {"FINISHED"}
self.collection_name.clear()
self.collection_identifier.clear()
for item in getter(self.data, context):
self.collection_identifier.add().name = item[0]
if item[0] == getattr(self.data, self.prop_name):
self.dummy_name = item[1] # We found the current enum value
self.collection_name.add().name = item[1]
return context.window_manager.invoke_props_dialog(self)
def execute(self, context):
return {"FINISHED"}
def draw(self, context):
# Mandatory to access context.data in update :
self.layout.context_pointer_set(name="data", data=self.data)
self.layout.prop_search(self, "dummy_name", self, "collection_name")
+5
View File
@@ -195,6 +195,7 @@ class Attribute(PropertyGroup):
is_null: BoolProperty(name="Is Null") is_null: BoolProperty(name="Is Null")
is_optional: BoolProperty(name="Is Optional") is_optional: BoolProperty(name="Is Optional")
is_uri: BoolProperty(name="Is Uri", default=False) is_uri: BoolProperty(name="Is Uri", default=False)
is_selected: BoolProperty(name="Is Selected", default=False)
def get_value(self): def get_value(self):
if self.is_null: if self.is_null:
@@ -239,6 +240,10 @@ class Attribute(PropertyGroup):
value = str(value) value = str(value)
setattr(self, self.get_value_name(), value) setattr(self, self.get_value_name(), value)
getter_enum = {
"enum_value": getAttributeEnumValues,
}
class ModuleVisibility(PropertyGroup): class ModuleVisibility(PropertyGroup):
name: StringProperty(name="Name") name: StringProperty(name="Name")
-9
View File
@@ -170,15 +170,6 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences):
row.operator("bim.configure_visibility") row.operator("bim.configure_visibility")
def prop_with_search(layout, data, prop_name, **kwargs): # Kwargs are layout.prop arguments (text, icon, etc.)
row = layout.row(align=True)
# Magick courtesy of https://blender.stackexchange.com/a/203443/86891
row.context_pointer_set(name="data", data=data)
row.prop(data, prop_name, **kwargs)
op = row.operator("bim.enum_property_search", text="", icon="VIEWZOOM")
op.prop_name = prop_name
def ifc_units(self, context): def ifc_units(self, context):
scene = context.scene scene = context.scene
props = scene.BIMProperties props = scene.BIMProperties
+2 -6
View File
@@ -375,15 +375,11 @@ class Drawing(blenderbim.core.tool.Drawing):
@classmethod @classmethod
def open_spreadsheet(cls, uri): def open_spreadsheet(cls, uri):
cls.open_with_user_command( cls.open_with_user_command(bpy.context.preferences.addons["blenderbim"].preferences.spreadsheet_command, uri)
bpy.context.preferences.addons["blenderbim"].preferences.spreadsheet_command, uri
)
@classmethod @classmethod
def open_svg(cls, uri): def open_svg(cls, uri):
cls.open_with_user_command( cls.open_with_user_command(bpy.context.preferences.addons["blenderbim"].preferences.svg_command, uri)
bpy.context.preferences.addons["blenderbim"].preferences.svg_command, uri
)
@classmethod @classmethod
def run_root_assign_class( def run_root_assign_class(
+3 -1
View File
@@ -75,7 +75,9 @@ class Material(blenderbim.core.tool.Material):
new = props.materials.add() new = props.materials.add()
new.ifc_definition_id = material.id() new.ifc_definition_id = material.id()
new.name = get_name(material) new.name = get_name(material)
new.total_elements = len(ifcopenshell.util.element.get_elements_by_material(tool.Ifc.get(), material)) new.total_elements = len(
ifcopenshell.util.element.get_elements_by_material(tool.Ifc.get(), material)
)
return return
for material in materials: for material in materials:
new = props.materials.add() new = props.materials.add()
+8 -5
View File
@@ -18,11 +18,14 @@
import subprocess import subprocess
import sys import sys
from pathlib import Path
py_exec = str(sys.executable) py_exec = str(sys.executable)
lib = Path(py_exec).parent.parent / "lib"
subprocess.call([py_exec, "-m", "ensurepip", "--user"]) subprocess.call([py_exec, "-m", "ensurepip", "--user"])
subprocess.call([py_exec, "-m", "pip", "install", "--upgrade", "pip"]) subprocess.call([py_exec, "-m", "pip", "install", "--upgrade", "pip"])
subprocess.call([py_exec, "-m", "pip", "install", "pytest"]) subprocess.call([py_exec, "-m", "pip", "install", f"--target={str(lib)}", "pytest"])
subprocess.call([py_exec, "-m", "pip", "install", "pytest-blender"]) subprocess.call([py_exec, "-m", "pip", "install", f"--target={str(lib)}", "pytest-blender"])
subprocess.call([py_exec, "-m", "pip", "install", "pytest-bdd"]) subprocess.call([py_exec, "-m", "pip", "install", f"--target={str(lib)}", "pytest-bdd"])
subprocess.call([py_exec, "-m", "pip", "install", "pygments"]) subprocess.call([py_exec, "-m", "pip", "install", f"--target={str(lib)}", "pygments"])
-1
View File
@@ -125,7 +125,6 @@ class TestHidePorts:
system.delete_element_objects(["port"]).should_be_called() system.delete_element_objects(["port"]).should_be_called()
subject.hide_ports(ifc, system, element="element") subject.hide_ports(ifc, system, element="element")
def test_syncing_locations_if_objects_moved_prior_to_hiding_ports(self, ifc, system): def test_syncing_locations_if_objects_moved_prior_to_hiding_ports(self, ifc, system):
ifc.get_object("element").should_be_called().will_return("obj") ifc.get_object("element").should_be_called().will_return("obj")
ifc.is_moved("obj").should_be_called().will_return(True) ifc.is_moved("obj").should_be_called().will_return(True)
+17 -16
View File
@@ -163,6 +163,7 @@ class TestGetMapConversionAttributes(NewFile):
"Scale": 6.0, "Scale": 6.0,
} }
class TestGetTrueNorthAttributes(NewFile): class TestGetTrueNorthAttributes(NewFile):
def test_run(self): def test_run(self):
TestImportTrueNorth().test_run() TestImportTrueNorth().test_run()
@@ -185,18 +186,18 @@ class TestDisableEditing(NewFile):
class TestSetCoordinates(NewFile): class TestSetCoordinates(NewFile):
def test_run(self): def test_run(self):
subject.set_coordinates("input", [1., 2., 3.]) subject.set_coordinates("input", [1.0, 2.0, 3.0])
assert bpy.context.scene.BIMGeoreferenceProperties.coordinate_input == "1.0,2.0,3.0" assert bpy.context.scene.BIMGeoreferenceProperties.coordinate_input == "1.0,2.0,3.0"
subject.set_coordinates("output", [4., 5., 6.]) subject.set_coordinates("output", [4.0, 5.0, 6.0])
assert bpy.context.scene.BIMGeoreferenceProperties.coordinate_output == "4.0,5.0,6.0" assert bpy.context.scene.BIMGeoreferenceProperties.coordinate_output == "4.0,5.0,6.0"
class TestGetCoordinates(NewFile): class TestGetCoordinates(NewFile):
def test_run(self): def test_run(self):
bpy.context.scene.BIMGeoreferenceProperties.coordinate_input = "1.0,2.0,3.0" bpy.context.scene.BIMGeoreferenceProperties.coordinate_input = "1.0,2.0,3.0"
assert subject.get_coordinates("input") == [1., 2., 3.] assert subject.get_coordinates("input") == [1.0, 2.0, 3.0]
bpy.context.scene.BIMGeoreferenceProperties.coordinate_output = "4.0,5.0,6.0" bpy.context.scene.BIMGeoreferenceProperties.coordinate_output = "4.0,5.0,6.0"
assert subject.get_coordinates("output") == [4., 5., 6.] assert subject.get_coordinates("output") == [4.0, 5.0, 6.0]
class TestGetCursorLocation(NewFile): class TestGetCursorLocation(NewFile):
@@ -207,8 +208,8 @@ class TestGetCursorLocation(NewFile):
ifcopenshell.api.run("context.add_context", ifc, context_type="Model") ifcopenshell.api.run("context.add_context", ifc, context_type="Model")
unit = ifcopenshell.api.run("unit.add_si_unit", ifc, unit_type="LENGTHUNIT", prefix="MILLI", name="METRE") unit = ifcopenshell.api.run("unit.add_si_unit", ifc, unit_type="LENGTHUNIT", prefix="MILLI", name="METRE")
ifcopenshell.api.run("unit.assign_unit", ifc, units=[unit]) ifcopenshell.api.run("unit.assign_unit", ifc, units=[unit])
bpy.context.scene.cursor.location = (1., 2., 3.) bpy.context.scene.cursor.location = (1.0, 2.0, 3.0)
assert subject.get_cursor_location() == [1000., 2000., 3000.] assert subject.get_cursor_location() == [1000.0, 2000.0, 3000.0]
class TestSetCursorLocation(NewFile): class TestSetCursorLocation(NewFile):
@@ -219,8 +220,8 @@ class TestSetCursorLocation(NewFile):
ifcopenshell.api.run("context.add_context", ifc, context_type="Model") ifcopenshell.api.run("context.add_context", ifc, context_type="Model")
unit = ifcopenshell.api.run("unit.add_si_unit", ifc, unit_type="LENGTHUNIT", prefix="MILLI", name="METRE") unit = ifcopenshell.api.run("unit.add_si_unit", ifc, unit_type="LENGTHUNIT", prefix="MILLI", name="METRE")
ifcopenshell.api.run("unit.assign_unit", ifc, units=[unit]) ifcopenshell.api.run("unit.assign_unit", ifc, units=[unit])
subject.set_cursor_location([1000., 2000., 3000.]) subject.set_cursor_location([1000.0, 2000.0, 3000.0])
assert list(bpy.context.scene.cursor.location) == [1., 2., 3.] assert list(bpy.context.scene.cursor.location) == [1.0, 2.0, 3.0]
class TestSetIfcTrueNorth(NewFile): class TestSetIfcTrueNorth(NewFile):
@@ -258,13 +259,13 @@ class TestGetMapConversion(NewFile):
class TestXyz2Enh(NewFile): class TestXyz2Enh(NewFile):
def test_run(self): def test_run(self):
assert subject.xyz2enh([0., 0., 0.], None) == [0., 0., 0.] assert subject.xyz2enh([0.0, 0.0, 0.0], None) == [0.0, 0.0, 0.0]
def test_using_the_blender_offset(self): def test_using_the_blender_offset(self):
props = bpy.context.scene.BIMGeoreferenceProperties props = bpy.context.scene.BIMGeoreferenceProperties
props.has_blender_offset = True props.has_blender_offset = True
props.blender_eastings = "1.0" props.blender_eastings = "1.0"
assert subject.xyz2enh([0., 0., 0.], None) == (1., 0., 0.) assert subject.xyz2enh([0.0, 0.0, 0.0], None) == (1.0, 0.0, 0.0)
def test_using_the_map_conversion(self): def test_using_the_map_conversion(self):
ifc = ifcopenshell.file() ifc = ifcopenshell.file()
@@ -274,7 +275,7 @@ class TestXyz2Enh(NewFile):
ifcopenshell.api.run("georeference.add_georeferencing", ifc) ifcopenshell.api.run("georeference.add_georeferencing", ifc)
map_conversion = ifc.by_type("IfcMapConversion")[0] map_conversion = ifc.by_type("IfcMapConversion")[0]
map_conversion.Eastings = 1.0 map_conversion.Eastings = 1.0
assert subject.xyz2enh([0., 0., 0.], map_conversion) == (1., 0., 0.) assert subject.xyz2enh([0.0, 0.0, 0.0], map_conversion) == (1.0, 0.0, 0.0)
def test_applying_both_blender_offset_and_map_conversion(self): def test_applying_both_blender_offset_and_map_conversion(self):
props = bpy.context.scene.BIMGeoreferenceProperties props = bpy.context.scene.BIMGeoreferenceProperties
@@ -287,18 +288,18 @@ class TestXyz2Enh(NewFile):
ifcopenshell.api.run("georeference.add_georeferencing", ifc) ifcopenshell.api.run("georeference.add_georeferencing", ifc)
map_conversion = ifc.by_type("IfcMapConversion")[0] map_conversion = ifc.by_type("IfcMapConversion")[0]
map_conversion.Northings = 1.0 map_conversion.Northings = 1.0
assert subject.xyz2enh([0., 0., 0.], map_conversion) == (1., 1., 0.) assert subject.xyz2enh([0.0, 0.0, 0.0], map_conversion) == (1.0, 1.0, 0.0)
class TestEnh2Xyz(NewFile): class TestEnh2Xyz(NewFile):
def test_run(self): def test_run(self):
assert subject.enh2xyz([0., 0., 0.], None) == [0., 0., 0.] assert subject.enh2xyz([0.0, 0.0, 0.0], None) == [0.0, 0.0, 0.0]
def test_using_the_blender_offset(self): def test_using_the_blender_offset(self):
props = bpy.context.scene.BIMGeoreferenceProperties props = bpy.context.scene.BIMGeoreferenceProperties
props.has_blender_offset = True props.has_blender_offset = True
props.blender_eastings = "1.0" props.blender_eastings = "1.0"
assert subject.enh2xyz([0., 0., 0.], None) == (-1., 0., 0.) assert subject.enh2xyz([0.0, 0.0, 0.0], None) == (-1.0, 0.0, 0.0)
def test_using_the_map_conversion(self): def test_using_the_map_conversion(self):
ifc = ifcopenshell.file() ifc = ifcopenshell.file()
@@ -308,7 +309,7 @@ class TestEnh2Xyz(NewFile):
ifcopenshell.api.run("georeference.add_georeferencing", ifc) ifcopenshell.api.run("georeference.add_georeferencing", ifc)
map_conversion = ifc.by_type("IfcMapConversion")[0] map_conversion = ifc.by_type("IfcMapConversion")[0]
map_conversion.Eastings = 1.0 map_conversion.Eastings = 1.0
assert subject.enh2xyz([0., 0., 0.], map_conversion) == (-1., 0., 0.) assert subject.enh2xyz([0.0, 0.0, 0.0], map_conversion) == (-1.0, 0.0, 0.0)
def test_applying_both_blender_offset_and_map_conversion(self): def test_applying_both_blender_offset_and_map_conversion(self):
props = bpy.context.scene.BIMGeoreferenceProperties props = bpy.context.scene.BIMGeoreferenceProperties
@@ -321,7 +322,7 @@ class TestEnh2Xyz(NewFile):
ifcopenshell.api.run("georeference.add_georeferencing", ifc) ifcopenshell.api.run("georeference.add_georeferencing", ifc)
map_conversion = ifc.by_type("IfcMapConversion")[0] map_conversion = ifc.by_type("IfcMapConversion")[0]
map_conversion.Northings = 1.0 map_conversion.Northings = 1.0
assert subject.enh2xyz([0., 0., 0.], map_conversion) == (-1., -1., 0.) assert subject.enh2xyz([0.0, 0.0, 0.0], map_conversion) == (-1.0, -1.0, 0.0)
class TestSetIfcGridNorth(NewFile): class TestSetIfcGridNorth(NewFile):
@@ -106,7 +106,12 @@ class Usecase:
properties.append(value) properties.append(value)
continue continue
else: else:
nominal_value = value properties.append(
self.file.create_entity(
"IfcPropertySingleValue",
**{"Name": name, "NominalValue": value},
)
)
#TODO-The following "elif" is temporary code, will need to refactor at some point - vulevukusej #TODO-The following "elif" is temporary code, will need to refactor at some point - vulevukusej
elif isinstance(value, list): elif isinstance(value, list):
for pset_template in self.settings["pset_template"].HasPropertyTemplates: for pset_template in self.settings["pset_template"].HasPropertyTemplates:
@@ -130,12 +135,12 @@ class Usecase:
value = self.cast_value_to_primary_measure_type(value, primary_measure_type) value = self.cast_value_to_primary_measure_type(value, primary_measure_type)
nominal_value = self.file.create_entity(primary_measure_type, value) nominal_value = self.file.create_entity(primary_measure_type, value)
properties.append( properties.append(
self.file.create_entity( self.file.create_entity(
"IfcPropertySingleValue", "IfcPropertySingleValue",
**{"Name": name, "NominalValue": nominal_value}, **{"Name": name, "NominalValue": nominal_value},
)
) )
)
return properties return properties
def extend_pset_with_new_properties(self, new_properties): def extend_pset_with_new_properties(self, new_properties):
@@ -1,4 +1,4 @@
lxml==4.6.5 lxml==4.9.1
numpy==1.22.0 numpy==1.22.0
regex==2021.4.4 regex==2021.4.4
xmlschema==1.6.4 xmlschema==1.6.4
@@ -145,6 +145,7 @@ def create_case(fn, openings):
class TestWallOpenings: class TestWallOpenings:
@pytest.mark.skipif(shutil.which("IfcConvert") is None, reason="Requires IfcConvert in path")
def test_all(self): def test_all(self):
cases = [ cases = [
@@ -244,4 +245,4 @@ class TestWallOpenings:
if __name__ == "__main__": if __name__ == "__main__":
TestWallOpenings().test_all() pytest.main(["-x", __file__])
+9 -9
View File
@@ -483,6 +483,15 @@ struct ShapeRTTI : public boost::static_visitor<PyObject*>
} }
} }
if (!ifc_representation) {
if (reps->size()) {
// Return a random representation
ifc_representation = *reps->begin();
} else {
throw IfcParse::IfcException("No suitable IfcRepresentation found");
}
}
// Read precision for found representation's context // Read precision for found representation's context
auto context = ifc_representation->ContextOfItems(); auto context = ifc_representation->ContextOfItems();
if (context->template as<typename Schema::IfcGeometricRepresentationSubContext>()) { if (context->template as<typename Schema::IfcGeometricRepresentationSubContext>()) {
@@ -499,15 +508,6 @@ struct ShapeRTTI : public boost::static_visitor<PyObject*>
kernel.setValue(IfcGeom::Kernel::GV_PRECISION, p); kernel.setValue(IfcGeom::Kernel::GV_PRECISION, p);
} }
if (!ifc_representation) {
if (reps->size()) {
// Return a random representation
ifc_representation = *reps->begin();
} else {
throw IfcParse::IfcException("No suitable IfcRepresentation found");
}
}
IfcGeom::BRepElement* brep = kernel.convert(settings, ifc_representation, product); IfcGeom::BRepElement* brep = kernel.convert(settings, ifc_representation, product);
if (!brep) { if (!brep) {
throw IfcParse::IfcException("Failed to process shape"); throw IfcParse::IfcException("Failed to process shape");
+1 -1
View File
@@ -808,7 +808,7 @@ void SvgSerializer::write(const geometry_data& data) {
const std::string& ty_entity_name = ty->declaration().name(); const std::string& ty_entity_name = ty->declaration().name();
// Damn you, IFC // Damn you, IFC
if (ty_entity_name == "IfcDoorStyle" || ty_entity_name == "IfcDoorType") { if (ty_entity_name == "IfcDoorStyle" || ty_entity_name == "IfcDoorType") {
operation_type = *((IfcUtil::IfcBaseEntity*)ty)->get("OperationType"); operation_type = (std::string)*((IfcUtil::IfcBaseEntity*)ty)->get("OperationType");
} }
} }
} }