mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-23 21:38:51 +00:00
ty: detect unresolved references
This commit is contained in:
@@ -68,6 +68,7 @@ def unpack_dependencies(install_dir: Path) -> None:
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
action = None
|
||||||
if len(sys.argv) != 2 or (action := sys.argv[1].lower()) not in ("pack", "unpack"):
|
if len(sys.argv) != 2 or (action := sys.argv[1].lower()) not in ("pack", "unpack"):
|
||||||
print(__doc__)
|
print(__doc__)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|||||||
@@ -82,8 +82,6 @@ ignore = [
|
|||||||
all = "error"
|
all = "error"
|
||||||
|
|
||||||
# Structural rules (no deep type inference needed, easier to adapt).
|
# Structural rules (no deep type inference needed, easier to adapt).
|
||||||
# Has false positives due to ty walrus operator bug.
|
|
||||||
possibly-unresolved-reference = "ignore"
|
|
||||||
# Maybe later, requires to specify element types for all generics.
|
# Maybe later, requires to specify element types for all generics.
|
||||||
missing-type-argument = "ignore"
|
missing-type-argument = "ignore"
|
||||||
# Conflicts with `bpy` props defined using annotations.
|
# Conflicts with `bpy` props defined using annotations.
|
||||||
@@ -194,7 +192,6 @@ format.sequence = ["black", "ruff"]
|
|||||||
cmake-format = "gersemi . --in-place"
|
cmake-format = "gersemi . --in-place"
|
||||||
|
|
||||||
[tool.poe.tasks.ty-ios]
|
[tool.poe.tasks.ty-ios]
|
||||||
# --ignore unresolved-reference: walrus operator false positives in ty.
|
|
||||||
cmd = """
|
cmd = """
|
||||||
ty check
|
ty check
|
||||||
nix/
|
nix/
|
||||||
@@ -212,7 +209,6 @@ cmd = """
|
|||||||
src/ifcpatch
|
src/ifcpatch
|
||||||
src/ifctester
|
src/ifctester
|
||||||
--python=src/ifcopenshell-python/.venv
|
--python=src/ifcopenshell-python/.venv
|
||||||
--ignore unresolved-reference
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
[tool.poe.tasks.bonsai-deps]
|
[tool.poe.tasks.bonsai-deps]
|
||||||
|
|||||||
@@ -188,6 +188,8 @@ class BcfClient:
|
|||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
return response.status_code, response.text
|
return response.status_code, response.text
|
||||||
except requests.exceptions.HTTPError as errh:
|
except requests.exceptions.HTTPError as errh:
|
||||||
|
response = errh.response
|
||||||
|
assert response is not None
|
||||||
print(f"message: {response.reason}' '{response.status_code}, {errh}")
|
print(f"message: {response.reason}' '{response.status_code}, {errh}")
|
||||||
return response.status_code, response.reason
|
return response.status_code, response.reason
|
||||||
|
|
||||||
@@ -206,6 +208,8 @@ class BcfClient:
|
|||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
return response.status_code, response.text
|
return response.status_code, response.text
|
||||||
except requests.exceptions.HTTPError as errh:
|
except requests.exceptions.HTTPError as errh:
|
||||||
|
response = errh.response
|
||||||
|
assert response is not None
|
||||||
print(f"message: {response.reason}' '{response.status_code}, {errh}")
|
print(f"message: {response.reason}' '{response.status_code}, {errh}")
|
||||||
return response.status_code, response.reason
|
return response.status_code, response.reason
|
||||||
|
|
||||||
@@ -222,6 +226,8 @@ class BcfClient:
|
|||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
return response.status_code, response.text
|
return response.status_code, response.text
|
||||||
except requests.exceptions.HTTPError as errh:
|
except requests.exceptions.HTTPError as errh:
|
||||||
|
response = errh.response
|
||||||
|
assert response is not None
|
||||||
print(f"message: {response.reason}' '{response.status_code}, {errh}")
|
print(f"message: {response.reason}' '{response.status_code}, {errh}")
|
||||||
return response.status_code, response.reason
|
return response.status_code, response.reason
|
||||||
|
|
||||||
|
|||||||
@@ -1103,12 +1103,14 @@ class IfcImporter:
|
|||||||
vertices = [[v[i], v[i + 1], v[i + 2], 1] for i in range(0, len(v), 3)]
|
vertices = [[v[i], v[i + 1], v[i + 2], 1] for i in range(0, len(v), 3)]
|
||||||
edges = [[e[i], e[i + 1]] for i in range(0, len(e), 2)]
|
edges = [[e[i], e[i + 1]] for i in range(0, len(e), 2)]
|
||||||
v2 = None
|
v2 = None
|
||||||
|
polyline = None
|
||||||
for edge in edges:
|
for edge in edges:
|
||||||
v1 = vertices[edge[0]]
|
v1 = vertices[edge[0]]
|
||||||
if v1 != v2:
|
if v1 != v2:
|
||||||
polyline = curve.splines.new("POLY")
|
polyline = curve.splines.new("POLY")
|
||||||
polyline.points[-1].co = mathutils.Vector(v1)
|
polyline.points[-1].co = mathutils.Vector(v1)
|
||||||
v2 = vertices[edge[1]]
|
v2 = vertices[edge[1]]
|
||||||
|
assert polyline is not None
|
||||||
polyline.points.add(1)
|
polyline.points.add(1)
|
||||||
polyline.points[-1].co = mathutils.Vector(v2)
|
polyline.points[-1].co = mathutils.Vector(v2)
|
||||||
edges_item_ids = ifcopenshell.util.shape.get_edges_representation_item_ids(geometry).tolist()
|
edges_item_ids = ifcopenshell.util.shape.get_edges_representation_item_ids(geometry).tolist()
|
||||||
|
|||||||
@@ -1059,6 +1059,7 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
return tool.Ifc.get().createIfcConnectionSurfaceGeometry(surface)
|
return tool.Ifc.get().createIfcConnectionSurfaceGeometry(surface)
|
||||||
|
|
||||||
def export_surface(self, polygon, target_face_matrix):
|
def export_surface(self, polygon, target_face_matrix):
|
||||||
|
ifc_file = tool.Ifc.get()
|
||||||
x_axis = target_face_matrix.col[0][:3]
|
x_axis = target_face_matrix.col[0][:3]
|
||||||
z_axis = target_face_matrix.col[2][:3]
|
z_axis = target_face_matrix.col[2][:3]
|
||||||
p1 = target_face_matrix.translation
|
p1 = target_face_matrix.translation
|
||||||
@@ -1071,18 +1072,20 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
placement = builder.create_axis2_placement_3d([o / self.unit_scale for o in p1], z_axis, x_axis)
|
placement = builder.create_axis2_placement_3d([o / self.unit_scale for o in p1], z_axis, x_axis)
|
||||||
surface.BasisSurface = tool.Ifc.get().create_entity("IfcPlane", placement)
|
surface.BasisSurface = tool.Ifc.get().create_entity("IfcPlane", placement)
|
||||||
|
|
||||||
if tool.Ifc.get().schema != "IFC2X3":
|
schema = ifc_file.schema
|
||||||
|
if schema != "IFC2X3":
|
||||||
points = [tool.Model.convert_si_to_unit(list(co)) for co in polygon.exterior.coords]
|
points = [tool.Model.convert_si_to_unit(list(co)) for co in polygon.exterior.coords]
|
||||||
point_list = tool.Ifc.get().createIfcCartesianPointList2D(points)
|
point_list = tool.Ifc.get().createIfcCartesianPointList2D(points)
|
||||||
outer_boundary = tool.Ifc.get().createIfcIndexedPolyCurve(point_list, None, False)
|
outer_boundary = tool.Ifc.get().createIfcIndexedPolyCurve(point_list, None, False)
|
||||||
|
|
||||||
inner_boundaries = []
|
inner_boundaries: list[ifcopenshell.entity_instance] = []
|
||||||
for interior in polygon.interiors:
|
for interior in polygon.interiors:
|
||||||
points = [tool.Model.convert_si_to_unit(list(co)) for co in interior.coords]
|
points = [tool.Model.convert_si_to_unit(list(co)) for co in interior.coords]
|
||||||
point_list = tool.Ifc.get().createIfcCartesianPointList2D(points)
|
point_list = tool.Ifc.get().createIfcCartesianPointList2D(points)
|
||||||
inner_boundaries.append(tool.Ifc.get().createIfcIndexedPolyCurve(point_list, None, False))
|
inner_boundaries.append(tool.Ifc.get().createIfcIndexedPolyCurve(point_list, None, False))
|
||||||
else:
|
else:
|
||||||
pass # TODO
|
# TODO:
|
||||||
|
raise NotImplementedError(schema)
|
||||||
|
|
||||||
surface.OuterBoundary = outer_boundary
|
surface.OuterBoundary = outer_boundary
|
||||||
surface.InnerBoundaries = inner_boundaries
|
surface.InnerBoundaries = inner_boundaries
|
||||||
|
|||||||
@@ -400,6 +400,7 @@ class CadOffset(bpy.types.Operator):
|
|||||||
[verts.update(e.verts) for e in edges]
|
[verts.update(e.verts) for e in edges]
|
||||||
|
|
||||||
# Use the viewport angle to determine the offset direction
|
# Use the viewport angle to determine the offset direction
|
||||||
|
wp = None
|
||||||
for area in bpy.context.screen.areas:
|
for area in bpy.context.screen.areas:
|
||||||
if area.type == "VIEW_3D":
|
if area.type == "VIEW_3D":
|
||||||
# Don't ask me, I don't know.
|
# Don't ask me, I don't know.
|
||||||
@@ -409,6 +410,7 @@ class CadOffset(bpy.types.Operator):
|
|||||||
z = area.spaces.active.region_3d.view_rotation @ Vector((0, 0, 1))
|
z = area.spaces.active.region_3d.view_rotation @ Vector((0, 0, 1))
|
||||||
wp = Matrix([x, y, z, Vector((0, 0, 0))]).to_4x4().transposed()
|
wp = Matrix([x, y, z, Vector((0, 0, 0))]).to_4x4().transposed()
|
||||||
break
|
break
|
||||||
|
assert wp is not None
|
||||||
|
|
||||||
rotation = Matrix.Rotation(pi / 2, 2, "Z")
|
rotation = Matrix.Rotation(pi / 2, 2, "Z")
|
||||||
rotation_i = Matrix.Rotation(-pi / 2, 2, "Z")
|
rotation_i = Matrix.Rotation(-pi / 2, 2, "Z")
|
||||||
|
|||||||
@@ -478,6 +478,7 @@ class ChangeClassificationLevel(bpy.types.Operator):
|
|||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
props = tool.Classification.get_classification_props()
|
props = tool.Classification.get_classification_props()
|
||||||
props.available_library_references.clear()
|
props.available_library_references.clear()
|
||||||
|
reference = None
|
||||||
for reference in IfcStore.classification_file.by_id(self.parent_id).HasReferences:
|
for reference in IfcStore.classification_file.by_id(self.parent_id).HasReferences:
|
||||||
new = props.available_library_references.add()
|
new = props.available_library_references.add()
|
||||||
new.identification = reference.Identification or ""
|
new.identification = reference.Identification or ""
|
||||||
@@ -485,6 +486,7 @@ class ChangeClassificationLevel(bpy.types.Operator):
|
|||||||
new.ifc_definition_id = reference.id()
|
new.ifc_definition_id = reference.id()
|
||||||
new.has_references = bool(reference.HasReferences)
|
new.has_references = bool(reference.HasReferences)
|
||||||
new.referenced_source
|
new.referenced_source
|
||||||
|
assert reference
|
||||||
if reference.ReferencedSource.is_a("IfcClassificationReference"):
|
if reference.ReferencedSource.is_a("IfcClassificationReference"):
|
||||||
props.active_library_referenced_source = reference.ReferencedSource.ReferencedSource.id()
|
props.active_library_referenced_source = reference.ReferencedSource.ReferencedSource.id()
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -156,6 +156,8 @@ class CostSchedulesData:
|
|||||||
values = root_element.CostValues
|
values = root_element.CostValues
|
||||||
elif root_element.is_a("IfcConstructionResource"):
|
elif root_element.is_a("IfcConstructionResource"):
|
||||||
values = root_element.BaseCosts
|
values = root_element.BaseCosts
|
||||||
|
else:
|
||||||
|
assert False, root_element
|
||||||
for cost_value in values or []:
|
for cost_value in values or []:
|
||||||
cls._load_cost_value(root_element, data, cost_value)
|
cls._load_cost_value(root_element, data, cost_value)
|
||||||
# data["CostValues"].append(cost_value.id())
|
# data["CostValues"].append(cost_value.id())
|
||||||
|
|||||||
@@ -425,10 +425,12 @@ class BaseDecorator:
|
|||||||
|
|
||||||
blf.size(font_id, font_size_px)
|
blf.size(font_id, font_size_px)
|
||||||
|
|
||||||
|
w, h = None, None
|
||||||
if box_alignment or center or vcenter:
|
if box_alignment or center or vcenter:
|
||||||
w, h = blf.dimensions(font_id, text)
|
w, h = blf.dimensions(font_id, text)
|
||||||
|
|
||||||
if box_alignment:
|
if box_alignment:
|
||||||
|
assert w is not None and h is not None
|
||||||
box_alignment_offset = Vector((0, 0))
|
box_alignment_offset = Vector((0, 0))
|
||||||
if "bottom" in box_alignment:
|
if "bottom" in box_alignment:
|
||||||
pass
|
pass
|
||||||
@@ -450,10 +452,12 @@ class BaseDecorator:
|
|||||||
else:
|
else:
|
||||||
# horizontal centering
|
# horizontal centering
|
||||||
if center:
|
if center:
|
||||||
|
assert w is not None
|
||||||
pos -= Vector((cos, sin)) * w * 0.5
|
pos -= Vector((cos, sin)) * w * 0.5
|
||||||
|
|
||||||
# vertical centering
|
# vertical centering
|
||||||
if vcenter:
|
if vcenter:
|
||||||
|
assert h is not None
|
||||||
pos -= Vector((-sin, cos)) * h * 0.5
|
pos -= Vector((-sin, cos)) * h * 0.5
|
||||||
|
|
||||||
# side-shifting
|
# side-shifting
|
||||||
@@ -1001,6 +1005,8 @@ class FallDecorator(BaseDecorator):
|
|||||||
O = A.copy()
|
O = A.copy()
|
||||||
O.z = B.z
|
O.z = B.z
|
||||||
run = (B - O).length
|
run = (B - O).length
|
||||||
|
|
||||||
|
angle_tg = None
|
||||||
if run != 0:
|
if run != 0:
|
||||||
angle_tg = rise / run
|
angle_tg = rise / run
|
||||||
angle = round(degrees(atan(angle_tg)))
|
angle = round(degrees(atan(angle_tg)))
|
||||||
@@ -1018,6 +1024,7 @@ class FallDecorator(BaseDecorator):
|
|||||||
elif object_type == "SLOPE_PERCENT":
|
elif object_type == "SLOPE_PERCENT":
|
||||||
if angle == 90:
|
if angle == 90:
|
||||||
return "-"
|
return "-"
|
||||||
|
assert angle_tg is not None
|
||||||
return f"{round(angle_tg * 100)} %"
|
return f"{round(angle_tg * 100)} %"
|
||||||
return "NO DATA"
|
return "NO DATA"
|
||||||
|
|
||||||
@@ -1249,6 +1256,7 @@ class SectionLevelDecorator(BaseDecorator):
|
|||||||
}
|
}
|
||||||
|
|
||||||
# process edges
|
# process edges
|
||||||
|
text_position, text_dir = None, None
|
||||||
for edge in edges_original:
|
for edge in edges_original:
|
||||||
v0, v1 = winspace_verts[edge[0]], winspace_verts[edge[1]]
|
v0, v1 = winspace_verts[edge[0]], winspace_verts[edge[1]]
|
||||||
start_i = len(output_verts)
|
start_i = len(output_verts)
|
||||||
@@ -1554,32 +1562,39 @@ class SectionDecorator(BaseDecorator):
|
|||||||
v0, v1 = winspace_verts[edge[0]], winspace_verts[edge[1]]
|
v0, v1 = winspace_verts[edge[0]], winspace_verts[edge[1]]
|
||||||
start_i = len(output_verts)
|
start_i = len(output_verts)
|
||||||
|
|
||||||
|
circle_head = None
|
||||||
if display_start_circle or display_end_circle:
|
if display_start_circle or display_end_circle:
|
||||||
circle_head = get_circle_head(circle_size)
|
circle_head = get_circle_head(circle_size)
|
||||||
|
|
||||||
if display_start_symbol or display_end_symbol or connect_markers:
|
triangle_head, divider_offset, edge_dir_circle = None, None, None
|
||||||
|
display_symbol = display_start_symbol or display_end_symbol
|
||||||
|
if display_symbol or connect_markers:
|
||||||
edge_dir = (v1 - v0).normalized()
|
edge_dir = (v1 - v0).normalized()
|
||||||
side = (edge_dir.yx * Vector((1, -1))).to_3d()
|
side = (edge_dir.yx * Vector((1, -1))).to_3d()
|
||||||
edge_dir_circle = edge_dir * circle_size
|
edge_dir_circle = edge_dir * circle_size
|
||||||
|
|
||||||
if display_start_symbol or display_end_symbol:
|
if display_symbol:
|
||||||
triangle_head = get_triangle_head(edge_dir, -side, triangle_length, triangle_width)
|
triangle_head = get_triangle_head(edge_dir, -side, triangle_length, triangle_width)
|
||||||
divider_offset = []
|
divider_offset = []
|
||||||
divider_offset.append(edge_dir_circle if connect_markers else edge_dir_circle * 3)
|
divider_offset.append(edge_dir_circle if connect_markers else edge_dir_circle * 3)
|
||||||
divider_offset.append(edge_dir_circle)
|
divider_offset.append(edge_dir_circle)
|
||||||
|
|
||||||
if display_start_circle:
|
if display_start_circle:
|
||||||
|
assert circle_head is not None
|
||||||
start_i = add_verts_sequence([v + v0 for v in circle_head], start_i, **out_kwargs, closed=True)
|
start_i = add_verts_sequence([v + v0 for v in circle_head], start_i, **out_kwargs, closed=True)
|
||||||
# circle middle divider
|
# circle middle divider
|
||||||
if not display_start_symbol:
|
if not display_start_symbol:
|
||||||
|
assert divider_offset is not None
|
||||||
start_i = add_verts_sequence(
|
start_i = add_verts_sequence(
|
||||||
[v0 + divider_offset[0], v0 - divider_offset[1]], start_i, **out_kwargs
|
[v0 + divider_offset[0], v0 - divider_offset[1]], start_i, **out_kwargs
|
||||||
)
|
)
|
||||||
|
|
||||||
if display_start_symbol:
|
if display_start_symbol:
|
||||||
|
assert triangle_head is not None
|
||||||
start_i = add_verts_sequence([v + v0 for v in triangle_head], start_i, **out_kwargs, closed=True)
|
start_i = add_verts_sequence([v + v0 for v in triangle_head], start_i, **out_kwargs, closed=True)
|
||||||
|
|
||||||
if display_end_circle:
|
if display_end_circle:
|
||||||
|
assert circle_head is not None
|
||||||
start_i = add_verts_sequence([v + v1 for v in circle_head], start_i, **out_kwargs, closed=True)
|
start_i = add_verts_sequence([v + v1 for v in circle_head], start_i, **out_kwargs, closed=True)
|
||||||
# circle middle divider
|
# circle middle divider
|
||||||
if not display_end_symbol:
|
if not display_end_symbol:
|
||||||
@@ -1588,9 +1603,11 @@ class SectionDecorator(BaseDecorator):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if display_end_symbol:
|
if display_end_symbol:
|
||||||
|
assert triangle_head is not None
|
||||||
start_i = add_verts_sequence([v + v1 for v in triangle_head], start_i, **out_kwargs, closed=True)
|
start_i = add_verts_sequence([v + v1 for v in triangle_head], start_i, **out_kwargs, closed=True)
|
||||||
|
|
||||||
if connect_markers:
|
if connect_markers:
|
||||||
|
assert edge_dir_circle is not None
|
||||||
gap = []
|
gap = []
|
||||||
gap.append(edge_dir_circle if display_start_symbol else Vector((0, 0, 0)))
|
gap.append(edge_dir_circle if display_start_symbol else Vector((0, 0, 0)))
|
||||||
gap.append(edge_dir_circle if display_end_symbol else Vector((0, 0, 0)))
|
gap.append(edge_dir_circle if display_end_symbol else Vector((0, 0, 0)))
|
||||||
@@ -1871,6 +1888,8 @@ class CutDecorator:
|
|||||||
layer_set = material
|
layer_set = material
|
||||||
offset = 0
|
offset = 0
|
||||||
sense_factor = 1
|
sense_factor = 1
|
||||||
|
else:
|
||||||
|
assert False, material
|
||||||
|
|
||||||
if len(layer_set.MaterialLayers) == 1:
|
if len(layer_set.MaterialLayers) == 1:
|
||||||
material = layer_set.MaterialLayers[0].Material
|
material = layer_set.MaterialLayers[0].Material
|
||||||
@@ -1897,6 +1916,8 @@ class CutDecorator:
|
|||||||
co = Vector((0.0, 0.0, offset))
|
co = Vector((0.0, 0.0, offset))
|
||||||
no = tool.Drawing.get_extrusion_vector(element).normalized()
|
no = tool.Drawing.get_extrusion_vector(element).normalized()
|
||||||
no = Vector([1.0, 0.0, 0.0])
|
no = Vector([1.0, 0.0, 0.0])
|
||||||
|
else:
|
||||||
|
assert False, usage
|
||||||
no *= sense_factor
|
no *= sense_factor
|
||||||
last_i = len(layer_set.MaterialLayers) - 1
|
last_i = len(layer_set.MaterialLayers) - 1
|
||||||
|
|
||||||
|
|||||||
@@ -225,9 +225,11 @@ def format_distance(
|
|||||||
unit_system, unit_length, unit_fraction = unit_mapping[custom_unit]
|
unit_system, unit_length, unit_fraction = unit_mapping[custom_unit]
|
||||||
|
|
||||||
value *= unit_scale
|
value *= unit_scale
|
||||||
|
tx_dist = None
|
||||||
|
|
||||||
# Imperial Formatting
|
# Imperial Formatting
|
||||||
if unit_system == "IMPERIAL":
|
if unit_system == "IMPERIAL":
|
||||||
|
toInches = None
|
||||||
if in_unit_length:
|
if in_unit_length:
|
||||||
if unit_length == "INCHES":
|
if unit_length == "INCHES":
|
||||||
toInches = 1
|
toInches = 1
|
||||||
@@ -241,6 +243,7 @@ def format_distance(
|
|||||||
toInches = 1550
|
toInches = 1550
|
||||||
inPerFoot = 144
|
inPerFoot = 144
|
||||||
|
|
||||||
|
assert toInches is not None
|
||||||
decInches = value * toInches
|
decInches = value * toInches
|
||||||
decFeet = decInches / 12
|
decFeet = decInches / 12
|
||||||
|
|
||||||
@@ -383,6 +386,7 @@ def format_distance(
|
|||||||
if precision and isinstance(precision, float):
|
if precision and isinstance(precision, float):
|
||||||
value = precision * round(float(value) / precision)
|
value = precision * round(float(value) / precision)
|
||||||
|
|
||||||
|
fmt = None
|
||||||
if decimal_places is not None:
|
if decimal_places is not None:
|
||||||
fmt = "%1." + str(decimal_places) + "f"
|
fmt = "%1." + str(decimal_places) + "f"
|
||||||
|
|
||||||
@@ -465,6 +469,7 @@ def format_distance(
|
|||||||
assert f"Unexpected unit_system - '{unit_system}'."
|
assert f"Unexpected unit_system - '{unit_system}'."
|
||||||
# tx_dist = fmt % value
|
# tx_dist = fmt % value
|
||||||
|
|
||||||
|
assert tx_dist is not None
|
||||||
return tx_dist
|
return tx_dist
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -698,6 +698,8 @@ class CreateDrawing(bpy.types.Operator):
|
|||||||
layer_set = material
|
layer_set = material
|
||||||
offset = 0
|
offset = 0
|
||||||
sense_factor = 1
|
sense_factor = 1
|
||||||
|
else:
|
||||||
|
assert False, material
|
||||||
|
|
||||||
camera_matrix_i = context.scene.camera.matrix_world.inverted()
|
camera_matrix_i = context.scene.camera.matrix_world.inverted()
|
||||||
|
|
||||||
@@ -722,7 +724,6 @@ class CreateDrawing(bpy.types.Operator):
|
|||||||
bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.000001)
|
bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.000001)
|
||||||
bmesh.ops.triangle_fill(bm, use_dissolve=True, edges=bm.edges)
|
bmesh.ops.triangle_fill(bm, use_dissolve=True, edges=bm.edges)
|
||||||
|
|
||||||
prev_co = None
|
|
||||||
if not usage:
|
if not usage:
|
||||||
sense_factor = 1 # Assume the extrusion vector points in the direction sense
|
sense_factor = 1 # Assume the extrusion vector points in the direction sense
|
||||||
no = tool.Drawing.get_extrusion_vector(element).normalized()
|
no = tool.Drawing.get_extrusion_vector(element).normalized()
|
||||||
@@ -739,6 +740,8 @@ class CreateDrawing(bpy.types.Operator):
|
|||||||
co = Vector((0.0, 0.0, offset))
|
co = Vector((0.0, 0.0, offset))
|
||||||
no = tool.Drawing.get_extrusion_vector(element).normalized()
|
no = tool.Drawing.get_extrusion_vector(element).normalized()
|
||||||
no = Vector([1.0, 0.0, 0.0])
|
no = Vector([1.0, 0.0, 0.0])
|
||||||
|
else:
|
||||||
|
assert False, usage
|
||||||
no *= sense_factor
|
no *= sense_factor
|
||||||
last_i = len(layer_set.MaterialLayers) - 1
|
last_i = len(layer_set.MaterialLayers) - 1
|
||||||
for i, layer in enumerate(layer_set.MaterialLayers):
|
for i, layer in enumerate(layer_set.MaterialLayers):
|
||||||
@@ -906,6 +909,10 @@ class CreateDrawing(bpy.types.Operator):
|
|||||||
if os.path.isfile(svg_path) and self.props.should_use_linework_cache:
|
if os.path.isfile(svg_path) and self.props.should_use_linework_cache:
|
||||||
return svg_path
|
return svg_path
|
||||||
|
|
||||||
|
ifc = tool.Ifc.get()
|
||||||
|
semantics = None
|
||||||
|
pairs = None
|
||||||
|
|
||||||
# in case of printing multiple drawings we need to sync just once
|
# in case of printing multiple drawings we need to sync just once
|
||||||
if self.sync and self.drawing_index == 0:
|
if self.sync and self.drawing_index == 0:
|
||||||
with profile("sync"):
|
with profile("sync"):
|
||||||
|
|||||||
@@ -110,12 +110,14 @@ class Scheduler:
|
|||||||
y = self.margin
|
y = self.margin
|
||||||
rows = list(sheet.iter_rows())
|
rows = list(sheet.iter_rows())
|
||||||
total_rows = len(rows)
|
total_rows = len(rows)
|
||||||
|
x = None
|
||||||
for i, row in enumerate(rows):
|
for i, row in enumerate(rows):
|
||||||
# The last row may contain only null values
|
# The last row may contain only null values
|
||||||
if i == (total_rows - 1) and not [c for c in row if c.value is not None]:
|
if i == (total_rows - 1) and not [c for c in row if c.value is not None]:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
x = self.margin
|
x = self.margin
|
||||||
|
unmerged_height = None
|
||||||
for cell in row:
|
for cell in row:
|
||||||
if isinstance(cell, openpyxl.cell.cell.MergedCell):
|
if isinstance(cell, openpyxl.cell.cell.MergedCell):
|
||||||
column_letter = openpyxl.utils.get_column_letter(cell.column)
|
column_letter = openpyxl.utils.get_column_letter(cell.column)
|
||||||
@@ -230,8 +232,11 @@ class Scheduler:
|
|||||||
)
|
)
|
||||||
|
|
||||||
x += unmerged_width
|
x += unmerged_width
|
||||||
|
|
||||||
|
assert unmerged_height is not None
|
||||||
y += unmerged_height
|
y += unmerged_height
|
||||||
|
|
||||||
|
assert x is not None
|
||||||
total_width = x + self.margin
|
total_width = x + self.margin
|
||||||
total_height = y + self.margin
|
total_height = y + self.margin
|
||||||
self.svg["width"] = "{}mm".format(total_width)
|
self.svg["width"] = "{}mm".format(total_width)
|
||||||
@@ -375,6 +380,7 @@ class Scheduler:
|
|||||||
tri = 0
|
tri = 0
|
||||||
stop_iterating_over_rows = False
|
stop_iterating_over_rows = False
|
||||||
# TODO: row spans support?
|
# TODO: row spans support?
|
||||||
|
x = None
|
||||||
for tr in table.getElementsByType(TableRow):
|
for tr in table.getElementsByType(TableRow):
|
||||||
if stop_iterating_over_rows:
|
if stop_iterating_over_rows:
|
||||||
break
|
break
|
||||||
@@ -491,6 +497,7 @@ class Scheduler:
|
|||||||
tri += 1
|
tri += 1
|
||||||
y += height
|
y += height
|
||||||
|
|
||||||
|
assert x is not None
|
||||||
total_width = x + self.margin
|
total_width = x + self.margin
|
||||||
total_height = y + self.margin
|
total_height = y + self.margin
|
||||||
self.svg["width"] = "{}mm".format(total_width)
|
self.svg["width"] = "{}mm".format(total_width)
|
||||||
|
|||||||
@@ -102,16 +102,16 @@ void angle_circle_head(
|
|||||||
in vec4 circle_start, in float circle_angle,
|
in vec4 circle_start, in float circle_angle,
|
||||||
in bool counterclockwise,
|
in bool counterclockwise,
|
||||||
out vec4 head[CIRCLE_SEGS+1], out float angle_segs) {
|
out vec4 head[CIRCLE_SEGS+1], out float angle_segs) {
|
||||||
|
|
||||||
// 1 added to CIRCLE_SEGS because we're number of vertices
|
// 1 added to CIRCLE_SEGS because we're number of vertices
|
||||||
// for n segments is n+1
|
// for n segments is n+1
|
||||||
|
|
||||||
float angle_d;
|
float angle_d;
|
||||||
angle_d = PI * 2 / CIRCLE_SEGS; // 30d
|
angle_d = PI * 2 / CIRCLE_SEGS; // 30d
|
||||||
// need to bottom clamp it to 1, otherwise it causes Blender crash at extruding the curve
|
// need to bottom clamp it to 1, otherwise it causes Blender crash at extruding the curve
|
||||||
angle_segs = max(1, ceil(circle_angle / angle_d));
|
angle_segs = max(1, ceil(circle_angle / angle_d));
|
||||||
angle_d = circle_angle / angle_segs;
|
angle_d = circle_angle / angle_segs;
|
||||||
|
|
||||||
for(int i = 0; i < (angle_segs + 1); i++) {
|
for(int i = 0; i < (angle_segs + 1); i++) {
|
||||||
float angle = angle_d * i;
|
float angle = angle_d * i;
|
||||||
if (counterclockwise) {
|
if (counterclockwise) {
|
||||||
@@ -143,7 +143,7 @@ void cross_head(in vec4 dir, in float size, out vec4 head[3]) {
|
|||||||
#define do_vertex(pos, e) (do_vertex_util(pos, vec2(-(e).y, (e).x) / winsize.xy))
|
#define do_vertex(pos, e) (do_vertex_util(pos, vec2(-(e).y, (e).x) / winsize.xy))
|
||||||
#define do_vertex_win(pos, e) ( do_vertex( WIN2CLIP( pos ), e ) )
|
#define do_vertex_win(pos, e) ( do_vertex( WIN2CLIP( pos ), e ) )
|
||||||
|
|
||||||
// if vertex is shared by two segments of the line still need to emit it twice
|
// if vertex is shared by two segments of the line still need to emit it twice
|
||||||
// to avoid smoothing artifacts
|
// to avoid smoothing artifacts
|
||||||
// don't forget to initialize `vec2 EDGE_DIR` for macro to work
|
// don't forget to initialize `vec2 EDGE_DIR` for macro to work
|
||||||
// `pos0` / `pos1` - vertex position in clip space
|
// `pos0` / `pos1` - vertex position in clip space
|
||||||
@@ -197,10 +197,13 @@ void do_circle_head(vec4 pos_w, vec4 head[CIRCLE_SEGS]) {
|
|||||||
|
|
||||||
def add_verts_sequence(verts, start_i, output_verts, output_edges, closed=False):
|
def add_verts_sequence(verts, start_i, output_verts, output_edges, closed=False):
|
||||||
"""Add sequence of verts to output lists, returns next vertex index"""
|
"""Add sequence of verts to output lists, returns next vertex index"""
|
||||||
|
i = None
|
||||||
for i, v in enumerate(verts[:-1], start_i):
|
for i, v in enumerate(verts[:-1], start_i):
|
||||||
output_verts.append(v)
|
output_verts.append(v)
|
||||||
output_edges.append((i, i + 1))
|
output_edges.append((i, i + 1))
|
||||||
output_verts.append(verts[-1])
|
output_verts.append(verts[-1])
|
||||||
|
assert i is not None
|
||||||
|
|
||||||
if closed:
|
if closed:
|
||||||
output_edges.append((i + 1, start_i))
|
output_edges.append((i + 1, start_i))
|
||||||
return i + 2
|
return i + 2
|
||||||
@@ -273,7 +276,7 @@ class BaseShader:
|
|||||||
FRAG_GLSL = """
|
FRAG_GLSL = """
|
||||||
uniform vec4 color;
|
uniform vec4 color;
|
||||||
uniform float lineWidth;
|
uniform float lineWidth;
|
||||||
|
|
||||||
in float smoothline;
|
in float smoothline;
|
||||||
out vec4 fragColor;
|
out vec4 fragColor;
|
||||||
void main() {
|
void main() {
|
||||||
|
|||||||
@@ -1449,6 +1449,7 @@ class SvgWriter:
|
|||||||
angle_tg = rise / run
|
angle_tg = rise / run
|
||||||
angle = round(degrees(atan(angle_tg)))
|
angle = round(degrees(atan(angle_tg)))
|
||||||
else:
|
else:
|
||||||
|
angle_tg = None
|
||||||
angle = 90
|
angle = 90
|
||||||
|
|
||||||
# ues SLOPE_ANGLE as default
|
# ues SLOPE_ANGLE as default
|
||||||
@@ -1462,6 +1463,7 @@ class SvgWriter:
|
|||||||
elif object_type == "SLOPE_PERCENT":
|
elif object_type == "SLOPE_PERCENT":
|
||||||
if angle == 90:
|
if angle == 90:
|
||||||
return "-"
|
return "-"
|
||||||
|
assert angle_tg is not None
|
||||||
return f"{round(angle_tg * 100)} %"
|
return f"{round(angle_tg * 100)} %"
|
||||||
|
|
||||||
tag = element.Description or get_label_text()
|
tag = element.Description or get_label_text()
|
||||||
|
|||||||
@@ -964,14 +964,14 @@ class BIM_UL_sheets(bpy.types.UIList):
|
|||||||
|
|
||||||
if self.filter_name:
|
if self.filter_name:
|
||||||
filter_name = self.filter_name.lower()
|
filter_name = self.filter_name.lower()
|
||||||
active_sheet = None
|
active_sheet_index = None
|
||||||
for sheet in data.sheets:
|
for sheet in data.sheets:
|
||||||
if sheet.is_sheet:
|
if sheet.is_sheet:
|
||||||
active_sheet = sheet
|
|
||||||
active_sheet_index = len(flt_flags)
|
active_sheet_index = len(flt_flags)
|
||||||
if filter_name in sheet.name.lower() or filter_name in sheet.identification.lower():
|
if filter_name in sheet.name.lower() or filter_name in sheet.identification.lower():
|
||||||
flt_flags.append(self.bitflag_filter_item)
|
flt_flags.append(self.bitflag_filter_item)
|
||||||
if not sheet.is_sheet:
|
if not sheet.is_sheet:
|
||||||
|
assert active_sheet_index is not None
|
||||||
flt_flags[active_sheet_index] = self.bitflag_filter_item
|
flt_flags[active_sheet_index] = self.bitflag_filter_item
|
||||||
else:
|
else:
|
||||||
flt_flags.append(0)
|
flt_flags.append(0)
|
||||||
|
|||||||
@@ -75,9 +75,13 @@ class Helper:
|
|||||||
for face in bm.faces:
|
for face in bm.faces:
|
||||||
if len(face.verts) > 4:
|
if len(face.verts) > 4:
|
||||||
potential_faces.append(face)
|
potential_faces.append(face)
|
||||||
|
|
||||||
|
# TODO: replace with next(..., None)
|
||||||
|
face = None
|
||||||
for face in potential_faces:
|
for face in potential_faces:
|
||||||
if face.normal.z < -0.1:
|
if face.normal.z < -0.1:
|
||||||
break
|
break
|
||||||
|
assert face is not None
|
||||||
|
|
||||||
profile = [l.vert.index for l in face.loops]
|
profile = [l.vert.index for l in face.loops]
|
||||||
extrusion = self.detect_extrusion_edge(bm, face)
|
extrusion = self.detect_extrusion_edge(bm, face)
|
||||||
@@ -108,10 +112,12 @@ class Helper:
|
|||||||
if not potential_faces:
|
if not potential_faces:
|
||||||
potential_faces = bm.faces
|
potential_faces = bm.faces
|
||||||
|
|
||||||
|
# TODO: replace with next(..., None)
|
||||||
|
face = None
|
||||||
for face in potential_faces:
|
for face in potential_faces:
|
||||||
if face.normal.z < -0.1:
|
if face.normal.z < -0.1:
|
||||||
break
|
break
|
||||||
|
assert face is not None
|
||||||
profile = [l.vert.index for l in face.loops]
|
profile = [l.vert.index for l in face.loops]
|
||||||
extrusion = self.detect_extrusion_edge(bm, face)
|
extrusion = self.detect_extrusion_edge(bm, face)
|
||||||
|
|
||||||
@@ -145,9 +151,12 @@ class Helper:
|
|||||||
if total_verts > 4:
|
if total_verts > 4:
|
||||||
potential_faces.append(face)
|
potential_faces.append(face)
|
||||||
|
|
||||||
|
# TODO: replace with next(..., None)
|
||||||
|
face = None
|
||||||
for face in potential_faces:
|
for face in potential_faces:
|
||||||
if face.normal.z < -0.1:
|
if face.normal.z < -0.1:
|
||||||
break
|
break
|
||||||
|
assert face is not None
|
||||||
|
|
||||||
end_faces = []
|
end_faces = []
|
||||||
end_face_normal = face.normal
|
end_face_normal = face.normal
|
||||||
|
|||||||
@@ -3527,12 +3527,15 @@ class EditRepresentationItemShapeAspect(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
if props.representation_item_shape_aspect == "NEW":
|
if props.representation_item_shape_aspect == "NEW":
|
||||||
active_representation = tool.Geometry.get_active_representation(obj)
|
active_representation = tool.Geometry.get_active_representation(obj)
|
||||||
# find IfcProductRepresentationSelect based on current representation
|
# find IfcProductRepresentationSelect based on current representation
|
||||||
|
product_shape = None
|
||||||
if hasattr(element, "Representation"): # IfcProduct
|
if hasattr(element, "Representation"): # IfcProduct
|
||||||
product_shape = element.Representation
|
product_shape = element.Representation
|
||||||
else: # IfcTypeProduct
|
else: # IfcTypeProduct
|
||||||
for representation_map in element.RepresentationMaps:
|
for representation_map in element.RepresentationMaps:
|
||||||
if representation_map.MappedRepresentation == active_representation:
|
if representation_map.MappedRepresentation == active_representation:
|
||||||
product_shape = representation_map
|
product_shape = representation_map
|
||||||
|
assert product_shape is not None
|
||||||
|
|
||||||
previous_shape_aspect_id = props.active_item.shape_aspect_id
|
previous_shape_aspect_id = props.active_item.shape_aspect_id
|
||||||
# will be None if item didn't had a shape aspect
|
# will be None if item didn't had a shape aspect
|
||||||
previous_shape_aspect = tool.Ifc.get_entity_by_id(previous_shape_aspect_id)
|
previous_shape_aspect = tool.Ifc.get_entity_by_id(previous_shape_aspect_id)
|
||||||
@@ -3882,6 +3885,8 @@ class AddSweptAreaSolidItem(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
curve = builder.rectangle(size=Vector((0.5, 0.5)) / unit_scale)
|
curve = builder.rectangle(size=Vector((0.5, 0.5)) / unit_scale)
|
||||||
elif self.shape == "CYLINDER":
|
elif self.shape == "CYLINDER":
|
||||||
curve = builder.circle(radius=0.25 / unit_scale)
|
curve = builder.circle(radius=0.25 / unit_scale)
|
||||||
|
else:
|
||||||
|
assert False, self.shape
|
||||||
item = builder.extrude(
|
item = builder.extrude(
|
||||||
curve,
|
curve,
|
||||||
magnitude=0.5 / unit_scale,
|
magnitude=0.5 / unit_scale,
|
||||||
|
|||||||
@@ -156,6 +156,7 @@ class RadianceRender(bpy.types.Operator):
|
|||||||
print(f"Quality: {quality}, Detail: {detail}, Variability: {variability}")
|
print(f"Quality: {quality}, Detail: {detail}, Variability: {variability}")
|
||||||
print(f"Output directory: {output_dir}")
|
print(f"Output directory: {output_dir}")
|
||||||
|
|
||||||
|
hdr_image_path, hdr_mask_path, sky_map_cal_path = None, None
|
||||||
if use_hdr:
|
if use_hdr:
|
||||||
hdr_image = "noon_grass_2k.hdr"
|
hdr_image = "noon_grass_2k.hdr"
|
||||||
hdr_mask = "noon_grass_2k_mask.hdr"
|
hdr_mask = "noon_grass_2k_mask.hdr"
|
||||||
@@ -254,6 +255,9 @@ class RadianceRender(bpy.types.Operator):
|
|||||||
# 4 0 0 -1 180
|
# 4 0 0 -1 180
|
||||||
|
|
||||||
if use_hdr and choose_hdr_image == "Noon":
|
if use_hdr and choose_hdr_image == "Noon":
|
||||||
|
assert hdr_image_path is not None
|
||||||
|
assert hdr_mask_path is not None
|
||||||
|
assert sky_map_cal_path is not None
|
||||||
|
|
||||||
with open(sky_file_path, "w") as f:
|
with open(sky_file_path, "w") as f:
|
||||||
f.write(sky_description_str)
|
f.write(sky_description_str)
|
||||||
|
|||||||
@@ -564,6 +564,7 @@ class SelectAllArrayObjects(bpy.types.Operator):
|
|||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
self.report({"ERROR"}, f"Objects that don't have an array parent, were deselected.")
|
self.report({"ERROR"}, f"Objects that don't have an array parent, were deselected.")
|
||||||
object.select_set(False)
|
object.select_set(False)
|
||||||
|
continue
|
||||||
|
|
||||||
array_objects = tool.Array.get_all_objects(parent_element)
|
array_objects = tool.Array.get_all_objects(parent_element)
|
||||||
tool.Blender.set_objects_selection(
|
tool.Blender.set_objects_selection(
|
||||||
|
|||||||
@@ -408,6 +408,8 @@ class MEPGenerator:
|
|||||||
compare = tool.Cad.is_x(requested_value, fitting_value, compare_precision)
|
compare = tool.Cad.is_x(requested_value, fitting_value, compare_precision)
|
||||||
elif isinstance(fitting_value, list):
|
elif isinstance(fitting_value, list):
|
||||||
compare = tool.Cad.are_vectors_equal(requested_value, Vector(fitting_value), precision)
|
compare = tool.Cad.are_vectors_equal(requested_value, Vector(fitting_value), precision)
|
||||||
|
else:
|
||||||
|
assert False, f"{key} {second_key}"
|
||||||
return compare
|
return compare
|
||||||
|
|
||||||
ignore_keys = []
|
ignore_keys = []
|
||||||
@@ -476,11 +478,13 @@ class MEPGenerator:
|
|||||||
if predefined_type == "OBSTRUCTION":
|
if predefined_type == "OBSTRUCTION":
|
||||||
return packed_data
|
return packed_data
|
||||||
|
|
||||||
|
start_port = None
|
||||||
for port in ports:
|
for port in ports:
|
||||||
port_local_position = V(*port.ObjectPlacement.RelativePlacement.Location.Coordinates)
|
port_local_position = V(*port.ObjectPlacement.RelativePlacement.Location.Coordinates)
|
||||||
if tool.Cad.is_x(port_local_position.length, 0.0):
|
if tool.Cad.is_x(port_local_position.length, 0.0):
|
||||||
start_port = port
|
start_port = port
|
||||||
break
|
break
|
||||||
|
assert start_port is not None
|
||||||
|
|
||||||
connected_port = tool.System.get_connected_port(start_port)
|
connected_port = tool.System.get_connected_port(start_port)
|
||||||
connected_element = tool.System.get_port_relating_element(connected_port)
|
connected_element = tool.System.get_port_relating_element(connected_port)
|
||||||
|
|||||||
@@ -325,7 +325,7 @@ class AddOccurrence(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
if self.from_invoke and str(self.relating_type_id) in AuthoringData.data["relating_type_id"]:
|
if self.from_invoke and str(self.relating_type_id) in AuthoringData.data["relating_type_id"]:
|
||||||
props.relating_type_id = str(self.relating_type_id)
|
props.relating_type_id = str(self.relating_type_id)
|
||||||
|
|
||||||
building_obj = None
|
building_obj, building_element = None, None
|
||||||
if len(context.selected_objects) == 1 and context.active_object:
|
if len(context.selected_objects) == 1 and context.active_object:
|
||||||
building_obj = context.active_object
|
building_obj = context.active_object
|
||||||
building_element = tool.Ifc.get_entity(building_obj)
|
building_element = tool.Ifc.get_entity(building_obj)
|
||||||
|
|||||||
@@ -593,6 +593,8 @@ class DumbProfileJoiner:
|
|||||||
axisl = (profile2.matrix_world.inverted() @ axis1[1]) - (profile2.matrix_world.inverted() @ axis1[0])
|
axisl = (profile2.matrix_world.inverted() @ axis1[1]) - (profile2.matrix_world.inverted() @ axis1[0])
|
||||||
elif connection1 == "ATSTART":
|
elif connection1 == "ATSTART":
|
||||||
axisl = (profile2.matrix_world.inverted() @ axis1[0]) - (profile2.matrix_world.inverted() @ axis1[1])
|
axisl = (profile2.matrix_world.inverted() @ axis1[0]) - (profile2.matrix_world.inverted() @ axis1[1])
|
||||||
|
else:
|
||||||
|
assert False, connection1
|
||||||
xy_angle = degrees(Vector((1, 0)).angle_signed(axisl.normalized().to_2d()))
|
xy_angle = degrees(Vector((1, 0)).angle_signed(axisl.normalized().to_2d()))
|
||||||
if xy_angle >= -135 and xy_angle <= -45:
|
if xy_angle >= -135 and xy_angle <= -45:
|
||||||
closest_plane = "bottom"
|
closest_plane = "bottom"
|
||||||
@@ -617,6 +619,8 @@ class DumbProfileJoiner:
|
|||||||
axisl = (profile1.matrix_world.inverted() @ axis2[1]) - (profile1.matrix_world.inverted() @ axis2[0])
|
axisl = (profile1.matrix_world.inverted() @ axis2[1]) - (profile1.matrix_world.inverted() @ axis2[0])
|
||||||
elif connection2 == "ATSTART":
|
elif connection2 == "ATSTART":
|
||||||
axisl = (profile1.matrix_world.inverted() @ axis2[0]) - (profile1.matrix_world.inverted() @ axis2[1])
|
axisl = (profile1.matrix_world.inverted() @ axis2[0]) - (profile1.matrix_world.inverted() @ axis2[1])
|
||||||
|
else:
|
||||||
|
assert False, connection2
|
||||||
xy_angle2 = degrees(Vector((1, 0)).angle_signed(axisl.normalized().to_2d()))
|
xy_angle2 = degrees(Vector((1, 0)).angle_signed(axisl.normalized().to_2d()))
|
||||||
if xy_angle2 >= -135 and xy_angle2 <= -45:
|
if xy_angle2 >= -135 and xy_angle2 <= -45:
|
||||||
closest_plane2 = "bottom"
|
closest_plane2 = "bottom"
|
||||||
@@ -844,6 +848,8 @@ class DumbProfileJoiner:
|
|||||||
else:
|
else:
|
||||||
y_axis = obj.matrix_world.to_quaternion() @ Vector((0, 1, 0))
|
y_axis = obj.matrix_world.to_quaternion() @ Vector((0, 1, 0))
|
||||||
z_axis = obj.matrix_world.to_quaternion() @ Vector((-1, 0, 0))
|
z_axis = obj.matrix_world.to_quaternion() @ Vector((-1, 0, 0))
|
||||||
|
else:
|
||||||
|
assert False, plane
|
||||||
return self.create_matrix(p, x_axis, y_axis, z_axis)
|
return self.create_matrix(p, x_axis, y_axis, z_axis)
|
||||||
|
|
||||||
def create_matrix(self, p: Vector, x: Vector, y: Vector, z: Vector) -> Matrix:
|
def create_matrix(self, p: Vector, x: Vector, y: Vector, z: Vector) -> Matrix:
|
||||||
|
|||||||
@@ -508,6 +508,7 @@ class EditSketchExtrusionProfile(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
converter.run()
|
converter.run()
|
||||||
|
|
||||||
profile = tool.Ifc.get().createIfcArbitraryClosedProfileDef("AREA")
|
profile = tool.Ifc.get().createIfcArbitraryClosedProfileDef("AREA")
|
||||||
|
curve = None
|
||||||
for path in converter.paths:
|
for path in converter.paths:
|
||||||
points = []
|
points = []
|
||||||
lines = path[0]
|
lines = path[0]
|
||||||
@@ -517,6 +518,7 @@ class EditSketchExtrusionProfile(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
points.append(tool.Ifc.get().createIfcCartesianPoint(local_point))
|
points.append(tool.Ifc.get().createIfcCartesianPoint(local_point))
|
||||||
points.append(points[0])
|
points.append(points[0])
|
||||||
curve = tool.Ifc.get().createIfcPolyline(points)
|
curve = tool.Ifc.get().createIfcPolyline(points)
|
||||||
|
assert curve
|
||||||
profile.OuterCurve = curve
|
profile.OuterCurve = curve
|
||||||
|
|
||||||
old_profile = extrusion.SweptArea
|
old_profile = extrusion.SweptArea
|
||||||
|
|||||||
@@ -1577,6 +1577,7 @@ class DumbWallJoiner:
|
|||||||
# Get the ATEND connection from wall1 to use it in wall2
|
# Get the ATEND connection from wall1 to use it in wall2
|
||||||
relating_element = None
|
relating_element = None
|
||||||
connections = element1.ConnectedTo
|
connections = element1.ConnectedTo
|
||||||
|
relating_connection, description = ..., ...
|
||||||
for conn in connections:
|
for conn in connections:
|
||||||
if conn.is_a("IfcRelConnectsPathElements") and conn.RelatingConnectionType == "ATEND":
|
if conn.is_a("IfcRelConnectsPathElements") and conn.RelatingConnectionType == "ATEND":
|
||||||
relating_element = conn.RelatedElement
|
relating_element = conn.RelatedElement
|
||||||
@@ -1591,6 +1592,7 @@ class DumbWallJoiner:
|
|||||||
description = conn.Description
|
description = conn.Description
|
||||||
bonsai.core.geometry.remove_connection(tool.Geometry, connection=conn)
|
bonsai.core.geometry.remove_connection(tool.Geometry, connection=conn)
|
||||||
if relating_element:
|
if relating_element:
|
||||||
|
assert relating_connection is not ... and description is not ...
|
||||||
ifcopenshell.api.geometry.connect_path(
|
ifcopenshell.api.geometry.connect_path(
|
||||||
tool.Ifc.get(),
|
tool.Ifc.get(),
|
||||||
relating_element=relating_element,
|
relating_element=relating_element,
|
||||||
|
|||||||
@@ -714,6 +714,8 @@ class AppendLibraryElement(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
representations = element.RepresentationMaps or []
|
representations = element.RepresentationMaps or []
|
||||||
elif element.is_a("IfcProduct"):
|
elif element.is_a("IfcProduct"):
|
||||||
representations = [element.Representation] if element.Representation else []
|
representations = [element.Representation] if element.Representation else []
|
||||||
|
else:
|
||||||
|
assert False, element
|
||||||
for representation in representations or []:
|
for representation in representations or []:
|
||||||
for element in self.file.traverse(representation):
|
for element in self.file.traverse(representation):
|
||||||
if not element.is_a("IfcRepresentationItem") or not element.StyledByItem:
|
if not element.is_a("IfcRepresentationItem") or not element.StyledByItem:
|
||||||
@@ -2029,6 +2031,7 @@ class ExportIFC(bpy.types.Operator, ExportHelper):
|
|||||||
project_props = tool.Project.get_project_props()
|
project_props = tool.Project.get_project_props()
|
||||||
prefs = tool.Blender.get_addon_preferences()
|
prefs = tool.Blender.get_addon_preferences()
|
||||||
project_props.use_relative_project_path = self.use_relative_path
|
project_props.use_relative_project_path = self.use_relative_path
|
||||||
|
old_history_size, old_undo_steps = None, None
|
||||||
if prefs.should_disable_undo_on_save:
|
if prefs.should_disable_undo_on_save:
|
||||||
old_history_size = tool.Ifc.get().history_size
|
old_history_size = tool.Ifc.get().history_size
|
||||||
old_undo_steps = context.preferences.edit.undo_steps
|
old_undo_steps = context.preferences.edit.undo_steps
|
||||||
@@ -2036,6 +2039,7 @@ class ExportIFC(bpy.types.Operator, ExportHelper):
|
|||||||
context.preferences.edit.undo_steps = 0
|
context.preferences.edit.undo_steps = 0
|
||||||
IfcStore.execute_ifc_operator(self, context)
|
IfcStore.execute_ifc_operator(self, context)
|
||||||
if prefs.should_disable_undo_on_save:
|
if prefs.should_disable_undo_on_save:
|
||||||
|
assert old_history_size is not None and old_undo_steps is not None
|
||||||
tool.Ifc.get().history_size = old_history_size
|
tool.Ifc.get().history_size = old_history_size
|
||||||
context.preferences.edit.undo_steps = old_undo_steps
|
context.preferences.edit.undo_steps = old_undo_steps
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|||||||
@@ -113,6 +113,8 @@ class EditPset(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
elif props.active_pset_type == "QTO":
|
elif props.active_pset_type == "QTO":
|
||||||
pset = ifcopenshell.api.pset.add_qto(self.file, product=element, name=props.active_pset_name)
|
pset = ifcopenshell.api.pset.add_qto(self.file, product=element, name=props.active_pset_name)
|
||||||
props.active_pset_id = pset.id()
|
props.active_pset_id = pset.id()
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
if self.properties:
|
if self.properties:
|
||||||
properties = json.loads(self.properties)
|
properties = json.loads(self.properties)
|
||||||
|
|||||||
@@ -228,6 +228,8 @@ def get_qto_name(self: "PsetProperties", context: bpy.types.Context) -> tool.Ble
|
|||||||
if "bpy.data.objects" in pset_type:
|
if "bpy.data.objects" in pset_type:
|
||||||
if prop_type == "PsetProperties":
|
if prop_type == "PsetProperties":
|
||||||
results = get_object_qto_name(self, context)
|
results = get_object_qto_name(self, context)
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
elif prop_type == "TaskPsetProperties":
|
elif prop_type == "TaskPsetProperties":
|
||||||
results = get_task_qto_names(self, context)
|
results = get_task_qto_names(self, context)
|
||||||
elif prop_type == "ResourcePsetProperties":
|
elif prop_type == "ResourcePsetProperties":
|
||||||
|
|||||||
@@ -480,6 +480,7 @@ class BIM_PT_material_psets(Panel):
|
|||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
assert self.layout
|
assert self.layout
|
||||||
props = tool.Material.get_material_props()
|
props = tool.Material.get_material_props()
|
||||||
|
ifc_definition_id = None
|
||||||
if material := props.active_material:
|
if material := props.active_material:
|
||||||
ifc_definition_id = material.ifc_definition_id
|
ifc_definition_id = material.ifc_definition_id
|
||||||
|
|
||||||
|
|||||||
@@ -630,10 +630,13 @@ class AddElement(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
local_z = wall_matrix.to_3x3() @ Vector((0, 0, 1))
|
local_z = wall_matrix.to_3x3() @ Vector((0, 0, 1))
|
||||||
direction_sense = getattr(usage, "DirectionSense", "POSITIVE")
|
direction_sense = getattr(usage, "DirectionSense", "POSITIVE")
|
||||||
|
|
||||||
if usage.LayerSetDirection == "AXIS2":
|
layer_set_direction = usage.LayerSetDirection
|
||||||
|
if layer_set_direction == "AXIS2":
|
||||||
z_axis = tuple(local_y) if direction_sense == "POSITIVE" else tuple(-local_y)
|
z_axis = tuple(local_y) if direction_sense == "POSITIVE" else tuple(-local_y)
|
||||||
elif usage.LayerSetDirection == "AXIS3":
|
elif layer_set_direction == "AXIS3":
|
||||||
z_axis = tuple(local_z) if direction_sense == "POSITIVE" else tuple(-local_z)
|
z_axis = tuple(local_z) if direction_sense == "POSITIVE" else tuple(-local_z)
|
||||||
|
else:
|
||||||
|
assert False, layer_set_direction
|
||||||
|
|
||||||
item = builder.extrude(
|
item = builder.extrude(
|
||||||
profile,
|
profile,
|
||||||
@@ -763,6 +766,8 @@ class AddElement(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
WebThickness=default_web_thickness / unit_scale,
|
WebThickness=default_web_thickness / unit_scale,
|
||||||
FlangeThickness=default_flange_thickness / unit_scale,
|
FlangeThickness=default_flange_thickness / unit_scale,
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
|
assert False, representation_template
|
||||||
|
|
||||||
rel = ifcopenshell.api.material.assign_material(
|
rel = ifcopenshell.api.material.assign_material(
|
||||||
tool.Ifc.get(), products=[element], type="IfcMaterialProfileSet"
|
tool.Ifc.get(), products=[element], type="IfcMaterialProfileSet"
|
||||||
|
|||||||
@@ -1009,6 +1009,7 @@ class ColourByProperty(Operator):
|
|||||||
palette = props.palette
|
palette = props.palette
|
||||||
is_qualitative = palette in ("tab10", "paired")
|
is_qualitative = palette in ("tab10", "paired")
|
||||||
|
|
||||||
|
colours = None
|
||||||
if is_qualitative:
|
if is_qualitative:
|
||||||
colours = tool.Search.get_qualitative_palette(palette)
|
colours = tool.Search.get_qualitative_palette(palette)
|
||||||
|
|
||||||
@@ -1035,6 +1036,7 @@ class ColourByProperty(Operator):
|
|||||||
if value in colourscheme:
|
if value in colourscheme:
|
||||||
colourscheme[value]["total"] += 1
|
colourscheme[value]["total"] += 1
|
||||||
else:
|
else:
|
||||||
|
assert colours is not None
|
||||||
colourscheme[value] = {"colour": next(colours)[0:3], "total": 1}
|
colourscheme[value] = {"colour": next(colours)[0:3], "total": 1}
|
||||||
obj.color = (*colourscheme[value]["colour"], 1)
|
obj.color = (*colourscheme[value]["colour"], 1)
|
||||||
else:
|
else:
|
||||||
@@ -1139,6 +1141,7 @@ class SelectByProperty(Operator):
|
|||||||
|
|
||||||
is_qualitative = palette in ("tab10", "paired")
|
is_qualitative = palette in ("tab10", "paired")
|
||||||
|
|
||||||
|
values = None
|
||||||
if not is_qualitative:
|
if not is_qualitative:
|
||||||
values = []
|
values = []
|
||||||
for colour in props.colourscheme:
|
for colour in props.colourscheme:
|
||||||
|
|||||||
@@ -281,11 +281,11 @@ class BIM_PT_work_schedules(Panel):
|
|||||||
def draw_task_operators(self) -> None:
|
def draw_task_operators(self) -> None:
|
||||||
row = self.layout.row(align=True)
|
row = self.layout.row(align=True)
|
||||||
row.alignment = "RIGHT"
|
row.alignment = "RIGHT"
|
||||||
ifc_definition_id = None
|
task, ifc_definition_id = None, None
|
||||||
if self.tprops.tasks and self.props.active_task_index < len(self.tprops.tasks):
|
if self.tprops.tasks and self.props.active_task_index < len(self.tprops.tasks):
|
||||||
task = self.tprops.tasks[self.props.active_task_index]
|
task = self.tprops.tasks[self.props.active_task_index]
|
||||||
ifc_definition_id = task.ifc_definition_id
|
ifc_definition_id = task.ifc_definition_id
|
||||||
if ifc_definition_id:
|
if task and ifc_definition_id:
|
||||||
if self.props.active_task_id:
|
if self.props.active_task_id:
|
||||||
if self.props.editing_task_type == "TASKTIME":
|
if self.props.editing_task_type == "TASKTIME":
|
||||||
row.operator("bim.edit_task_time", text="", icon="CHECKMARK")
|
row.operator("bim.edit_task_time", text="", icon="CHECKMARK")
|
||||||
@@ -341,6 +341,8 @@ class BIM_PT_work_schedules(Panel):
|
|||||||
row.prop(self.props, "other_columns", text="")
|
row.prop(self.props, "other_columns", text="")
|
||||||
column_type, name = self.props.other_columns.split(".")
|
column_type, name = self.props.other_columns.split(".")
|
||||||
data_type = "string"
|
data_type = "string"
|
||||||
|
else:
|
||||||
|
assert False, column_type
|
||||||
row.operator("bim.set_task_sort_column", text="", icon="SORTALPHA").column = f"{column_type}.{name}"
|
row.operator("bim.set_task_sort_column", text="", icon="SORTALPHA").column = f"{column_type}.{name}"
|
||||||
row.prop(
|
row.prop(
|
||||||
self.props, "is_sort_reversed", text="", icon="SORT_DESC" if self.props.is_sort_reversed else "SORT_ASC"
|
self.props, "is_sort_reversed", text="", icon="SORT_DESC" if self.props.is_sort_reversed else "SORT_ASC"
|
||||||
|
|||||||
@@ -516,7 +516,7 @@ class SetContainerVisibility(bpy.types.Operator):
|
|||||||
if self.mode == "ISOLATE":
|
if self.mode == "ISOLATE":
|
||||||
if tool.Ifc.get_schema() == "IFC2X3":
|
if tool.Ifc.get_schema() == "IFC2X3":
|
||||||
containers = tool.Ifc.get().by_type("IfcSpatialStructureElement")
|
containers = tool.Ifc.get().by_type("IfcSpatialStructureElement")
|
||||||
elif tool.Ifc.get_schema() != "IFC2X3":
|
else:
|
||||||
containers = set(tool.Ifc.get().by_type("IfcSpatialElement"))
|
containers = set(tool.Ifc.get().by_type("IfcSpatialElement"))
|
||||||
containers -= set(tool.Ifc.get().by_type("IfcSpatialZone"))
|
containers -= set(tool.Ifc.get().by_type("IfcSpatialZone"))
|
||||||
for container in containers:
|
for container in containers:
|
||||||
|
|||||||
@@ -125,6 +125,7 @@ class BIM_PT_spatial_decomposition(Panel):
|
|||||||
row.label(text="Warning: No Default Container", icon="ERROR")
|
row.label(text="Warning: No Default Container", icon="ERROR")
|
||||||
row.operator("bim.import_spatial_decomposition", icon="FILE_REFRESH", text="")
|
row.operator("bim.import_spatial_decomposition", icon="FILE_REFRESH", text="")
|
||||||
|
|
||||||
|
ifc_definition_id = None
|
||||||
if self.props.active_container:
|
if self.props.active_container:
|
||||||
ifc_definition_id = self.props.active_container.ifc_definition_id
|
ifc_definition_id = self.props.active_container.ifc_definition_id
|
||||||
row = self.layout.row(align=True)
|
row = self.layout.row(align=True)
|
||||||
@@ -170,6 +171,7 @@ class BIM_PT_spatial_decomposition(Panel):
|
|||||||
|
|
||||||
if not self.props.active_container:
|
if not self.props.active_container:
|
||||||
return
|
return
|
||||||
|
assert ifc_definition_id is not None
|
||||||
|
|
||||||
container_has_elements = bool(self.props.total_elements)
|
container_has_elements = bool(self.props.total_elements)
|
||||||
if container_has_elements:
|
if container_has_elements:
|
||||||
|
|||||||
@@ -102,6 +102,7 @@ class BIM_PT_styles(Panel):
|
|||||||
|
|
||||||
# style ui tools
|
# style ui tools
|
||||||
if active_style:
|
if active_style:
|
||||||
|
style = active_style
|
||||||
row = self.layout.row(align=True)
|
row = self.layout.row(align=True)
|
||||||
if material := style.blender_material:
|
if material := style.blender_material:
|
||||||
msprops = tool.Style.get_material_style_props(material)
|
msprops = tool.Style.get_material_style_props(material)
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ class AddOpening(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
|
|
||||||
opening_objects = [obj for obj in selected_objects if obj != target_object]
|
opening_objects = [obj for obj in selected_objects if obj != target_object]
|
||||||
|
|
||||||
|
obj1 = ...
|
||||||
for opening_obj in opening_objects:
|
for opening_obj in opening_objects:
|
||||||
element1 = tool.Ifc.get_entity(target_object)
|
element1 = tool.Ifc.get_entity(target_object)
|
||||||
obj1 = target_object
|
obj1 = target_object
|
||||||
@@ -196,6 +197,7 @@ class AddOpening(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
bpy.data.objects.remove(obj2)
|
bpy.data.objects.remove(obj2)
|
||||||
|
|
||||||
tool.Model.purge_scene_openings()
|
tool.Model.purge_scene_openings()
|
||||||
|
assert obj1 is not ...
|
||||||
context.view_layer.objects.active = obj1
|
context.view_layer.objects.active = obj1
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|||||||
@@ -284,11 +284,13 @@ class GizmoPreferences(bpy.types.PropertyGroup):
|
|||||||
draw_gizmos_in_3d_viewport: bool
|
draw_gizmos_in_3d_viewport: bool
|
||||||
|
|
||||||
|
|
||||||
|
_gizmo_pref_entry = None
|
||||||
for _gizmo_pref_entry in tool.Parametric.EDIT_TYPES:
|
for _gizmo_pref_entry in tool.Parametric.EDIT_TYPES:
|
||||||
GizmoPreferences.__annotations__[_gizmo_pref_entry.name] = BoolProperty(
|
GizmoPreferences.__annotations__[_gizmo_pref_entry.name] = BoolProperty(
|
||||||
name=_gizmo_pref_entry.name.replace("_", " ").title(),
|
name=_gizmo_pref_entry.name.replace("_", " ").title(),
|
||||||
default=True,
|
default=True,
|
||||||
)
|
)
|
||||||
|
assert _gizmo_pref_entry is not None
|
||||||
del _gizmo_pref_entry
|
del _gizmo_pref_entry
|
||||||
|
|
||||||
|
|
||||||
@@ -394,12 +396,14 @@ class DefaultParameters(bpy.types.PropertyGroup):
|
|||||||
and gives the create operator a preset to copy from."""
|
and gives the create operator a preset to copy from."""
|
||||||
|
|
||||||
|
|
||||||
|
_default_params_entry = None
|
||||||
for _default_params_entry in tool.Parametric.EDIT_TYPES:
|
for _default_params_entry in tool.Parametric.EDIT_TYPES:
|
||||||
if not _default_params_entry.has_default_parameters:
|
if not _default_params_entry.has_default_parameters:
|
||||||
continue
|
continue
|
||||||
DefaultParameters.__annotations__[_default_params_entry.name] = bpy.props.PointerProperty(
|
DefaultParameters.__annotations__[_default_params_entry.name] = bpy.props.PointerProperty(
|
||||||
type=getattr(_model_prop, _default_params_entry.props_attr),
|
type=getattr(_model_prop, _default_params_entry.props_attr),
|
||||||
)
|
)
|
||||||
|
assert _default_params_entry is not None
|
||||||
del _default_params_entry
|
del _default_params_entry
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -74,10 +74,11 @@ def add_instance_ceiling_covering_from_cursor(
|
|||||||
if not relating_type.is_a("IfcCoveringType"):
|
if not relating_type.is_a("IfcCoveringType"):
|
||||||
relating_type = None
|
relating_type = None
|
||||||
|
|
||||||
|
ceiling_height = None
|
||||||
if selected_objects and active_obj:
|
if selected_objects and active_obj:
|
||||||
x, y, z, h, mat = spatial.get_x_y_z_h_mat_from_obj(active_obj)
|
x, y, z, _, _ = spatial.get_x_y_z_h_mat_from_obj(active_obj)
|
||||||
else:
|
else:
|
||||||
x, y, z, h, mat = spatial.get_x_y_z_h_mat_from_cursor()
|
x, y, z, _, _ = spatial.get_x_y_z_h_mat_from_cursor()
|
||||||
ceiling_height = covering.get_z_from_ceiling_height()
|
ceiling_height = covering.get_z_from_ceiling_height()
|
||||||
|
|
||||||
space_polygon = spatial.get_space_polygon_from_context_visible_objects(x, y)
|
space_polygon = spatial.get_space_polygon_from_context_visible_objects(x, y)
|
||||||
@@ -87,6 +88,7 @@ def add_instance_ceiling_covering_from_cursor(
|
|||||||
|
|
||||||
obj = spatial.create_object("Covering")
|
obj = spatial.create_object("Covering")
|
||||||
spatial.set_obj_origin_to_cursor_position_and_zero_elevation(obj)
|
spatial.set_obj_origin_to_cursor_position_and_zero_elevation(obj)
|
||||||
|
assert ceiling_height is not None
|
||||||
spatial.translate_obj_to_z_location(obj, z + ceiling_height)
|
spatial.translate_obj_to_z_location(obj, z + ceiling_height)
|
||||||
spatial.assign_type_to_obj(obj)
|
spatial.assign_type_to_obj(obj)
|
||||||
spatial.set_covering_representation_from_polygon(obj, space_polygon, polygon_is_si=True)
|
spatial.set_covering_representation_from_polygon(obj, space_polygon, polygon_is_si=True)
|
||||||
@@ -100,7 +102,9 @@ def regen_selected_covering_object(root: type[tool.Root], spatial: type[tool.Spa
|
|||||||
selected_objects = spatial.get_selected_objects()
|
selected_objects = spatial.get_selected_objects()
|
||||||
|
|
||||||
if selected_objects and active_obj:
|
if selected_objects and active_obj:
|
||||||
x, y, z, h, mat = spatial.get_x_y_z_h_mat_from_obj(active_obj)
|
x, y, _, _, _ = spatial.get_x_y_z_h_mat_from_obj(active_obj)
|
||||||
|
else:
|
||||||
|
assert False, "Object has to be active and selected."
|
||||||
|
|
||||||
space_polygon = spatial.get_space_polygon_from_context_visible_objects(x, y)
|
space_polygon = spatial.get_space_polygon_from_context_visible_objects(x, y)
|
||||||
|
|
||||||
|
|||||||
@@ -497,6 +497,7 @@ def add_annotation(
|
|||||||
drawing_tool.show_decorations()
|
drawing_tool.show_decorations()
|
||||||
obj = drawing_tool.create_annotation_object(drawing, object_type)
|
obj = drawing_tool.create_annotation_object(drawing, object_type)
|
||||||
element = ifc.get_entity(obj)
|
element = ifc.get_entity(obj)
|
||||||
|
relating_type_rep = None
|
||||||
if not element: # Brand new annotation
|
if not element: # Brand new annotation
|
||||||
relating_type_rep = drawing_tool.get_annotation_representation(relating_type) if relating_type else None
|
relating_type_rep = drawing_tool.get_annotation_representation(relating_type) if relating_type else None
|
||||||
element = drawing_tool.run_root_assign_class(
|
element = drawing_tool.run_root_assign_class(
|
||||||
|
|||||||
@@ -981,6 +981,7 @@ class Cad:
|
|||||||
has_found_connected_edge = True
|
has_found_connected_edge = True
|
||||||
loops.append(loop)
|
loops.append(loop)
|
||||||
|
|
||||||
|
new_verts = None
|
||||||
for loop in loops:
|
for loop in loops:
|
||||||
all_verts = {v.index for e in loop for v in e.verts}
|
all_verts = {v.index for e in loop for v in e.verts}
|
||||||
possible_v1s = []
|
possible_v1s = []
|
||||||
@@ -1084,6 +1085,7 @@ class Cad:
|
|||||||
break
|
break
|
||||||
|
|
||||||
v1 = v2
|
v1 = v2
|
||||||
|
assert new_verts is not None
|
||||||
|
|
||||||
return new_verts
|
return new_verts
|
||||||
|
|
||||||
|
|||||||
@@ -280,6 +280,8 @@ class Cost(bonsai.core.tool.Cost):
|
|||||||
new = props.cost_item_processes.add()
|
new = props.cost_item_processes.add()
|
||||||
elif related_object.is_a("IfcResource"):
|
elif related_object.is_a("IfcResource"):
|
||||||
new = props.cost_item_resources.add()
|
new = props.cost_item_resources.add()
|
||||||
|
else:
|
||||||
|
assert False, related_object
|
||||||
new.ifc_definition_id = related_object.id()
|
new.ifc_definition_id = related_object.id()
|
||||||
new.name = related_object.Name or "Unnamed"
|
new.name = related_object.Name or "Unnamed"
|
||||||
|
|
||||||
|
|||||||
@@ -2575,16 +2575,15 @@ class Drawing(bonsai.core.tool.Drawing):
|
|||||||
if not obj:
|
if not obj:
|
||||||
continue
|
continue
|
||||||
current_representation = tool.Geometry.get_active_representation(obj)
|
current_representation = tool.Geometry.get_active_representation(obj)
|
||||||
|
current_representation_subcontext = None
|
||||||
if current_representation:
|
if current_representation:
|
||||||
subcontext = current_representation.ContextOfItems
|
subcontext = current_representation.ContextOfItems
|
||||||
current_representation_subcontext = tool.Geometry.get_subcontext_parameters(subcontext)
|
current_representation_subcontext = tool.Geometry.get_subcontext_parameters(subcontext)
|
||||||
|
|
||||||
has_context = False
|
|
||||||
for subcontext in subcontexts:
|
for subcontext in subcontexts:
|
||||||
# prioritize already active representation if it matches the subcontext
|
# prioritize already active representation if it matches the subcontext
|
||||||
# (element could have multiple representations in the same subcontext)
|
# (element could have multiple representations in the same subcontext)
|
||||||
if current_representation and subcontext == current_representation_subcontext:
|
if current_representation_subcontext and subcontext == current_representation_subcontext:
|
||||||
has_context = True
|
|
||||||
break
|
break
|
||||||
priority_representation = ifcopenshell.util.representation.get_representation(element, *subcontext)
|
priority_representation = ifcopenshell.util.representation.get_representation(element, *subcontext)
|
||||||
if priority_representation:
|
if priority_representation:
|
||||||
@@ -2594,7 +2593,6 @@ class Drawing(bonsai.core.tool.Drawing):
|
|||||||
obj=obj,
|
obj=obj,
|
||||||
representation=priority_representation,
|
representation=priority_representation,
|
||||||
)
|
)
|
||||||
has_context = True
|
|
||||||
break
|
break
|
||||||
|
|
||||||
linked_handles: set[bpy.types.Object] = set()
|
linked_handles: set[bpy.types.Object] = set()
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ from typing import TYPE_CHECKING
|
|||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
import ifcopenshell.api.feature
|
import ifcopenshell.api.feature
|
||||||
import ifcopenshell.util.representation
|
|
||||||
|
|
||||||
import bonsai.core.geometry
|
import bonsai.core.geometry
|
||||||
import bonsai.core.tool
|
import bonsai.core.tool
|
||||||
@@ -50,6 +49,7 @@ class Feature(bonsai.core.tool.Feature):
|
|||||||
has_visible_openings = True
|
has_visible_openings = True
|
||||||
break
|
break
|
||||||
|
|
||||||
|
element_had_openings = None
|
||||||
for feature_obj in feature_objs:
|
for feature_obj in feature_objs:
|
||||||
feature_element = tool.Ifc.get_entity(feature_obj)
|
feature_element = tool.Ifc.get_entity(feature_obj)
|
||||||
|
|
||||||
@@ -58,7 +58,6 @@ class Feature(bonsai.core.tool.Feature):
|
|||||||
bonsai.core.geometry.edit_object_placement(tool.Ifc, tool.Geometry, tool.Surveyor, obj=featured_obj)
|
bonsai.core.geometry.edit_object_placement(tool.Ifc, tool.Geometry, tool.Surveyor, obj=featured_obj)
|
||||||
|
|
||||||
element_had_openings = tool.Geometry.has_openings(featured_element)
|
element_had_openings = tool.Geometry.has_openings(featured_element)
|
||||||
body_context = ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Model", "Body")
|
|
||||||
ifcopenshell.api.feature.add_feature(tool.Ifc.get(), feature=feature_element, element=featured_element)
|
ifcopenshell.api.feature.add_feature(tool.Ifc.get(), feature=feature_element, element=featured_element)
|
||||||
|
|
||||||
if tool.Ifc.is_moved(feature_obj):
|
if tool.Ifc.is_moved(feature_obj):
|
||||||
@@ -73,6 +72,7 @@ class Feature(bonsai.core.tool.Feature):
|
|||||||
if voided_obj.data:
|
if voided_obj.data:
|
||||||
if tool.Ifc.is_edited(voided_obj):
|
if tool.Ifc.is_edited(voided_obj):
|
||||||
voided_element_ = tool.Ifc.get_entity(voided_obj)
|
voided_element_ = tool.Ifc.get_entity(voided_obj)
|
||||||
|
assert element_had_openings is not None
|
||||||
if element_had_openings or (voided_element_ != featured_element and voided_element_.HasOpenings):
|
if element_had_openings or (voided_element_ != featured_element and voided_element_.HasOpenings):
|
||||||
voided_obj.scale = (1.0, 1.0, 1.0)
|
voided_obj.scale = (1.0, 1.0, 1.0)
|
||||||
tool.Ifc.finish_edit(voided_obj)
|
tool.Ifc.finish_edit(voided_obj)
|
||||||
|
|||||||
@@ -757,6 +757,7 @@ class Geometry(bonsai.core.tool.Geometry):
|
|||||||
# its centroid not obscured (tested via raycasting) by any other
|
# its centroid not obscured (tested via raycasting) by any other
|
||||||
# face.
|
# face.
|
||||||
distance = max(obj.dimensions.xyz)
|
distance = max(obj.dimensions.xyz)
|
||||||
|
min_y, max_z = None, None
|
||||||
if axis == "+Z":
|
if axis == "+Z":
|
||||||
max_z = max([co[2] for co in obj.bound_box]) + 0.002
|
max_z = max([co[2] for co in obj.bound_box]) + 0.002
|
||||||
direction = Vector((0, 0, -1))
|
direction = Vector((0, 0, -1))
|
||||||
@@ -771,8 +772,10 @@ class Geometry(bonsai.core.tool.Geometry):
|
|||||||
if direction.dot(face.normal) > 0:
|
if direction.dot(face.normal) > 0:
|
||||||
continue
|
continue
|
||||||
if axis == "+Z":
|
if axis == "+Z":
|
||||||
|
assert max_z is not None
|
||||||
face_centroid_at_max = Vector((*face.calc_center_median().xy, max_z))
|
face_centroid_at_max = Vector((*face.calc_center_median().xy, max_z))
|
||||||
elif axis == "-Y":
|
elif axis == "-Y":
|
||||||
|
assert min_y is not None
|
||||||
centroid = face.calc_center_median()
|
centroid = face.calc_center_median()
|
||||||
face_centroid_at_max = Vector((centroid.x, min_y, centroid.z))
|
face_centroid_at_max = Vector((centroid.x, min_y, centroid.z))
|
||||||
face_centroid_at_max = obj.matrix_world @ face_centroid_at_max
|
face_centroid_at_max = obj.matrix_world @ face_centroid_at_max
|
||||||
@@ -1885,6 +1888,7 @@ class Geometry(bonsai.core.tool.Geometry):
|
|||||||
"""NOTE: we assume that all items belonged to the same representation and to the same shape aspect"""
|
"""NOTE: we assume that all items belonged to the same representation and to the same shape aspect"""
|
||||||
ifc_file = tool.Ifc.get()
|
ifc_file = tool.Ifc.get()
|
||||||
previous_shape_aspect = None
|
previous_shape_aspect = None
|
||||||
|
base_representation = None
|
||||||
for inverse in ifc_file.get_inverse(representation_items[0]):
|
for inverse in ifc_file.get_inverse(representation_items[0]):
|
||||||
if inverse.is_a("IfcShapeRepresentation"):
|
if inverse.is_a("IfcShapeRepresentation"):
|
||||||
if inverse.OfShapeAspect:
|
if inverse.OfShapeAspect:
|
||||||
@@ -1894,6 +1898,7 @@ class Geometry(bonsai.core.tool.Geometry):
|
|||||||
previous_shape_aspect = inverse.OfShapeAspect[0]
|
previous_shape_aspect = inverse.OfShapeAspect[0]
|
||||||
else:
|
else:
|
||||||
base_representation = inverse
|
base_representation = inverse
|
||||||
|
assert base_representation
|
||||||
|
|
||||||
# remove item from previous shape aspect
|
# remove item from previous shape aspect
|
||||||
if previous_shape_aspect:
|
if previous_shape_aspect:
|
||||||
@@ -2211,6 +2216,7 @@ class Geometry(bonsai.core.tool.Geometry):
|
|||||||
assert item
|
assert item
|
||||||
obj.data.clear_geometry()
|
obj.data.clear_geometry()
|
||||||
|
|
||||||
|
cartesian_point_offset = None
|
||||||
if item.is_a("IfcHalfSpaceSolid"):
|
if item.is_a("IfcHalfSpaceSolid"):
|
||||||
bm = bmesh.new()
|
bm = bmesh.new()
|
||||||
bmesh.ops.create_grid(bm, size=0.5)
|
bmesh.ops.create_grid(bm, size=0.5)
|
||||||
|
|||||||
@@ -1087,18 +1087,21 @@ class Loader(bonsai.core.tool.Loader):
|
|||||||
bm = bmesh.new()
|
bm = bmesh.new()
|
||||||
bm.from_mesh(mesh)
|
bm.from_mesh(mesh)
|
||||||
prev_co = None
|
prev_co = None
|
||||||
if usage.LayerSetDirection == "AXIS2":
|
layer_set_direction = usage.LayerSetDirection
|
||||||
|
if layer_set_direction == "AXIS2":
|
||||||
co = Vector((0.0, offset, 0.0))
|
co = Vector((0.0, offset, 0.0))
|
||||||
no = cls.get_extrusion_vector(element).normalized()
|
no = cls.get_extrusion_vector(element).normalized()
|
||||||
no = no.cross(Vector([1.0, 0.0, 0.0]))
|
no = no.cross(Vector([1.0, 0.0, 0.0]))
|
||||||
elif usage.LayerSetDirection == "AXIS3":
|
elif layer_set_direction == "AXIS3":
|
||||||
co = Vector((0.0, 0.0, offset))
|
co = Vector((0.0, 0.0, offset))
|
||||||
no = cls.get_extrusion_vector(element).normalized()
|
no = cls.get_extrusion_vector(element).normalized()
|
||||||
no = Vector([0.0, 0.0, 1.0])
|
no = Vector([0.0, 0.0, 1.0])
|
||||||
elif usage.LayerSetDirection == "AXIS1":
|
elif layer_set_direction == "AXIS1":
|
||||||
co = Vector((0.0, 0.0, offset))
|
co = Vector((0.0, 0.0, offset))
|
||||||
no = cls.get_extrusion_vector(element).normalized()
|
no = cls.get_extrusion_vector(element).normalized()
|
||||||
no = Vector([1.0, 0.0, 0.0])
|
no = Vector([1.0, 0.0, 0.0])
|
||||||
|
else:
|
||||||
|
assert False, layer_set_direction
|
||||||
no *= sense_factor
|
no *= sense_factor
|
||||||
# Cache this
|
# Cache this
|
||||||
body = ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Model", "Body", "MODEL_VIEW")
|
body = ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Model", "Body", "MODEL_VIEW")
|
||||||
@@ -1108,6 +1111,7 @@ class Loader(bonsai.core.tool.Loader):
|
|||||||
if style := tool.Ifc.get_entity(material):
|
if style := tool.Ifc.get_entity(material):
|
||||||
styles[style] = i
|
styles[style] = i
|
||||||
last_i = len(layer_set.MaterialLayers) - 1
|
last_i = len(layer_set.MaterialLayers) - 1
|
||||||
|
bisect_geom = None
|
||||||
for i, layer in enumerate(layer_set.MaterialLayers):
|
for i, layer in enumerate(layer_set.MaterialLayers):
|
||||||
if i != last_i:
|
if i != last_i:
|
||||||
prev_co = co.copy()
|
prev_co = co.copy()
|
||||||
@@ -1121,6 +1125,7 @@ class Loader(bonsai.core.tool.Loader):
|
|||||||
if (material_index := styles.get(style, None)) is None:
|
if (material_index := styles.get(style, None)) is None:
|
||||||
material_index = len(mesh.materials)
|
material_index = len(mesh.materials)
|
||||||
mesh.materials.append(tool.Ifc.get_object(style))
|
mesh.materials.append(tool.Ifc.get_object(style))
|
||||||
|
assert bisect_geom is not None
|
||||||
if i == last_i:
|
if i == last_i:
|
||||||
for face in bisect_geom["geom"]:
|
for face in bisect_geom["geom"]:
|
||||||
if isinstance(face, bmesh.types.BMFace):
|
if isinstance(face, bmesh.types.BMFace):
|
||||||
@@ -1286,6 +1291,7 @@ class Loader(bonsai.core.tool.Loader):
|
|||||||
polyline.material_index = material_index
|
polyline.material_index = material_index
|
||||||
return polyline
|
return polyline
|
||||||
|
|
||||||
|
item = None
|
||||||
for item_data, item_style in zip(rep_items, item_styles):
|
for item_data, item_style in zip(rep_items, item_styles):
|
||||||
item = item_data["item"]
|
item = item_data["item"]
|
||||||
|
|
||||||
@@ -1313,6 +1319,7 @@ class Loader(bonsai.core.tool.Loader):
|
|||||||
polyline.points.add(1)
|
polyline.points.add(1)
|
||||||
polyline.points[-1].co = native_data["matrix"] @ Vector(v2)
|
polyline.points[-1].co = native_data["matrix"] @ Vector(v2)
|
||||||
|
|
||||||
|
assert item is not None
|
||||||
curve.bevel_depth = unit_scale * item.Radius
|
curve.bevel_depth = unit_scale * item.Radius
|
||||||
thickness = None
|
thickness = None
|
||||||
if (inner_radius := item.InnerRadius) and (thickness := max(item.Radius - inner_radius, 0)):
|
if (inner_radius := item.InnerRadius) and (thickness := max(item.Radius - inner_radius, 0)):
|
||||||
|
|||||||
@@ -220,10 +220,12 @@ class Misc(bonsai.core.tool.Misc):
|
|||||||
related_objects.append((element, ifcopenshell.util.placement.get_storey_elevation(element)))
|
related_objects.append((element, ifcopenshell.util.placement.get_storey_elevation(element)))
|
||||||
related_objects = sorted(related_objects, key=lambda e: e[1])
|
related_objects = sorted(related_objects, key=lambda e: e[1])
|
||||||
storey_elevation = None
|
storey_elevation = None
|
||||||
|
i = None
|
||||||
for i, related_object in enumerate(related_objects):
|
for i, related_object in enumerate(related_objects):
|
||||||
if related_object[0] == storey:
|
if related_object[0] == storey:
|
||||||
storey_elevation = related_object[1]
|
storey_elevation = related_object[1]
|
||||||
break
|
break
|
||||||
|
assert i is not None
|
||||||
if i + total_storeys < len(related_objects):
|
if i + total_storeys < len(related_objects):
|
||||||
next_storey_elevation = related_objects[i + total_storeys][1]
|
next_storey_elevation = related_objects[i + total_storeys][1]
|
||||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||||
|
|||||||
@@ -641,6 +641,8 @@ del _edit_type_names
|
|||||||
# call sites can reference ``tool.Parametric.ROOF`` directly. Renaming a
|
# call sites can reference ``tool.Parametric.ROOF`` directly. Renaming a
|
||||||
# registry entry renames the constant; a typo at the call site surfaces as
|
# registry entry renames the constant; a typo at the call site surfaces as
|
||||||
# AttributeError at module load.
|
# AttributeError at module load.
|
||||||
|
_entry = None
|
||||||
for _entry in Parametric.EDIT_TYPES:
|
for _entry in Parametric.EDIT_TYPES:
|
||||||
setattr(Parametric, _entry.name.upper(), _entry)
|
setattr(Parametric, _entry.name.upper(), _entry)
|
||||||
|
assert _entry is not None
|
||||||
del _entry
|
del _entry
|
||||||
|
|||||||
@@ -168,6 +168,7 @@ class Polyline(bonsai.core.tool.Polyline):
|
|||||||
distance = (mouse_vector - last_point).length
|
distance = (mouse_vector - last_point).length
|
||||||
if distance < 0:
|
if distance < 0:
|
||||||
return
|
return
|
||||||
|
angle, orientation_angle, angle_round_threshold = None, None
|
||||||
if distance > 0:
|
if distance > 0:
|
||||||
angle = tool.Cad.angle_3_vectors(
|
angle = tool.Cad.angle_3_vectors(
|
||||||
second_to_last_point, last_point, mouse_vector, new_angle=None, degrees=True
|
second_to_last_point, last_point, mouse_vector, new_angle=None, degrees=True
|
||||||
@@ -188,6 +189,7 @@ class Polyline(bonsai.core.tool.Polyline):
|
|||||||
angle = 0
|
angle = 0
|
||||||
orientation_angle = 0
|
orientation_angle = 0
|
||||||
if input_ui:
|
if input_ui:
|
||||||
|
assert angle is not None and orientation_angle is not None and angle_round_threshold is not None
|
||||||
if should_round:
|
if should_round:
|
||||||
angle_snap = tool.Snap.get_angle_snap_value(context)
|
angle_snap = tool.Snap.get_angle_snap_value(context)
|
||||||
angle = angle_snap * round(angle / angle_snap) if distance < angle_round_threshold else angle
|
angle = angle_snap * round(angle / angle_snap) if distance < angle_round_threshold else angle
|
||||||
|
|||||||
@@ -370,18 +370,21 @@ class Project(bonsai.core.tool.Project):
|
|||||||
props = cls.get_project_props()
|
props = cls.get_project_props()
|
||||||
active_library_breadcrumb = props.get_active_library_breadcrumb()
|
active_library_breadcrumb = props.get_active_library_breadcrumb()
|
||||||
change_back = False
|
change_back = False
|
||||||
|
breadcrumb = None
|
||||||
if active_library_breadcrumb:
|
if active_library_breadcrumb:
|
||||||
name = active_library_breadcrumb.name
|
name = active_library_breadcrumb.name
|
||||||
breadcrumb_type = active_library_breadcrumb.breadcrumb_type
|
breadcrumb_type = active_library_breadcrumb.breadcrumb_type
|
||||||
library_id = active_library_breadcrumb.library_id
|
library_id = active_library_breadcrumb.library_id
|
||||||
|
breadcrumb = (name, breadcrumb_type, library_id)
|
||||||
change_back = True
|
change_back = True
|
||||||
|
|
||||||
bpy.ops.bim.rewind_library()
|
bpy.ops.bim.rewind_library()
|
||||||
if change_back:
|
if change_back:
|
||||||
|
assert breadcrumb
|
||||||
bpy.ops.bim.change_library_element(
|
bpy.ops.bim.change_library_element(
|
||||||
element_name=name,
|
element_name=breadcrumb[0],
|
||||||
breadcrumb_type=breadcrumb_type,
|
breadcrumb_type=breadcrumb[1],
|
||||||
library_id=library_id,
|
library_id=breadcrumb[2],
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|||||||
@@ -505,6 +505,8 @@ class Search(bonsai.core.tool.Search):
|
|||||||
(0.773, 0.922, 0.816),
|
(0.773, 0.922, 0.816),
|
||||||
(0.871, 0.957, 0.894),
|
(0.871, 0.957, 0.894),
|
||||||
]
|
]
|
||||||
|
else:
|
||||||
|
assert False, theme
|
||||||
|
|
||||||
if value < min_val:
|
if value < min_val:
|
||||||
value = min_val
|
value = min_val
|
||||||
@@ -574,8 +576,10 @@ class ImportFilterQueryTransformer(lark.Transformer):
|
|||||||
new = self.filter_groups.add()
|
new = self.filter_groups.add()
|
||||||
global_ids = []
|
global_ids = []
|
||||||
is_first_group = len(self.filter_groups) == 1
|
is_first_group = len(self.filter_groups) == 1
|
||||||
|
new2 = None
|
||||||
for filter_index, arg in enumerate(args):
|
for filter_index, arg in enumerate(args):
|
||||||
if arg["type"] == "instance" and global_ids:
|
if arg["type"] == "instance" and global_ids:
|
||||||
|
assert new2
|
||||||
if "bpy.data.texts" in new2.value:
|
if "bpy.data.texts" in new2.value:
|
||||||
data_name = new2.value.split("bpy.data.texts")[1][2:-2]
|
data_name = new2.value.split("bpy.data.texts")[1][2:-2]
|
||||||
bpy.data.texts[data_name].write("," + arg["value"])
|
bpy.data.texts[data_name].write("," + arg["value"])
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import re
|
|||||||
from collections.abc import Iterable
|
from collections.abc import Iterable
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from datetime import time as datetime_time
|
from datetime import time as datetime_time
|
||||||
from typing import TYPE_CHECKING, Any, Literal, Optional, Union
|
from typing import TYPE_CHECKING, Any, Literal, Optional, Union, assert_never
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
@@ -1127,7 +1127,8 @@ class Sequence(bonsai.core.tool.Sequence):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def load_default_animation_color_scheme(cls):
|
def load_default_animation_color_scheme(cls):
|
||||||
groups = {
|
GroupType = Literal["CREATION", "OPERATION", "MOVEMENT_TO", "DESTRUCTION", "MOVEMENT_FROM", "USERDEFINED"]
|
||||||
|
groups: dict[GroupType, dict[str, Any]] = {
|
||||||
"CREATION": {
|
"CREATION": {
|
||||||
"PredefinedType": ["CONSTRUCTION", "INSTALLATION"],
|
"PredefinedType": ["CONSTRUCTION", "INSTALLATION"],
|
||||||
"Color": (0.0, 1.0, 0.0),
|
"Color": (0.0, 1.0, 0.0),
|
||||||
@@ -1167,6 +1168,8 @@ class Sequence(bonsai.core.tool.Sequence):
|
|||||||
predefined_type_item2 = props.task_output_colors.add()
|
predefined_type_item2 = props.task_output_colors.add()
|
||||||
predefined_type_item2.name = predefined_type
|
predefined_type_item2.name = predefined_type
|
||||||
predefined_type_item2.color = data["Color"]
|
predefined_type_item2.color = data["Color"]
|
||||||
|
else:
|
||||||
|
assert_never(group)
|
||||||
# TO DO: consider cases where users confuses inputs and outputs
|
# TO DO: consider cases where users confuses inputs and outputs
|
||||||
predefined_type_item.name = predefined_type
|
predefined_type_item.name = predefined_type
|
||||||
predefined_type_item.color = data["Color"]
|
predefined_type_item.color = data["Color"]
|
||||||
|
|||||||
@@ -225,6 +225,7 @@ class Snap(bonsai.core.tool.Snap):
|
|||||||
# Get axis that are closer than the stick factor threshold
|
# Get axis that are closer than the stick factor threshold
|
||||||
elegible_axis = []
|
elegible_axis = []
|
||||||
|
|
||||||
|
axis = None
|
||||||
for axis in snap_axis:
|
for axis in snap_axis:
|
||||||
if not axis:
|
if not axis:
|
||||||
continue
|
continue
|
||||||
@@ -326,6 +327,7 @@ class Snap(bonsai.core.tool.Snap):
|
|||||||
detected_snaps: list[dict[str, Any]] = []
|
detected_snaps: list[dict[str, Any]] = []
|
||||||
|
|
||||||
def select_plane_method():
|
def select_plane_method():
|
||||||
|
plane_origin, plane_normal = None, None
|
||||||
if not last_polyline_point:
|
if not last_polyline_point:
|
||||||
plane_origin = Vector((0, 0, 0))
|
plane_origin = Vector((0, 0, 0))
|
||||||
plane_normal = Vector((0, 0, 1))
|
plane_normal = Vector((0, 0, 1))
|
||||||
@@ -357,6 +359,7 @@ class Snap(bonsai.core.tool.Snap):
|
|||||||
plane_origin = Vector((last_polyline_point.x, last_polyline_point.y, last_polyline_point.z))
|
plane_origin = Vector((last_polyline_point.x, last_polyline_point.y, last_polyline_point.z))
|
||||||
plane_normal = Vector((1, 0, 0))
|
plane_normal = Vector((1, 0, 0))
|
||||||
|
|
||||||
|
assert plane_origin and plane_normal
|
||||||
plane_normal = tool.Polyline.use_transform_orientations(plane_normal)
|
plane_normal = tool.Polyline.use_transform_orientations(plane_normal)
|
||||||
return plane_origin, plane_normal
|
return plane_origin, plane_normal
|
||||||
|
|
||||||
@@ -583,6 +586,7 @@ class Snap(bonsai.core.tool.Snap):
|
|||||||
|
|
||||||
snaps_by_group = filter_snapping_points_by_group(detected_snaps)
|
snaps_by_group = filter_snapping_points_by_group(detected_snaps)
|
||||||
edges = [] # Get edges to create edge-intersection snap
|
edges = [] # Get edges to create edge-intersection snap
|
||||||
|
axis_start, axis_end = ..., ...
|
||||||
for snapping_point in snaps_by_group:
|
for snapping_point in snaps_by_group:
|
||||||
if snapping_point["group"] in {"Polyline", "Measure", "Wireframe", "Object"}:
|
if snapping_point["group"] in {"Polyline", "Measure", "Wireframe", "Object"}:
|
||||||
if snapping_point["type"] == "Edge":
|
if snapping_point["type"] == "Edge":
|
||||||
@@ -607,6 +611,7 @@ class Snap(bonsai.core.tool.Snap):
|
|||||||
if point["type"] == "Axis":
|
if point["type"] == "Axis":
|
||||||
if ordered_snaps[0]["type"] not in {"Axis", "Plane"}:
|
if ordered_snaps[0]["type"] not in {"Axis", "Plane"}:
|
||||||
obj = ordered_snaps[0]["object"]
|
obj = ordered_snaps[0]["object"]
|
||||||
|
assert axis_start is not ... and axis_end is not ...
|
||||||
mixed_snap = cls.mix_snap_and_axis(ordered_snaps[0], axis_start, axis_end)
|
mixed_snap = cls.mix_snap_and_axis(ordered_snaps[0], axis_start, axis_end)
|
||||||
for mixed_point in mixed_snap:
|
for mixed_point in mixed_snap:
|
||||||
snap_point = {
|
snap_point = {
|
||||||
|
|||||||
@@ -304,12 +304,14 @@ class Spatial(bonsai.core.tool.Spatial):
|
|||||||
|
|
||||||
while True:
|
while True:
|
||||||
has_parent = None
|
has_parent = None
|
||||||
|
new_current_results = None
|
||||||
for key in current_results:
|
for key in current_results:
|
||||||
if flat_key.startswith(key):
|
if flat_key.startswith(key):
|
||||||
has_parent = True
|
has_parent = True
|
||||||
new_current_results = current_results[key]["children"]
|
new_current_results = current_results[key]["children"]
|
||||||
break
|
break
|
||||||
if has_parent:
|
if has_parent:
|
||||||
|
assert new_current_results is not None
|
||||||
current_results = new_current_results
|
current_results = new_current_results
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
@@ -978,19 +980,24 @@ class Spatial(bonsai.core.tool.Spatial):
|
|||||||
interiors_list = []
|
interiors_list = []
|
||||||
|
|
||||||
if union_geom.geom_type == "MultiPolygon":
|
if union_geom.geom_type == "MultiPolygon":
|
||||||
|
poly = None
|
||||||
for poly in union_geom.geoms:
|
for poly in union_geom.geoms:
|
||||||
interiors_list = cls.get_poly_valid_interior_list(
|
interiors_list = cls.get_poly_valid_interior_list(
|
||||||
poly=poly, min_area=min_area, interiors_list=interiors_list
|
poly=poly, min_area=min_area, interiors_list=interiors_list
|
||||||
)
|
)
|
||||||
|
|
||||||
|
assert poly
|
||||||
new_poly = Polygon(poly.exterior.coords, holes=interiors_list)
|
new_poly = Polygon(poly.exterior.coords, holes=interiors_list)
|
||||||
|
|
||||||
if union_geom.geom_type == "Polygon":
|
elif union_geom.geom_type == "Polygon":
|
||||||
interiors_list = cls.get_poly_valid_interior_list(
|
interiors_list = cls.get_poly_valid_interior_list(
|
||||||
poly=union_geom, min_area=min_area, interiors_list=interiors_list
|
poly=union_geom, min_area=min_area, interiors_list=interiors_list
|
||||||
)
|
)
|
||||||
new_poly = Polygon(union_geom.exterior.coords, holes=interiors_list)
|
new_poly = Polygon(union_geom.exterior.coords, holes=interiors_list)
|
||||||
|
|
||||||
|
else:
|
||||||
|
assert False, union_geom.geom_type
|
||||||
|
|
||||||
return new_poly
|
return new_poly
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|||||||
@@ -360,6 +360,10 @@ class Style(bonsai.core.tool.Style):
|
|||||||
material_output = tool.Blender.get_material_node(obj, "OUTPUT_MATERIAL", {"is_active_output": True})
|
material_output = tool.Blender.get_material_node(obj, "OUTPUT_MATERIAL", {"is_active_output": True})
|
||||||
surface_output = get_input_node(material_output, "Surface")
|
surface_output = get_input_node(material_output, "Surface")
|
||||||
|
|
||||||
|
# TODO: this variable is not really needed,
|
||||||
|
# just workaround a for ty issue detecting unresolved refs.
|
||||||
|
bsdf = None
|
||||||
|
|
||||||
if surface_output and surface_output.type == "MIX_SHADER":
|
if surface_output and surface_output.type == "MIX_SHADER":
|
||||||
mix_shader = surface_output
|
mix_shader = surface_output
|
||||||
if (
|
if (
|
||||||
@@ -388,6 +392,7 @@ class Style(bonsai.core.tool.Style):
|
|||||||
and (bsdf := get_input_node(surface_output, input_index=1, of_type="BSDF_PRINCIPLED"))
|
and (bsdf := get_input_node(surface_output, input_index=1, of_type="BSDF_PRINCIPLED"))
|
||||||
)
|
)
|
||||||
):
|
):
|
||||||
|
assert bsdf
|
||||||
report(f"Because of {BLUE}BSDF_PRINCIPLED{R} node reflectance method identified as {BLUE}PHYSICAL{R}")
|
report(f"Because of {BLUE}BSDF_PRINCIPLED{R} node reflectance method identified as {BLUE}PHYSICAL{R}")
|
||||||
attributes["ReflectanceMethod"] = "NOTDEFINED" if tool.Ifc.get_schema() != "IFC4X3" else "PHYSICAL"
|
attributes["ReflectanceMethod"] = "NOTDEFINED" if tool.Ifc.get_schema() != "IFC4X3" else "PHYSICAL"
|
||||||
|
|
||||||
|
|||||||
@@ -216,6 +216,7 @@ def update_translations_from_po(po_directory: Path, translations_module: Path):
|
|||||||
|
|
||||||
|
|
||||||
if BPY_IS_LOADED:
|
if BPY_IS_LOADED:
|
||||||
|
import bpy
|
||||||
|
|
||||||
class SetupTranslationUI(bpy.types.Operator):
|
class SetupTranslationUI(bpy.types.Operator):
|
||||||
bl_idname = "bim.setup_translation_ui"
|
bl_idname = "bim.setup_translation_ui"
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ class Generator:
|
|||||||
}
|
}
|
||||||
""".replace("{entity}", location.split("#")[-1]))
|
""".replace("{entity}", location.split("#")[-1]))
|
||||||
# filter parents for the brick entity
|
# filter parents for the brick entity
|
||||||
|
parent = None
|
||||||
for row in query:
|
for row in query:
|
||||||
parent = row.get("parent").toPython()
|
parent = row.get("parent").toPython()
|
||||||
if "brickschema.org" in parent and parent in references.keys():
|
if "brickschema.org" in parent and parent in references.keys():
|
||||||
|
|||||||
@@ -1036,6 +1036,7 @@ class LibraryGenerator:
|
|||||||
seat_width_offset = 0.7 * width / 2 if cistern_depth else width / 2
|
seat_width_offset = 0.7 * width / 2 if cistern_depth else width / 2
|
||||||
seat_start_width_offset = 0.6 * width
|
seat_start_width_offset = 0.6 * width
|
||||||
|
|
||||||
|
cistern_3d = None
|
||||||
if cistern_height:
|
if cistern_height:
|
||||||
cistern = builder.rectangle(size=V(width, cistern_depth), position=shift_to_center)
|
cistern = builder.rectangle(size=V(width, cistern_depth), position=shift_to_center)
|
||||||
cistern_3d = ifcopenshell.util.element.copy_deep(self.file, cistern)
|
cistern_3d = ifcopenshell.util.element.copy_deep(self.file, cistern)
|
||||||
@@ -1118,6 +1119,7 @@ class LibraryGenerator:
|
|||||||
|
|
||||||
# cistern
|
# cistern
|
||||||
if cistern_height:
|
if cistern_height:
|
||||||
|
assert cistern_3d
|
||||||
cistern_3d = builder.extrude(
|
cistern_3d = builder.extrude(
|
||||||
cistern_3d, cistern_height + seat_level / 2, position=V(0, 0, seat_level / 2)
|
cistern_3d, cistern_height + seat_level / 2, position=V(0, 0, seat_level / 2)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -143,6 +143,7 @@ class LibraryGenerator:
|
|||||||
if "unused" in ifc_params:
|
if "unused" in ifc_params:
|
||||||
del ifc_params["unused"]
|
del ifc_params["unused"]
|
||||||
|
|
||||||
|
profiles_gap = ...
|
||||||
if prof_type == "profile_hollow*_square":
|
if prof_type == "profile_hollow*_square":
|
||||||
ifc_params["YDim"] = ifc_params["XDim"]
|
ifc_params["YDim"] = ifc_params["XDim"]
|
||||||
elif ifc_profile_name == "IfcCircleHollowProfileDef":
|
elif ifc_profile_name == "IfcCircleHollowProfileDef":
|
||||||
@@ -160,6 +161,7 @@ class LibraryGenerator:
|
|||||||
profile = self.file.create_entity(ifc_profile_name, ProfileName=prof_name, ProfileType="AREA", **ifc_params)
|
profile = self.file.create_entity(ifc_profile_name, ProfileName=prof_name, ProfileType="AREA", **ifc_params)
|
||||||
|
|
||||||
if prof_type == "profile_l*lbeam_2l":
|
if prof_type == "profile_l*lbeam_2l":
|
||||||
|
assert profiles_gap is not ...
|
||||||
profile.ProfileName = None # to avoid name confusion
|
profile.ProfileName = None # to avoid name confusion
|
||||||
mode = "SLBB" if prof_name.endswith("_SLBB") else "LLBB"
|
mode = "SLBB" if prof_name.endswith("_SLBB") else "LLBB"
|
||||||
profile = self.create_double_l_profile(profile, prof_name, profiles_gap, mode)
|
profile = self.create_double_l_profile(profile, prof_name, profiles_gap, mode)
|
||||||
|
|||||||
@@ -27,10 +27,12 @@ flatten = itertools.chain.from_iterable
|
|||||||
|
|
||||||
def get_element_data(model, name, element):
|
def get_element_data(model, name, element):
|
||||||
if element["geometry_type"] == "Edge":
|
if element["geometry_type"] == "Edge":
|
||||||
|
cell_tags, cell_block = None, None
|
||||||
for i, cell_block in enumerate(model.cells):
|
for i, cell_block in enumerate(model.cells):
|
||||||
if cell_block.type == "line":
|
if cell_block.type == "line":
|
||||||
cell_tags = model.cell_data["cell_tags"][i]
|
cell_tags = model.cell_data["cell_tags"][i]
|
||||||
break
|
break
|
||||||
|
assert cell_tags is not None and cell_block is not None
|
||||||
rows = []
|
rows = []
|
||||||
for i_row, i in enumerate(cell_tags):
|
for i_row, i in enumerate(cell_tags):
|
||||||
if i == 0:
|
if i == 0:
|
||||||
@@ -59,6 +61,7 @@ def get_element_data(model, name, element):
|
|||||||
elif element["geometry_type"] == "Face":
|
elif element["geometry_type"] == "Face":
|
||||||
triangle_cell_tags = None
|
triangle_cell_tags = None
|
||||||
quad_cell_tags = None
|
quad_cell_tags = None
|
||||||
|
points, cell_block = None, None
|
||||||
for i, cell_block in enumerate(model.cells):
|
for i, cell_block in enumerate(model.cells):
|
||||||
if cell_block.type == "triangle":
|
if cell_block.type == "triangle":
|
||||||
triangle_cell_tags = model.cell_data["cell_tags"][i]
|
triangle_cell_tags = model.cell_data["cell_tags"][i]
|
||||||
@@ -78,8 +81,10 @@ def get_element_data(model, name, element):
|
|||||||
if not len(rows):
|
if not len(rows):
|
||||||
points = []
|
points = []
|
||||||
else:
|
else:
|
||||||
|
assert cell_block is not None
|
||||||
points = list(flatten([cell_block.data[c] for c in rows]))
|
points = list(flatten([cell_block.data[c] for c in rows]))
|
||||||
|
|
||||||
|
cell_block = None
|
||||||
for i, cell_block in enumerate(model.cells):
|
for i, cell_block in enumerate(model.cells):
|
||||||
if cell_block.type == "quad":
|
if cell_block.type == "quad":
|
||||||
quad_cell_tags = model.cell_data["cell_tags"][i]
|
quad_cell_tags = model.cell_data["cell_tags"][i]
|
||||||
@@ -97,6 +102,7 @@ def get_element_data(model, name, element):
|
|||||||
rows.append(i_row)
|
rows.append(i_row)
|
||||||
break
|
break
|
||||||
if len(rows):
|
if len(rows):
|
||||||
|
assert cell_block is not None and points is not None
|
||||||
points.extend(list(flatten([cell_block.data[c] for c in rows])))
|
points.extend(list(flatten([cell_block.data[c] for c in rows])))
|
||||||
|
|
||||||
points = list(set(points))
|
points = list(set(points))
|
||||||
@@ -172,6 +178,8 @@ def results_to_ifc(ifc_file, ifc_model, rmed_path, global_case, field_types, dat
|
|||||||
model_cases = data["load_cases"]
|
model_cases = data["load_cases"]
|
||||||
elif global_case == "COMB":
|
elif global_case == "COMB":
|
||||||
model_cases = data["load_combinations"]
|
model_cases = data["load_combinations"]
|
||||||
|
else:
|
||||||
|
assert False, global_case
|
||||||
for field in field_types:
|
for field in field_types:
|
||||||
if field == "InternalForces":
|
if field == "InternalForces":
|
||||||
_parsed_data = internal_forces_to_ifc(ifc_file, ifc_model, result, model_cases, data["elements"])
|
_parsed_data = internal_forces_to_ifc(ifc_file, ifc_model, result, model_cases, data["elements"])
|
||||||
|
|||||||
+19
-2
@@ -283,7 +283,7 @@ class Ifc2CA:
|
|||||||
geometry = [x.EdgeStart.VertexGeometry.Coordinates for x in repr_item.Bounds[0].Bound.EdgeList]
|
geometry = [x.EdgeStart.VertexGeometry.Coordinates for x in repr_item.Bounds[0].Bound.EdgeList]
|
||||||
|
|
||||||
else:
|
else:
|
||||||
print(representation)
|
assert False, representation
|
||||||
return geometry
|
return geometry
|
||||||
|
|
||||||
def parse_material(self, material: ios.entity_instance):
|
def parse_material(self, material: ios.entity_instance):
|
||||||
@@ -399,6 +399,9 @@ class Ifc2CA:
|
|||||||
elif element.is_a("IfcStructuralSurfaceMember"):
|
elif element.is_a("IfcStructuralSurfaceMember"):
|
||||||
placement = ifcopenshell.util.placement.get_axis2placement(repr_item.FaceSurface.Position)
|
placement = ifcopenshell.util.placement.get_axis2placement(repr_item.FaceSurface.Position)
|
||||||
|
|
||||||
|
else:
|
||||||
|
assert False, element
|
||||||
|
|
||||||
origin, orientation = self.parse_transformation_matrix(placement)
|
origin, orientation = self.parse_transformation_matrix(placement)
|
||||||
data["origin"] = origin
|
data["origin"] = origin
|
||||||
data["orientation"] = orientation
|
data["orientation"] = orientation
|
||||||
@@ -436,7 +439,7 @@ class Ifc2CA:
|
|||||||
for i, v in enumerate(placement[:3]):
|
for i, v in enumerate(placement[:3]):
|
||||||
v[3] = data["geometry"][i]
|
v[3] = data["geometry"][i]
|
||||||
|
|
||||||
if connection.is_a("IfcStructuralCurveConnection"):
|
elif connection.is_a("IfcStructuralCurveConnection"):
|
||||||
placement = ifcopenshell.util.placement.a2p(
|
placement = ifcopenshell.util.placement.a2p(
|
||||||
data["geometry"][0],
|
data["geometry"][0],
|
||||||
connection.Axis.DirectionRatios,
|
connection.Axis.DirectionRatios,
|
||||||
@@ -446,6 +449,9 @@ class Ifc2CA:
|
|||||||
elif connection.is_a("IfcStructuralSurfaceConnection"):
|
elif connection.is_a("IfcStructuralSurfaceConnection"):
|
||||||
placement = ifcopenshell.util.placement.get_axis2placement(repr_item.FaceSurface.Position)
|
placement = ifcopenshell.util.placement.get_axis2placement(repr_item.FaceSurface.Position)
|
||||||
|
|
||||||
|
else:
|
||||||
|
assert False, connection
|
||||||
|
|
||||||
origin, orientation = self.parse_transformation_matrix(placement)
|
origin, orientation = self.parse_transformation_matrix(placement)
|
||||||
data["origin"] = origin
|
data["origin"] = origin
|
||||||
data["orientation"] = orientation
|
data["orientation"] = orientation
|
||||||
@@ -552,6 +558,9 @@ class Ifc2CA:
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
else:
|
||||||
|
assert False, element["geometry_type"]
|
||||||
|
|
||||||
for action in actions:
|
for action in actions:
|
||||||
self.add_action_loads(element, action, data, load_cases)
|
self.add_action_loads(element, action, data, load_cases)
|
||||||
|
|
||||||
@@ -586,6 +595,7 @@ class Ifc2CA:
|
|||||||
|
|
||||||
data["actions"].append(action.get_info() | {"AppliedLoad": action.AppliedLoad.get_info()})
|
data["actions"].append(action.get_info() | {"AppliedLoad": action.AppliedLoad.get_info()})
|
||||||
if element["geometry_type"] in ["Vertex", "Edge"]:
|
if element["geometry_type"] in ["Vertex", "Edge"]:
|
||||||
|
force_projection_coeff, moment_projection_coeff = None, None
|
||||||
if action.is_a("IfcStructuralPointAction") and load.is_a("IfcStructuralLoadSingleForce"):
|
if action.is_a("IfcStructuralPointAction") and load.is_a("IfcStructuralLoadSingleForce"):
|
||||||
FX = tempFX = load.ForceX if load.ForceX is not None else 0.0
|
FX = tempFX = load.ForceX if load.ForceX is not None else 0.0
|
||||||
FY = tempFY = load.ForceY if load.ForceY is not None else 0.0
|
FY = tempFY = load.ForceY if load.ForceY is not None else 0.0
|
||||||
@@ -639,8 +649,12 @@ class Ifc2CA:
|
|||||||
force_projection_coeff = 1.0
|
force_projection_coeff = 1.0
|
||||||
moment_projection_coeff = 1.0
|
moment_projection_coeff = 1.0
|
||||||
|
|
||||||
|
else:
|
||||||
|
assert False, action
|
||||||
|
|
||||||
for iLC, load_case in enumerate(load_cases):
|
for iLC, load_case in enumerate(load_cases):
|
||||||
if load_case.id() in active_load_case_ids:
|
if load_case.id() in active_load_case_ids:
|
||||||
|
assert force_projection_coeff is not None and moment_projection_coeff is not None
|
||||||
load_case_coeff = 1.0 if load_case.Coefficient is None else load_case.Coefficient
|
load_case_coeff = 1.0 if load_case.Coefficient is None else load_case.Coefficient
|
||||||
data["loadGroups"].append(load_group.Name)
|
data["loadGroups"].append(load_group.Name)
|
||||||
data["loadsLC"]["FX"][iLC] += FX * load_group_coeff * load_case_coeff * force_projection_coeff
|
data["loadsLC"]["FX"][iLC] += FX * load_group_coeff * load_case_coeff * force_projection_coeff
|
||||||
@@ -672,6 +686,9 @@ class Ifc2CA:
|
|||||||
else:
|
else:
|
||||||
force_projection_coeff = 1.0
|
force_projection_coeff = 1.0
|
||||||
|
|
||||||
|
else:
|
||||||
|
assert False, action
|
||||||
|
|
||||||
for iLC, load_case in enumerate(load_cases):
|
for iLC, load_case in enumerate(load_cases):
|
||||||
if load_case.id() in active_load_case_ids:
|
if load_case.id() in active_load_case_ids:
|
||||||
load_case_coeff = 1.0 if load_case.Coefficient is None else load_case.Coefficient
|
load_case_coeff = 1.0 if load_case.Coefficient is None else load_case.Coefficient
|
||||||
|
|||||||
@@ -109,6 +109,8 @@ class ifc5D2json:
|
|||||||
values = root_element.CostValues
|
values = root_element.CostValues
|
||||||
elif root_element.is_a("IfcConstructionResource"):
|
elif root_element.is_a("IfcConstructionResource"):
|
||||||
values = root_element.BaseCosts
|
values = root_element.BaseCosts
|
||||||
|
else:
|
||||||
|
assert False, root_element
|
||||||
for cost_value in values or []:
|
for cost_value in values or []:
|
||||||
self.extract_cost_value(root_element, data, cost_value)
|
self.extract_cost_value(root_element, data, cost_value)
|
||||||
# data["CostValues"].append(cost_value.id())
|
# data["CostValues"].append(cost_value.id())
|
||||||
|
|||||||
@@ -478,6 +478,8 @@ class Ifc5DOdsWriter(Ifc5Dwriter):
|
|||||||
cell.addElement(P(text=value))
|
cell.addElement(P(text=value))
|
||||||
elif type == "formula":
|
elif type == "formula":
|
||||||
cell = TableCell(formula=value, stylename=style)
|
cell = TableCell(formula=value, stylename=style)
|
||||||
|
else:
|
||||||
|
assert False, type
|
||||||
row.addElement(cell)
|
row.addElement(cell)
|
||||||
|
|
||||||
def add_cost_item_rows(table, cost_data):
|
def add_cost_item_rows(table, cost_data):
|
||||||
@@ -715,6 +717,8 @@ if __name__ == "__main__":
|
|||||||
writer = Ifc5DOdsWriter(args["input"], args["output"])
|
writer = Ifc5DOdsWriter(args["input"], args["output"])
|
||||||
elif args["format"] == "XLSX":
|
elif args["format"] == "XLSX":
|
||||||
writer = Ifc5DXlsxWriter(args["input"], args["output"])
|
writer = Ifc5DXlsxWriter(args["input"], args["output"])
|
||||||
|
else:
|
||||||
|
assert False, args
|
||||||
writer.write()
|
writer.write()
|
||||||
|
|
||||||
logger.info("Finished conversion in %ss", time.time() - start)
|
logger.info("Finished conversion in %ss", time.time() - start)
|
||||||
|
|||||||
@@ -25,9 +25,10 @@ import re
|
|||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import TYPE_CHECKING, Any, Literal, Union
|
from typing import TYPE_CHECKING, Any, Literal, Union, cast
|
||||||
|
|
||||||
import ifcopenshell.util.selector
|
import ifcopenshell.util.selector
|
||||||
|
from typing_extensions import assert_never
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from openpyxl import Workbook
|
from openpyxl import Workbook
|
||||||
@@ -100,8 +101,9 @@ class Parser:
|
|||||||
def parse(self, ifc_file: ifcopenshell.file, name=None):
|
def parse(self, ifc_file: ifcopenshell.file, name=None):
|
||||||
for category_name, category_config in self.config["categories"].items():
|
for category_name, category_config in self.config["categories"].items():
|
||||||
for element in category_config["get_category_elements"](ifc_file):
|
for element in category_config["get_category_elements"](ifc_file):
|
||||||
get_element_data: Union[GetElementDataCallBack, dict[str, Any]]
|
get_element_data = cast(
|
||||||
get_element_data = category_config["get_element_data"]
|
Union[GetElementDataCallBack, dict[str, Any]], category_config["get_element_data"]
|
||||||
|
)
|
||||||
|
|
||||||
if isinstance(get_element_data, dict):
|
if isinstance(get_element_data, dict):
|
||||||
data = {}
|
data = {}
|
||||||
@@ -109,14 +111,18 @@ class Parser:
|
|||||||
data[key] = ifcopenshell.util.selector.get_element_value(element, query)
|
data[key] = ifcopenshell.util.selector.get_element_value(element, query)
|
||||||
elif isinstance(get_element_data, Callable):
|
elif isinstance(get_element_data, Callable):
|
||||||
data = get_element_data(ifc_file, element) or {}
|
data = get_element_data(ifc_file, element) or {}
|
||||||
|
else:
|
||||||
|
assert_never(get_element_data)
|
||||||
|
|
||||||
get_custom_element_data = self.get_custom_element_data.get(category_name, lambda x, y: None)
|
get_custom_element_data = self.get_custom_element_data.get(category_name, lambda *_: None)
|
||||||
if isinstance(get_custom_element_data, dict):
|
if isinstance(get_custom_element_data, dict):
|
||||||
custom_data = {}
|
custom_data = {}
|
||||||
for key, query in get_custom_element_data.items():
|
for key, query in get_custom_element_data.items():
|
||||||
custom_data[key] = ifcopenshell.util.selector.get_element_value(element, query)
|
custom_data[key] = ifcopenshell.util.selector.get_element_value(element, query)
|
||||||
elif isinstance(get_custom_element_data, Callable):
|
elif isinstance(get_custom_element_data, Callable):
|
||||||
custom_data = get_custom_element_data(ifc_file, element) or {}
|
custom_data = get_custom_element_data(ifc_file, element) or {}
|
||||||
|
else:
|
||||||
|
assert_never(get_custom_element_data)
|
||||||
|
|
||||||
data.update(custom_data)
|
data.update(custom_data)
|
||||||
|
|
||||||
|
|||||||
@@ -271,6 +271,8 @@ def get_contact_data(ifc_file: ifcopenshell.file, element: ifcopenshell.entity_i
|
|||||||
pao = the_actor
|
pao = the_actor
|
||||||
person = the_actor.ThePerson
|
person = the_actor.ThePerson
|
||||||
organization = the_actor.TheOrganization
|
organization = the_actor.TheOrganization
|
||||||
|
else:
|
||||||
|
assert False, the_actor
|
||||||
|
|
||||||
email = get_email_from_pao(person, organization)
|
email = get_email_from_pao(person, organization)
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ dependencies = [
|
|||||||
"openpyxl",
|
"openpyxl",
|
||||||
"odfpy",
|
"odfpy",
|
||||||
"pandas",
|
"pandas",
|
||||||
|
"typing-extensions",
|
||||||
]
|
]
|
||||||
|
|
||||||
[project.urls]
|
[project.urls]
|
||||||
|
|||||||
@@ -159,8 +159,10 @@ def _add_segment_to_curve(
|
|||||||
else:
|
else:
|
||||||
assert False
|
assert False
|
||||||
|
|
||||||
|
end_point = ...
|
||||||
for mapped_segment in mapped_segments:
|
for mapped_segment in mapped_segments:
|
||||||
if mapped_segment:
|
if mapped_segment:
|
||||||
end_point = _add_curve_segment_to_composite_curve(file, layout_segment, mapped_segment, curve)
|
end_point = _add_curve_segment_to_composite_curve(file, layout_segment, mapped_segment, curve)
|
||||||
|
|
||||||
|
assert end_point is not ...
|
||||||
return end_point
|
return end_point
|
||||||
|
|||||||
@@ -308,5 +308,7 @@ def _get_segment_start_point_label(prev_segment: entity_instance, segment: entit
|
|||||||
label = _cant_callback(prev_segment, segment)
|
label = _cant_callback(prev_segment, segment)
|
||||||
else:
|
else:
|
||||||
label = _cant_label(prev_segment, segment)
|
label = _cant_label(prev_segment, segment)
|
||||||
|
else:
|
||||||
|
assert False, s.DesignParameters
|
||||||
|
|
||||||
return label
|
return label
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ def _create_layout(file: ifcopenshell.file, alignment: entity_instance, points:
|
|||||||
ifcopenshell.api.nest.assign_object(file, related_objects=alignment_layouts, relating_object=alignment)
|
ifcopenshell.api.nest.assign_object(file, related_objects=alignment_layouts, relating_object=alignment)
|
||||||
|
|
||||||
start_dist_along = 0.0
|
start_dist_along = 0.0
|
||||||
|
gradient = None
|
||||||
for p1, p2 in zip(points, points[1:]):
|
for p1, p2 in zip(points, points[1:]):
|
||||||
x1, y1, z1 = p1.Coordinates
|
x1, y1, z1 = p1.Coordinates
|
||||||
x2, y2, z2 = p2.Coordinates
|
x2, y2, z2 = p2.Coordinates
|
||||||
@@ -100,6 +101,7 @@ def _create_layout(file: ifcopenshell.file, alignment: entity_instance, points:
|
|||||||
ifcopenshell.api.nest.assign_object(file, related_objects=[hsegment], relating_object=alignment_layouts[0])
|
ifcopenshell.api.nest.assign_object(file, related_objects=[hsegment], relating_object=alignment_layouts[0])
|
||||||
|
|
||||||
if include_vertical:
|
if include_vertical:
|
||||||
|
assert gradient is not None
|
||||||
vsegment = file.createIfcAlignmentSegment(
|
vsegment = file.createIfcAlignmentSegment(
|
||||||
ifcopenshell.guid.new(),
|
ifcopenshell.guid.new(),
|
||||||
DesignParameters=file.createIfcAlignmentVerticalSegment(
|
DesignParameters=file.createIfcAlignmentVerticalSegment(
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ def create_from_csv(file: ifcopenshell.file, filepath: str) -> entity_instance:
|
|||||||
:param filepath: path the to CSV file
|
:param filepath: path the to CSV file
|
||||||
:return: IfcAlignment
|
:return: IfcAlignment
|
||||||
"""
|
"""
|
||||||
|
alignment = None
|
||||||
with open(filepath, newline="") as csvfile:
|
with open(filepath, newline="") as csvfile:
|
||||||
reader = csv.reader(csvfile)
|
reader = csv.reader(csvfile)
|
||||||
row_count = 0
|
row_count = 0
|
||||||
@@ -89,9 +90,14 @@ def create_from_csv(file: ifcopenshell.file, filepath: str) -> entity_instance:
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
# add all subsequent vertical alignments
|
# add all subsequent vertical alignments
|
||||||
|
assert alignment is not None
|
||||||
vertical_layout = ifcopenshell.api.alignment.add_vertical_layout(file, alignment)
|
vertical_layout = ifcopenshell.api.alignment.add_vertical_layout(file, alignment)
|
||||||
ifcopenshell.api.alignment.layout_vertical_alignment_by_pi_method(
|
ifcopenshell.api.alignment.layout_vertical_alignment_by_pi_method(
|
||||||
file, vertical_layout, coordinates, radii
|
file, vertical_layout, coordinates, radii
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if row_count == 0:
|
||||||
|
raise ValueError(f"CSV file '{filepath}' is empty; expected at least one row for the horizontal alignment.")
|
||||||
|
|
||||||
|
assert alignment is not None
|
||||||
return alignment
|
return alignment
|
||||||
|
|||||||
@@ -182,6 +182,8 @@ class Usecase:
|
|||||||
if not reference:
|
if not reference:
|
||||||
migrator = ifcopenshell.util.schema.Migrator()
|
migrator = ifcopenshell.util.schema.Migrator()
|
||||||
|
|
||||||
|
old_referenced_source = ...
|
||||||
|
existing_classification = None
|
||||||
if self.settings["is_lightweight"]:
|
if self.settings["is_lightweight"]:
|
||||||
old_referenced_source = self.settings["reference"].ReferencedSource
|
old_referenced_source = self.settings["reference"].ReferencedSource
|
||||||
self.settings["reference"].ReferencedSource = None
|
self.settings["reference"].ReferencedSource = None
|
||||||
@@ -194,6 +196,7 @@ class Usecase:
|
|||||||
reference = migrator.migrate(self.settings["reference"], self.file)
|
reference = migrator.migrate(self.settings["reference"], self.file)
|
||||||
|
|
||||||
if self.settings["is_lightweight"]:
|
if self.settings["is_lightweight"]:
|
||||||
|
assert old_referenced_source is not ...
|
||||||
reference.ReferencedSource = self.settings["classification"]
|
reference.ReferencedSource = self.settings["classification"]
|
||||||
self.settings["reference"].ReferencedSource = old_referenced_source
|
self.settings["reference"].ReferencedSource = old_referenced_source
|
||||||
elif existing_classification:
|
elif existing_classification:
|
||||||
|
|||||||
@@ -88,6 +88,8 @@ def bearing2dd(bearing: str) -> float:
|
|||||||
elif cY == "S" and cX == "W":
|
elif cY == "S" and cX == "W":
|
||||||
angle = 270.0
|
angle = 270.0
|
||||||
sign = -1.0
|
sign = -1.0
|
||||||
|
else:
|
||||||
|
assert False, (cY, cX)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
dms = ifcopenshell.util.geolocation.dms2dd(d, m, s, ms)
|
dms = ifcopenshell.util.geolocation.dms2dd(d, m, s, ms)
|
||||||
|
|||||||
@@ -116,6 +116,8 @@ def add_feature(
|
|||||||
return ifcopenshell.api.aggregate.assign_object(file, [feature], element)
|
return ifcopenshell.api.aggregate.assign_object(file, [feature], element)
|
||||||
rels = feature.AdheresToElement
|
rels = feature.AdheresToElement
|
||||||
ifc_class = "IfcRelAdheresToElement"
|
ifc_class = "IfcRelAdheresToElement"
|
||||||
|
else:
|
||||||
|
assert False, feature
|
||||||
|
|
||||||
if rels:
|
if rels:
|
||||||
if rels[0][4] == element:
|
if rels[0][4] == element:
|
||||||
|
|||||||
@@ -54,6 +54,8 @@ def remove_feature(file: ifcopenshell.file, feature: ifcopenshell.entity_instanc
|
|||||||
rels = []
|
rels = []
|
||||||
else:
|
else:
|
||||||
rels = feature.ProjectsElements
|
rels = feature.ProjectsElements
|
||||||
|
else:
|
||||||
|
assert False, feature
|
||||||
for rel in rels:
|
for rel in rels:
|
||||||
history = rel.OwnerHistory
|
history = rel.OwnerHistory
|
||||||
file.remove(rel)
|
file.remove(rel)
|
||||||
|
|||||||
@@ -436,6 +436,7 @@ class Usecase:
|
|||||||
|
|
||||||
def create_curve_bounded_planes(self, is_2d: bool = False) -> list[ifcopenshell.entity_instance]:
|
def create_curve_bounded_planes(self, is_2d: bool = False) -> list[ifcopenshell.entity_instance]:
|
||||||
items = []
|
items = []
|
||||||
|
points = None
|
||||||
if self.file.schema != "IFC2X3":
|
if self.file.schema != "IFC2X3":
|
||||||
points = self.create_cartesian_point_list_from_vertices(self.settings["geometry"].vertices, is_2d=False)
|
points = self.create_cartesian_point_list_from_vertices(self.settings["geometry"].vertices, is_2d=False)
|
||||||
for polygon in self.settings["geometry"].polygons:
|
for polygon in self.settings["geometry"].polygons:
|
||||||
@@ -443,6 +444,7 @@ class Usecase:
|
|||||||
if self.file.schema == "IFC2X3":
|
if self.file.schema == "IFC2X3":
|
||||||
curve = self.create_curve_from_polygon_ifc2x3(polygon, is_2d=False)
|
curve = self.create_curve_from_polygon_ifc2x3(polygon, is_2d=False)
|
||||||
else:
|
else:
|
||||||
|
assert points is not None
|
||||||
curve = self.create_curve_from_polygon(points, polygon, is_2d=False)
|
curve = self.create_curve_from_polygon(points, polygon, is_2d=False)
|
||||||
items.append(self.file.createIfcCurveBoundedPlane(BasisSurface=plane, OuterBoundary=curve))
|
items.append(self.file.createIfcCurveBoundedPlane(BasisSurface=plane, OuterBoundary=curve))
|
||||||
return items
|
return items
|
||||||
@@ -457,12 +459,14 @@ class Usecase:
|
|||||||
|
|
||||||
def create_annotation_fill_areas(self, is_2d: bool = False) -> list[ifcopenshell.entity_instance]:
|
def create_annotation_fill_areas(self, is_2d: bool = False) -> list[ifcopenshell.entity_instance]:
|
||||||
items = []
|
items = []
|
||||||
|
points = None
|
||||||
if self.file.schema != "IFC2X3":
|
if self.file.schema != "IFC2X3":
|
||||||
points = self.create_cartesian_point_list_from_vertices(self.settings["geometry"].vertices, is_2d=is_2d)
|
points = self.create_cartesian_point_list_from_vertices(self.settings["geometry"].vertices, is_2d=is_2d)
|
||||||
for polygon in self.settings["geometry"].polygons:
|
for polygon in self.settings["geometry"].polygons:
|
||||||
if self.file.schema == "IFC2X3":
|
if self.file.schema == "IFC2X3":
|
||||||
curve = self.create_curve_from_polygon_ifc2x3(polygon, is_2d=is_2d)
|
curve = self.create_curve_from_polygon_ifc2x3(polygon, is_2d=is_2d)
|
||||||
else:
|
else:
|
||||||
|
assert points is not None
|
||||||
curve = self.create_curve_from_polygon(points, polygon, is_2d=is_2d)
|
curve = self.create_curve_from_polygon(points, polygon, is_2d=is_2d)
|
||||||
items.append(self.file.createIfcAnnotationFillArea(OuterBoundary=curve))
|
items.append(self.file.createIfcAnnotationFillArea(OuterBoundary=curve))
|
||||||
return items
|
return items
|
||||||
@@ -813,17 +817,20 @@ class Usecase:
|
|||||||
|
|
||||||
def create_triangulated_face_set(self) -> ifcopenshell.entity_instance:
|
def create_triangulated_face_set(self) -> ifcopenshell.entity_instance:
|
||||||
ifc_raw_items = [None] * self.settings["total_items"]
|
ifc_raw_items = [None] * self.settings["total_items"]
|
||||||
|
ifc_raw_uv_items = None
|
||||||
if self.settings["should_generate_uvs"]:
|
if self.settings["should_generate_uvs"]:
|
||||||
ifc_raw_uv_items = [None] * self.settings["total_items"]
|
ifc_raw_uv_items = [None] * self.settings["total_items"]
|
||||||
for i, value in enumerate(ifc_raw_items):
|
for i, value in enumerate(ifc_raw_items):
|
||||||
ifc_raw_items[i] = []
|
ifc_raw_items[i] = []
|
||||||
if self.settings["should_generate_uvs"]:
|
if self.settings["should_generate_uvs"]:
|
||||||
|
assert ifc_raw_uv_items is not None
|
||||||
ifc_raw_uv_items[i] = []
|
ifc_raw_uv_items[i] = []
|
||||||
for polygon in self.settings["geometry"].polygons:
|
for polygon in self.settings["geometry"].polygons:
|
||||||
ifc_raw_items[polygon.material_index % self.settings["total_items"]].append(
|
ifc_raw_items[polygon.material_index % self.settings["total_items"]].append(
|
||||||
[v + 1 for v in polygon.vertices]
|
[v + 1 for v in polygon.vertices]
|
||||||
)
|
)
|
||||||
if self.settings["should_generate_uvs"]:
|
if self.settings["should_generate_uvs"]:
|
||||||
|
assert ifc_raw_uv_items is not None
|
||||||
ifc_raw_uv_items[polygon.material_index % self.settings["total_items"]].append(
|
ifc_raw_uv_items[polygon.material_index % self.settings["total_items"]].append(
|
||||||
[uv + 1 for uv in polygon.loop_indices]
|
[uv + 1 for uv in polygon.loop_indices]
|
||||||
)
|
)
|
||||||
@@ -831,6 +838,7 @@ class Usecase:
|
|||||||
coordinates = self.create_cartesian_point_list_from_vertices(self.settings["geometry"].vertices)
|
coordinates = self.create_cartesian_point_list_from_vertices(self.settings["geometry"].vertices)
|
||||||
|
|
||||||
if self.settings["should_generate_uvs"]:
|
if self.settings["should_generate_uvs"]:
|
||||||
|
assert ifc_raw_uv_items is not None
|
||||||
# Blender supports multiple UV layers. We don't. Too bad.
|
# Blender supports multiple UV layers. We don't. Too bad.
|
||||||
tex_coords = self.file.createIfcTextureVertexList(
|
tex_coords = self.file.createIfcTextureVertexList(
|
||||||
[tuple(x.uv) for x in self.settings["geometry"].uv_layers[0].data]
|
[tuple(x.uv) for x in self.settings["geometry"].uv_layers[0].data]
|
||||||
|
|||||||
@@ -50,6 +50,12 @@ def disconnect_path(
|
|||||||
for r in relating_element.ConnectedTo
|
for r in relating_element.ConnectedTo
|
||||||
if r.is_a("IfcRelConnectsPathElements") and r.RelatedElement == related_element
|
if r.is_a("IfcRelConnectsPathElements") and r.RelatedElement == related_element
|
||||||
]
|
]
|
||||||
|
else:
|
||||||
|
raise ValueError(
|
||||||
|
"Either provide `element` and `connection_type`, or provide `relating_element` and `related_element`. "
|
||||||
|
f"Got: element={element}, connection_type={connection_type}, "
|
||||||
|
f"relating_element={relating_element}, related_element={related_element}."
|
||||||
|
)
|
||||||
|
|
||||||
for connection in set(connections):
|
for connection in set(connections):
|
||||||
history = connection.OwnerHistory
|
history = connection.OwnerHistory
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ def edit_true_north(file: ifcopenshell.file, true_north: Optional[Union[tuple[fl
|
|||||||
# This unsets true north
|
# This unsets true north
|
||||||
ifcopenshell.api.georeference.edit_true_north(model, true_north=None)
|
ifcopenshell.api.georeference.edit_true_north(model, true_north=None)
|
||||||
"""
|
"""
|
||||||
|
x, y = None, None
|
||||||
if isinstance(true_north, (float, int)):
|
if isinstance(true_north, (float, int)):
|
||||||
x, y = ifcopenshell.util.geolocation.angle2yaxis(true_north)
|
x, y = ifcopenshell.util.geolocation.angle2yaxis(true_north)
|
||||||
elif true_north is not None:
|
elif true_north is not None:
|
||||||
@@ -73,4 +74,5 @@ def edit_true_north(file: ifcopenshell.file, true_north: Optional[Union[tuple[fl
|
|||||||
context.TrueNorth = file.create_entity("IfcDirection")
|
context.TrueNorth = file.create_entity("IfcDirection")
|
||||||
else:
|
else:
|
||||||
context.TrueNorth = file.create_entity("IfcDirection")
|
context.TrueNorth = file.create_entity("IfcDirection")
|
||||||
|
assert x is not None and y is not None
|
||||||
context.TrueNorth.DirectionRatios = (x, y)
|
context.TrueNorth.DirectionRatios = (x, y)
|
||||||
|
|||||||
@@ -90,6 +90,8 @@ def edit_wcs(
|
|||||||
point,
|
point,
|
||||||
file.createIfcDirection((xaxis_x, xaxis_y)),
|
file.createIfcDirection((xaxis_x, xaxis_y)),
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
|
assert False, context
|
||||||
context.WorldCoordinateSystem = placement
|
context.WorldCoordinateSystem = placement
|
||||||
if file.get_total_inverses(old_wcs) == 0:
|
if file.get_total_inverses(old_wcs) == 0:
|
||||||
ifcopenshell.util.element.remove_deep2(file, old_wcs)
|
ifcopenshell.util.element.remove_deep2(file, old_wcs)
|
||||||
|
|||||||
+2
@@ -61,6 +61,8 @@ def add_structural_boundary_condition(
|
|||||||
boundary_class = "IfcBoundaryEdgeCondition"
|
boundary_class = "IfcBoundaryEdgeCondition"
|
||||||
elif related_connection.is_a("IfcStructuralSurfaceConnection"):
|
elif related_connection.is_a("IfcStructuralSurfaceConnection"):
|
||||||
boundary_class = "IfcBoundaryFaceCondition"
|
boundary_class = "IfcBoundaryFaceCondition"
|
||||||
|
else:
|
||||||
|
assert False, related_connection
|
||||||
|
|
||||||
condition = file.create_entity(boundary_class, Name=name)
|
condition = file.create_entity(boundary_class, Name=name)
|
||||||
connection.AppliedCondition = condition
|
connection.AppliedCondition = condition
|
||||||
|
|||||||
@@ -126,6 +126,7 @@ class Usecase:
|
|||||||
use_style_assignment = self.file.schema == "IFC2X3" or self.settings["should_use_presentation_style_assignment"]
|
use_style_assignment = self.file.schema == "IFC2X3" or self.settings["should_use_presentation_style_assignment"]
|
||||||
replace_previous_same_type_style = self.settings["replace_previous_same_type_style"]
|
replace_previous_same_type_style = self.settings["replace_previous_same_type_style"]
|
||||||
|
|
||||||
|
style: ifcopenshell.entity_instance | None = None
|
||||||
for element in self.file.traverse(self.settings["shape_representation"]):
|
for element in self.file.traverse(self.settings["shape_representation"]):
|
||||||
if not element.is_a("IfcShapeModel"):
|
if not element.is_a("IfcShapeModel"):
|
||||||
continue
|
continue
|
||||||
@@ -137,6 +138,7 @@ class Usecase:
|
|||||||
if self.settings["styles"]:
|
if self.settings["styles"]:
|
||||||
# If there are more items than styles, fallback to using the last style
|
# If there are more items than styles, fallback to using the last style
|
||||||
style = self.settings["styles"].pop(0)
|
style = self.settings["styles"].pop(0)
|
||||||
|
assert style is not None
|
||||||
name = style.Name
|
name = style.Name
|
||||||
current_style_type = style.is_a()
|
current_style_type = style.is_a()
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,8 @@ def assign_system(
|
|||||||
# This duct is part of the system
|
# This duct is part of the system
|
||||||
ifcopenshell.api.system.assign_system(model, products=[duct], system=system)
|
ifcopenshell.api.system.assign_system(model, products=[duct], system=system)
|
||||||
"""
|
"""
|
||||||
if not all(ifcopenshell.util.system.is_assignable(failed_product := product, system) for product in products):
|
for product in products:
|
||||||
raise TypeError(f"You cannot assign an {failed_product.is_a()} to an {system.is_a()}")
|
if not ifcopenshell.util.system.is_assignable(product, system):
|
||||||
|
raise TypeError(f"You cannot assign an {product.is_a()} to an {system.is_a()}")
|
||||||
|
|
||||||
return ifcopenshell.api.group.assign_group(file, products=products, group=system)
|
return ifcopenshell.api.group.assign_group(file, products=products, group=system)
|
||||||
|
|||||||
@@ -144,6 +144,8 @@ class Usecase:
|
|||||||
elif unit_type == "volume":
|
elif unit_type == "volume":
|
||||||
dimensional_exponents = self.file.createIfcDimensionalExponents(3, 0, 0, 0, 0, 0, 0)
|
dimensional_exponents = self.file.createIfcDimensionalExponents(3, 0, 0, 0, 0, 0, 0)
|
||||||
name_prefix = "cubic"
|
name_prefix = "cubic"
|
||||||
|
else:
|
||||||
|
assert False, unit_type
|
||||||
|
|
||||||
si_unit = self.file.createIfcSIUnit(
|
si_unit = self.file.createIfcSIUnit(
|
||||||
None,
|
None,
|
||||||
@@ -159,6 +161,8 @@ class Usecase:
|
|||||||
name = "{}mile".format(name_prefix + " " if name_prefix else "")
|
name = "{}mile".format(name_prefix + " " if name_prefix else "")
|
||||||
elif data["raw"] == "THOU":
|
elif data["raw"] == "THOU":
|
||||||
name = "{}thou".format(name_prefix + " " if name_prefix else "")
|
name = "{}thou".format(name_prefix + " " if name_prefix else "")
|
||||||
|
else:
|
||||||
|
assert False, data
|
||||||
value_component = self.file.create_entity(
|
value_component = self.file.create_entity(
|
||||||
"IfcReal", **{"wrappedValue": ifcopenshell.util.unit.si_conversions[name]}
|
"IfcReal", **{"wrappedValue": ifcopenshell.util.unit.si_conversions[name]}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -296,6 +296,7 @@ def main(
|
|||||||
else:
|
else:
|
||||||
num_passes = 0
|
num_passes = 0
|
||||||
|
|
||||||
|
g2 = None
|
||||||
for iteration in range(num_passes + 1):
|
for iteration in range(num_passes + 1):
|
||||||
|
|
||||||
# initialize empty group, note that in the current approach only one
|
# initialize empty group, note that in the current approach only one
|
||||||
@@ -316,6 +317,7 @@ def main(
|
|||||||
plt.fill(numpy.array(x.boundary).T[0], numpy.array(x.boundary).T[1])
|
plt.fill(numpy.array(x.boundary).T[0], numpy.array(x.boundary).T[1])
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
semantics, pairs = None, None
|
||||||
if iteration != num_passes:
|
if iteration != num_passes:
|
||||||
pairs = svgfill_context.get_face_pairs()
|
pairs = svgfill_context.get_face_pairs()
|
||||||
semantics = [None] * (max(pairs) + 1)
|
semantics = [None] * (max(pairs) + 1)
|
||||||
@@ -377,6 +379,7 @@ def main(
|
|||||||
if inside_elements:
|
if inside_elements:
|
||||||
elements = None
|
elements = None
|
||||||
if iteration != num_passes:
|
if iteration != num_passes:
|
||||||
|
assert semantics is not None
|
||||||
semantics[pi] = (inside_elements[0], -1)
|
semantics[pi] = (inside_elements[0], -1)
|
||||||
else:
|
else:
|
||||||
elements = tree.select_ray(pythonize(a), pythonize(b - a))
|
elements = tree.select_ray(pythonize(a), pythonize(b - a))
|
||||||
@@ -409,6 +412,7 @@ def main(
|
|||||||
svg_fill = "rgb(%s)" % ", ".join(str(f * 255.0) for f in clr[0:3])
|
svg_fill = "rgb(%s)" % ", ".join(str(f * 255.0) for f in clr[0:3])
|
||||||
|
|
||||||
if iteration != num_passes:
|
if iteration != num_passes:
|
||||||
|
assert semantics is not None
|
||||||
semantics[pi] = elements[0]
|
semantics[pi] = elements[0]
|
||||||
else:
|
else:
|
||||||
svg_fill = "none"
|
svg_fill = "none"
|
||||||
@@ -418,6 +422,8 @@ def main(
|
|||||||
if iteration != num_passes:
|
if iteration != num_passes:
|
||||||
to_remove = []
|
to_remove = []
|
||||||
|
|
||||||
|
assert pairs is not None
|
||||||
|
assert semantics is not None
|
||||||
for he_idx in range(0, len(pairs), 2):
|
for he_idx in range(0, len(pairs), 2):
|
||||||
# @todo instead of ray_distance, better do (x.point - y.point).dot(x.normal)
|
# @todo instead of ray_distance, better do (x.point - y.point).dot(x.normal)
|
||||||
# to see if they're coplanar, because ray-distance will be different in case
|
# to see if they're coplanar, because ray-distance will be different in case
|
||||||
@@ -445,6 +451,7 @@ def main(
|
|||||||
|
|
||||||
# Swap the XML nodes from the files
|
# Swap the XML nodes from the files
|
||||||
# Remove the original hidden line node we still have in the serializer output
|
# Remove the original hidden line node we still have in the serializer output
|
||||||
|
assert g2 is not None
|
||||||
g1.removeChild(projection)
|
g1.removeChild(projection)
|
||||||
g2.setAttribute("class", "projection")
|
g2.setAttribute("class", "projection")
|
||||||
# Find the children of the projection node parent
|
# Find the children of the projection node parent
|
||||||
|
|||||||
@@ -192,7 +192,7 @@ class entity_instance:
|
|||||||
return
|
return
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def file(self):
|
def file(self) -> "ifcopenshell.file":
|
||||||
# ugh circular imports, name collisions
|
# ugh circular imports, name collisions
|
||||||
from . import file
|
from . import file
|
||||||
|
|
||||||
|
|||||||
@@ -734,6 +734,7 @@ class file:
|
|||||||
# Don't store these attributes as transactions
|
# Don't store these attributes as transactions
|
||||||
# as the creation it self is already stored with
|
# as the creation it self is already stored with
|
||||||
# it's arguments
|
# it's arguments
|
||||||
|
transaction = None
|
||||||
if attrs:
|
if attrs:
|
||||||
transaction = self.transaction
|
transaction = self.transaction
|
||||||
self.transaction = None
|
self.transaction = None
|
||||||
@@ -849,11 +850,13 @@ class file:
|
|||||||
:returns: An ifcopenshell.entity_instance
|
:returns: An ifcopenshell.entity_instance
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
max_id = None
|
||||||
if self.transaction:
|
if self.transaction:
|
||||||
max_id = self.wrapped_data.getMaxId()
|
max_id = self.wrapped_data.getMaxId()
|
||||||
inst.wrapped_data.this.disown()
|
inst.wrapped_data.this.disown()
|
||||||
result = entity_instance(self.wrapped_data.add(inst.wrapped_data, -1 if _id is None else _id), self)
|
result = entity_instance(self.wrapped_data.add(inst.wrapped_data, -1 if _id is None else _id), self)
|
||||||
if self.transaction:
|
if self.transaction:
|
||||||
|
assert max_id is not None
|
||||||
added_elements = [e for e in self.traverse(result) if e.id() > max_id]
|
added_elements = [e for e in self.traverse(result) if e.id() > max_id]
|
||||||
[self.transaction.store_create(e) for e in reversed(added_elements)]
|
[self.transaction.store_create(e) for e in reversed(added_elements)]
|
||||||
return result
|
return result
|
||||||
|
|||||||
@@ -326,10 +326,11 @@ class iterator(ifcopenshell_wrapper.Iterator):
|
|||||||
if include_or_exclude_type == {"entity_instance"}:
|
if include_or_exclude_type == {"entity_instance"}:
|
||||||
include_or_exclude = cast(set[entity_instance], include_or_exclude)
|
include_or_exclude = cast(set[entity_instance], include_or_exclude)
|
||||||
|
|
||||||
if not all((last_inst := inst).is_a("IfcProduct") for inst in include_or_exclude):
|
for inst in include_or_exclude:
|
||||||
raise ValueError(
|
if not inst.is_a("IfcProduct"):
|
||||||
f"include and exclude need to be an aggregate of IfcProduct. Violating element: '{last_inst}'."
|
raise ValueError(
|
||||||
)
|
f"include and exclude need to be an aggregate of IfcProduct. Violating element: '{inst}'."
|
||||||
|
)
|
||||||
|
|
||||||
initializer = ifcopenshell_wrapper.construct_iterator_with_include_exclude_id
|
initializer = ifcopenshell_wrapper.construct_iterator_with_include_exclude_id
|
||||||
|
|
||||||
|
|||||||
@@ -1070,7 +1070,10 @@ class file:
|
|||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
def getMaxId(self): ...
|
def getMaxId(self) -> int:
|
||||||
|
"""Get the highest instance id currently in use in the file."""
|
||||||
|
...
|
||||||
|
|
||||||
def get_total_inverses_by_id(self, instance_id: int) -> int: ...
|
def get_total_inverses_by_id(self, instance_id: int) -> int: ...
|
||||||
def getUnit(self, unit_type): ...
|
def getUnit(self, unit_type): ...
|
||||||
def get_inverse(self, e: entity_instance) -> tuple[entity_instance, ...]: ...
|
def get_inverse(self, e: entity_instance) -> tuple[entity_instance, ...]: ...
|
||||||
|
|||||||
@@ -112,6 +112,8 @@ def sum_child_root_elements(root_element: ifcopenshell.entity_instance, category
|
|||||||
values = new_child_root_element.CostValues
|
values = new_child_root_element.CostValues
|
||||||
elif root_element.is_a("IfcConstructionResource"):
|
elif root_element.is_a("IfcConstructionResource"):
|
||||||
values = child_root_element.BaseCosts
|
values = child_root_element.BaseCosts
|
||||||
|
else:
|
||||||
|
assert False, root_element
|
||||||
for child_cost_value in values or []:
|
for child_cost_value in values or []:
|
||||||
if category_filter and child_cost_value.Category != category_filter:
|
if category_filter and child_cost_value.Category != category_filter:
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -329,6 +329,8 @@ def get_quantity(
|
|||||||
data["properties"] = get_quantities(quantity.HasQuantities, verbose=verbose)
|
data["properties"] = get_quantities(quantity.HasQuantities, verbose=verbose)
|
||||||
del data["HasQuantities"]
|
del data["HasQuantities"]
|
||||||
result = data
|
result = data
|
||||||
|
else:
|
||||||
|
assert False, quantity
|
||||||
if verbose:
|
if verbose:
|
||||||
result = {"id": quantity.id(), "class": quantity.is_a(), "value": result}
|
result = {"id": quantity.id(), "class": quantity.is_a(), "value": result}
|
||||||
return result
|
return result
|
||||||
@@ -385,6 +387,7 @@ def get_property(
|
|||||||
if prop.Name != name:
|
if prop.Name != name:
|
||||||
continue
|
continue
|
||||||
is_single_value = False # For now we pass value type only for single values.
|
is_single_value = False # For now we pass value type only for single values.
|
||||||
|
result_type = None
|
||||||
if prop.is_a("IfcPropertySingleValue"):
|
if prop.is_a("IfcPropertySingleValue"):
|
||||||
# 2 IfcPropertySingleValue.NominalValue
|
# 2 IfcPropertySingleValue.NominalValue
|
||||||
result = v.wrappedValue if (v := prop[2]) else None
|
result = v.wrappedValue if (v := prop[2]) else None
|
||||||
@@ -407,6 +410,8 @@ def get_property(
|
|||||||
data["properties"] = get_properties(prop.HasProperties, verbose=verbose)
|
data["properties"] = get_properties(prop.HasProperties, verbose=verbose)
|
||||||
del data["HasProperties"]
|
del data["HasProperties"]
|
||||||
result = data
|
result = data
|
||||||
|
else:
|
||||||
|
assert False, prop
|
||||||
if verbose:
|
if verbose:
|
||||||
result = {"id": prop.id(), "class": prop.is_a(), "value": result}
|
result = {"id": prop.id(), "class": prop.is_a(), "value": result}
|
||||||
if is_single_value:
|
if is_single_value:
|
||||||
|
|||||||
@@ -260,6 +260,8 @@ def get_helmert_transformation_parameters(ifc_file: ifcopenshell.file) -> Option
|
|||||||
xaa = 1.0
|
xaa = 1.0
|
||||||
xao = 0.0
|
xao = 0.0
|
||||||
scale = factor_x = factor_y = factor_z = 1
|
scale = factor_x = factor_y = factor_z = 1
|
||||||
|
else:
|
||||||
|
assert False, conversion
|
||||||
|
|
||||||
if not xaa and not xao:
|
if not xaa and not xao:
|
||||||
xaa = 1.0
|
xaa = 1.0
|
||||||
|
|||||||
@@ -18,18 +18,16 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
try:
|
import importlib.util
|
||||||
from lark import Lark, Transformer
|
|
||||||
from lark.exceptions import UnexpectedCharacters, UnexpectedEOF, UnexpectedToken
|
|
||||||
|
|
||||||
LARK_AVAILABLE = True
|
|
||||||
except ImportError:
|
|
||||||
LARK_AVAILABLE = False
|
|
||||||
|
|
||||||
import re
|
import re
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
|
LARK_AVAILABLE = importlib.util.find_spec("lark") is not None
|
||||||
|
|
||||||
if LARK_AVAILABLE:
|
if LARK_AVAILABLE:
|
||||||
|
from lark import Lark, Transformer
|
||||||
|
from lark.exceptions import UnexpectedCharacters, UnexpectedEOF, UnexpectedToken
|
||||||
|
|
||||||
mvd_grammar = r"""
|
mvd_grammar = r"""
|
||||||
start: entry+
|
start: entry+
|
||||||
|
|
||||||
@@ -92,9 +90,9 @@ if LARK_AVAILABLE:
|
|||||||
self.store_text_attribute(args, "options")
|
self.store_text_attribute(args, "options")
|
||||||
|
|
||||||
def dynamic_option(self, args):
|
def dynamic_option(self, args):
|
||||||
|
original_keyword = str(args[0])
|
||||||
|
key = original_keyword.lower()
|
||||||
try:
|
try:
|
||||||
original_keyword = str(args[0])
|
|
||||||
key = original_keyword.lower()
|
|
||||||
raw_text = args[1].children[0].value
|
raw_text = args[1].children[0].value
|
||||||
parsed_value = parse_semicolon_separated_kv(raw_text)
|
parsed_value = parse_semicolon_separated_kv(raw_text)
|
||||||
self._dynamic[key] = (parsed_value, original_keyword)
|
self._dynamic[key] = (parsed_value, original_keyword)
|
||||||
|
|||||||
@@ -89,6 +89,9 @@ def get_axis2placement(placement: ifcopenshell.entity_instance) -> MatrixType:
|
|||||||
x = np.array((1, 0, 0))
|
x = np.array((1, 0, 0))
|
||||||
o = placement.Location.Coordinates
|
o = placement.Location.Coordinates
|
||||||
|
|
||||||
|
else:
|
||||||
|
assert False, placement
|
||||||
|
|
||||||
return a2p(o, z, x)
|
return a2p(o, z, x)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -584,6 +584,7 @@ class Migrator:
|
|||||||
# NOTE: `attribute` is an attribute in new file schema
|
# NOTE: `attribute` is an attribute in new file schema
|
||||||
# print("Migrating attribute", element, new_element, attribute.name())
|
# print("Migrating attribute", element, new_element, attribute.name())
|
||||||
old_file = element.wrapped_data.file
|
old_file = element.wrapped_data.file
|
||||||
|
value = ...
|
||||||
if hasattr(element, attribute.name()):
|
if hasattr(element, attribute.name()):
|
||||||
value = getattr(element, attribute.name())
|
value = getattr(element, attribute.name())
|
||||||
# print("Attribute names matched", value)
|
# print("Attribute names matched", value)
|
||||||
@@ -622,9 +623,7 @@ class Migrator:
|
|||||||
except: # We tried our best
|
except: # We tried our best
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
if value is ...:
|
||||||
value
|
|
||||||
except UnboundLocalError:
|
|
||||||
print(
|
print(
|
||||||
f"Couldn't match attribute {attribute.name()} by name to migrate from {element} "
|
f"Couldn't match attribute {attribute.name()} by name to migrate from {element} "
|
||||||
f"to {new_element} and there is no special mapping to handle migration "
|
f"to {new_element} and there is no special mapping to handle migration "
|
||||||
|
|||||||
@@ -1279,6 +1279,8 @@ class FacetTransformer(lark.Transformer):
|
|||||||
result = bool(value.match(element_value)) if element_value is not None else False
|
result = bool(value.match(element_value)) if element_value is not None else False
|
||||||
elif value in (None, True, False):
|
elif value in (None, True, False):
|
||||||
result = element_value is value
|
result = element_value is value
|
||||||
|
else:
|
||||||
|
assert False, value
|
||||||
|
|
||||||
if comparison.startswith("!"):
|
if comparison.startswith("!"):
|
||||||
return not result
|
return not result
|
||||||
|
|||||||
@@ -210,6 +210,8 @@ def np_rotation_matrix(
|
|||||||
matrix = np.array([[cos_theta, 0, sin_theta], [0, 1, 0], [-sin_theta, 0, cos_theta]])
|
matrix = np.array([[cos_theta, 0, sin_theta], [0, 1, 0], [-sin_theta, 0, cos_theta]])
|
||||||
elif axis == "Z":
|
elif axis == "Z":
|
||||||
matrix = np.array([[cos_theta, -sin_theta, 0], [sin_theta, cos_theta, 0], [0, 0, 1]])
|
matrix = np.array([[cos_theta, -sin_theta, 0], [sin_theta, cos_theta, 0], [0, 0, 1]])
|
||||||
|
else:
|
||||||
|
assert False, axis
|
||||||
else:
|
else:
|
||||||
# Assume axis is a vector.
|
# Assume axis is a vector.
|
||||||
axis = axis / np.linalg.norm(axis)
|
axis = axis / np.linalg.norm(axis)
|
||||||
|
|||||||
@@ -320,6 +320,7 @@ def log_internal_cpp_errors(
|
|||||||
|
|
||||||
if log_content is None:
|
if log_content is None:
|
||||||
log_content = ifcopenshell.get_log()
|
log_content = ifcopenshell.get_log()
|
||||||
|
lines = None
|
||||||
msgs = list(map(json.loads, filter(None, log_content.split("\n"))))
|
msgs = list(map(json.loads, filter(None, log_content.split("\n"))))
|
||||||
chr_offsets = [chr_offset_re.findall(m["message"]) for m in msgs]
|
chr_offsets = [chr_offset_re.findall(m["message"]) for m in msgs]
|
||||||
instance_messages = [for_instance_re.findall(m["message"]) for m in msgs]
|
instance_messages = [for_instance_re.findall(m["message"]) for m in msgs]
|
||||||
@@ -356,6 +357,7 @@ def log_internal_cpp_errors(
|
|||||||
except:
|
except:
|
||||||
inst = None
|
inst = None
|
||||||
else:
|
else:
|
||||||
|
assert lines is not None
|
||||||
inst = next(
|
inst = next(
|
||||||
(
|
(
|
||||||
l.decode("ascii", errors="ignore").strip()
|
l.decode("ascii", errors="ignore").strip()
|
||||||
@@ -691,14 +693,16 @@ def validate_ifc_header(
|
|||||||
if not value:
|
if not value:
|
||||||
log_error(header_entity, name, index, AGGREGATE_TYPE, "EMPTY LIST")
|
log_error(header_entity, name, index, AGGREGATE_TYPE, "EMPTY LIST")
|
||||||
return
|
return
|
||||||
if not all(isinstance(last_value := v, str) for v in value):
|
for v in value:
|
||||||
log_error(
|
if not isinstance(v, str):
|
||||||
header_entity,
|
log_error(
|
||||||
name,
|
header_entity,
|
||||||
index,
|
name,
|
||||||
AGGREGATE_TYPE,
|
index,
|
||||||
f"LIST with {type(last_value).__name__} (value: {last_value})",
|
AGGREGATE_TYPE,
|
||||||
)
|
f"LIST with {type(v).__name__} (value: {v})",
|
||||||
|
)
|
||||||
|
break
|
||||||
return
|
return
|
||||||
|
|
||||||
if not isinstance(value, str):
|
if not isinstance(value, str):
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ def test_file_gc(args):
|
|||||||
inst = f.createIfcPerson()
|
inst = f.createIfcPerson()
|
||||||
elif api in (1, 2):
|
elif api in (1, 2):
|
||||||
inst = f.createIfcSite()
|
inst = f.createIfcSite()
|
||||||
|
else:
|
||||||
|
assert False, api
|
||||||
|
|
||||||
r = weakref.ref(f)
|
r = weakref.ref(f)
|
||||||
|
|
||||||
@@ -68,9 +70,9 @@ def test_file_gc(args):
|
|||||||
assert r()
|
assert r()
|
||||||
|
|
||||||
if not file_first:
|
if not file_first:
|
||||||
del f
|
del f # ty: ignore[possibly-unresolved-reference]
|
||||||
else:
|
else:
|
||||||
del inst
|
del inst # ty: ignore[possibly-unresolved-reference]
|
||||||
|
|
||||||
# With both deleted we should have no longer access to the file.
|
# With both deleted we should have no longer access to the file.
|
||||||
assert r() is None
|
assert r() is None
|
||||||
|
|||||||
@@ -71,11 +71,13 @@ class Patcher:
|
|||||||
|
|
||||||
# Sort elements by GlobalId to ensure consistent order
|
# Sort elements by GlobalId to ensure consistent order
|
||||||
elements_sorted = sorted(elements, key=lambda x: x.GlobalId)
|
elements_sorted = sorted(elements, key=lambda x: x.GlobalId)
|
||||||
|
element_quantities = None
|
||||||
for element in elements_sorted:
|
for element in elements_sorted:
|
||||||
quantities = self.get_element_quantities(element)
|
quantities = self.get_element_quantities(element)
|
||||||
if quantities:
|
if quantities:
|
||||||
element_quantities = quantities
|
element_quantities = quantities
|
||||||
break
|
break
|
||||||
|
assert element_quantities is not None
|
||||||
|
|
||||||
if not element_quantities:
|
if not element_quantities:
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -521,7 +521,7 @@ class Patcher(ifcpatch.BasePatcher):
|
|||||||
data_type = "JSON"
|
data_type = "JSON"
|
||||||
json_attrs.append(i)
|
json_attrs.append(i)
|
||||||
else:
|
else:
|
||||||
print("Possibly not implemented attribute data type:", attribute, primitive)
|
assert False, f"{attribute}, {primitive}"
|
||||||
if not self.is_strict or derived[i]:
|
if not self.is_strict or derived[i]:
|
||||||
optional = "DEFAULT NULL"
|
optional = "DEFAULT NULL"
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -142,6 +142,8 @@ class Facet:
|
|||||||
templates = [
|
templates = [
|
||||||
t.replace("shall", "may").replace("Shall", "May").replace("must", "may") for t in templates
|
t.replace("shall", "may").replace("Shall", "May").replace("must", "may") for t in templates
|
||||||
]
|
]
|
||||||
|
else:
|
||||||
|
assert False, clause_type
|
||||||
|
|
||||||
for template in templates:
|
for template in templates:
|
||||||
total_variables = len(template) - len(template.replace("{", ""))
|
total_variables = len(template) - len(template.replace("{", ""))
|
||||||
@@ -242,6 +244,7 @@ class Entity(Facet):
|
|||||||
elif not is_pass:
|
elif not is_pass:
|
||||||
reason = {"type": "NAME", "actual": inst.is_a().upper()}
|
reason = {"type": "NAME", "actual": inst.is_a().upper()}
|
||||||
|
|
||||||
|
predefined_type = None
|
||||||
if is_pass and self.predefinedType:
|
if is_pass and self.predefinedType:
|
||||||
if self.predefinedType == "USERDEFINED":
|
if self.predefinedType == "USERDEFINED":
|
||||||
is_pass = ifcopenshell.util.element.is_userdefined_type(inst)
|
is_pass = ifcopenshell.util.element.is_userdefined_type(inst)
|
||||||
@@ -616,6 +619,8 @@ class PartOf(Facet):
|
|||||||
if predefined_type != self.predefinedType:
|
if predefined_type != self.predefinedType:
|
||||||
is_pass = False
|
is_pass = False
|
||||||
reason = {"type": "PREDEFINEDTYPE", "actual": predefined_type}
|
reason = {"type": "PREDEFINEDTYPE", "actual": predefined_type}
|
||||||
|
else:
|
||||||
|
assert False, self.relation
|
||||||
|
|
||||||
if self.cardinality == "prohibited":
|
if self.cardinality == "prohibited":
|
||||||
return PartOfResult(not is_pass, {"type": "PROHIBITED"})
|
return PartOfResult(not is_pass, {"type": "PROHIBITED"})
|
||||||
@@ -800,11 +805,13 @@ class Property(Facet):
|
|||||||
]
|
]
|
||||||
elif prop_entity.is_a("IfcPropertyBoundedValue"):
|
elif prop_entity.is_a("IfcPropertyBoundedValue"):
|
||||||
values = []
|
values = []
|
||||||
|
data_type = None
|
||||||
for attribute in ["UpperBoundValue", "LowerBoundValue", "SetPointValue"]:
|
for attribute in ["UpperBoundValue", "LowerBoundValue", "SetPointValue"]:
|
||||||
value = getattr(prop_entity, attribute)
|
value = getattr(prop_entity, attribute)
|
||||||
if value is not None:
|
if value is not None:
|
||||||
data_type = value.is_a()
|
data_type = value.is_a()
|
||||||
values.append(value.wrappedValue)
|
values.append(value.wrappedValue)
|
||||||
|
assert data_type is not None, prop_entity
|
||||||
if self.dataType and data_type.lower() != self.dataType.lower():
|
if self.dataType and data_type.lower() != self.dataType.lower():
|
||||||
is_pass = False
|
is_pass = False
|
||||||
reason = {"type": "DATATYPE", "actual": data_type, "dataType": self.dataType}
|
reason = {"type": "DATATYPE", "actual": data_type, "dataType": self.dataType}
|
||||||
@@ -825,6 +832,7 @@ class Property(Facet):
|
|||||||
elif prop_entity.is_a("IfcPropertyTableValue"):
|
elif prop_entity.is_a("IfcPropertyTableValue"):
|
||||||
values = []
|
values = []
|
||||||
units = ifcopenshell.util.unit.get_property_table_unit(prop_entity, inst.wrapped_data.file)
|
units = ifcopenshell.util.unit.get_property_table_unit(prop_entity, inst.wrapped_data.file)
|
||||||
|
data_type = None
|
||||||
for attribute in ["Defining", "Defined"]:
|
for attribute in ["Defining", "Defined"]:
|
||||||
column_values = props[pset_name][prop_entity.Name][f"{attribute}Values"]
|
column_values = props[pset_name][prop_entity.Name][f"{attribute}Values"]
|
||||||
if not column_values:
|
if not column_values:
|
||||||
@@ -847,6 +855,7 @@ class Property(Facet):
|
|||||||
values.extend(column_values)
|
values.extend(column_values)
|
||||||
if not values:
|
if not values:
|
||||||
is_pass = False
|
is_pass = False
|
||||||
|
assert data_type is not None, prop_entity
|
||||||
reason = {"type": "DATATYPE", "actual": data_type, "dataType": self.dataType}
|
reason = {"type": "DATATYPE", "actual": data_type, "dataType": self.dataType}
|
||||||
break
|
break
|
||||||
props[pset_name][prop_entity.Name] = values
|
props[pset_name][prop_entity.Name] = values
|
||||||
@@ -984,6 +993,8 @@ class Material(Facet):
|
|||||||
values.update(
|
values.update(
|
||||||
[item.Name, item.Category, item.Material.Name, getattr(item.Material, "Category", None)]
|
[item.Name, item.Category, item.Material.Name, getattr(item.Material, "Category", None)]
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
|
assert False, material
|
||||||
|
|
||||||
is_pass = False
|
is_pass = False
|
||||||
for value in values:
|
for value in values:
|
||||||
|
|||||||
@@ -343,6 +343,8 @@ class Json(Reporter):
|
|||||||
elif requirement.value:
|
elif requirement.value:
|
||||||
label = "Reference"
|
label = "Reference"
|
||||||
value = requirement.value
|
value = requirement.value
|
||||||
|
else:
|
||||||
|
assert False, requirement
|
||||||
elif facet_type == "PartOf":
|
elif facet_type == "PartOf":
|
||||||
label = requirement.relation
|
label = requirement.relation
|
||||||
if requirement.predefinedType:
|
if requirement.predefinedType:
|
||||||
@@ -357,6 +359,8 @@ class Json(Reporter):
|
|||||||
label = "Name / Category"
|
label = "Name / Category"
|
||||||
if requirement.value:
|
if requirement.value:
|
||||||
value = requirement.value
|
value = requirement.value
|
||||||
|
else:
|
||||||
|
assert False, facet_type
|
||||||
requirements.append(
|
requirements.append(
|
||||||
ResultsRequirement(
|
ResultsRequirement(
|
||||||
facet_type=facet_type,
|
facet_type=facet_type,
|
||||||
|
|||||||
Reference in New Issue
Block a user