diff --git a/src/bonsai/bonsai/bim/import_ifc.py b/src/bonsai/bonsai/bim/import_ifc.py index d6758de903..267800469a 100644 --- a/src/bonsai/bonsai/bim/import_ifc.py +++ b/src/bonsai/bonsai/bim/import_ifc.py @@ -64,8 +64,8 @@ class MaterialCreator: mesh: Union[OBJECT_DATA_TYPE, None], shape_has_openings: bool, ) -> None: - if (((rep := getattr(element, "Representation", ...)) is not ... and not rep) or - ((rep := getattr(element, "RepresentationMaps", ...)) is not ... and not rep) + if ((rep := getattr(element, "Representation", ...)) is not ... and not rep) or ( + (rep := getattr(element, "RepresentationMaps", ...)) is not ... and not rep ): return diff --git a/src/bonsai/bonsai/tool/raycast.py b/src/bonsai/bonsai/tool/raycast.py index 70dae00438..23dde22ccc 100644 --- a/src/bonsai/bonsai/tool/raycast.py +++ b/src/bonsai/bonsai/tool/raycast.py @@ -76,11 +76,7 @@ class Raycast(bonsai.core.tool.Raycast): view_normal = rv3d.view_rotation @ mathutils.Vector((0.0, 0.0, -1.0)) obj_matrix = obj.matrix_world.copy() bbox = [obj_matrix @ Vector(v) for v in obj.bound_box] - bbox_edges = [ - (0,1),(1,2),(2,3),(3,0), - (4,5),(5,6),(6,7),(7,4), - (0,4),(1,5),(2,6),(3,7) - ] + bbox_edges = [(0, 1), (1, 2), (2, 3), (3, 0), (4, 5), (5, 6), (6, 7), (7, 4), (0, 4), (1, 5), (2, 6), (3, 7)] transposed_bbox: list[Vector] = [] bbox_2d: list[float] = [] @@ -116,7 +112,9 @@ class Raycast(bonsai.core.tool.Raycast): new_bbox = [x for x in new_bbox if x is not None] for edge in bbox_edges: if (transposed_bbox[edge[0]] is None) ^ (transposed_bbox[edge[1]] is None): - point, _ = cls.intersect_edge_region_border(context.region, context.space_data, rv3d, bbox[edge[0]], bbox[edge[1]]) + point, _ = cls.intersect_edge_region_border( + context.region, context.space_data, rv3d, bbox[edge[0]], bbox[edge[1]] + ) if point: new_bbox.append(point) if new_bbox: @@ -149,7 +147,7 @@ class Raycast(bonsai.core.tool.Raycast): z_near = -clip_start za = a_view.z zb = b_view.z - denom = (zb - za) + denom = zb - za if denom == 0.0: return None, None t = (z_near - za) / denom @@ -212,10 +210,7 @@ class Raycast(bonsai.core.tool.Raycast): final_t = initial_t else: found_world, found_2d, found_t = find_nearby_onscreen_point( - region, rv3d, - onscreen_vert, offscreen_vert, - t_on_ab, - max_iters=600, step=0.01 + region, rv3d, onscreen_vert, offscreen_vert, t_on_ab, max_iters=600, step=0.01 ) if found_world is None: if init_2d is None: @@ -405,7 +400,7 @@ class Raycast(bonsai.core.tool.Raycast): edge_verts[e] = (v1_2d, point) else: edge_verts[e] = (v1_2d, v2_2d) - + snap_threshold = 10.0 for i, point in enumerate(verts_2d): @@ -823,8 +818,8 @@ class Raycast(bonsai.core.tool.Raycast): hit = None for snap_obj in objs_to_raycast: - if (snap_obj.obj.type in {"EMPTY", "CURVE"} - or (hasattr(snap_obj.obj.data, "polygons") and len(snap_obj.obj.data.polygons) == 0) + if snap_obj.obj.type in {"EMPTY", "CURVE"} or ( + hasattr(snap_obj.obj.data, "polygons") and len(snap_obj.obj.data.polygons) == 0 ): # For wireframe objects we have to test all the snaps to see which is closer snap_points = tool.Raycast.ray_cast_by_proximity_2d(context, event, snap_obj) @@ -846,7 +841,6 @@ class Raycast(bonsai.core.tool.Raycast): hit = closest_wf_point["point"] face_index = None - else: # Solid objects hit_obj, hit, face_index = cls.cast_rays_to_single_object(context, event, snap_obj.obj) @@ -861,7 +855,6 @@ class Raycast(bonsai.core.tool.Raycast): "distance": 9, # High value so it has low priority } closest_snaps.append(snap_point) - # Here we test which is closer, including wireframe and solid objects if hit is not None: diff --git a/src/bonsai/bonsai/tool/snap.py b/src/bonsai/bonsai/tool/snap.py index f464779ea3..5755c02ba7 100644 --- a/src/bonsai/bonsai/tool/snap.py +++ b/src/bonsai/bonsai/tool/snap.py @@ -360,7 +360,6 @@ class Snap(bonsai.core.tool.Snap): plane_normal = tool.Polyline.use_transform_orientations(plane_normal) return plane_origin, plane_normal - # Polyline polyline_props = tool.Model.get_polyline_props() try: @@ -392,7 +391,9 @@ class Snap(bonsai.core.tool.Snap): closest_snaps = tool.Raycast.ray_cast_and_get_closest_to_camera_snaps(context, event, objs_to_raycast) detected_snaps.extend(closest_snaps) - xray_mode = (space.shading.type == "SOLID" and space.shading.show_xray) or (space.shading.type == "WIREFRAME" and space.shading.show_xray_wireframe) + xray_mode = (space.shading.type == "SOLID" and space.shading.show_xray) or ( + space.shading.type == "WIREFRAME" and space.shading.show_xray_wireframe + ) for snap_obj in objs_to_raycast: for snap in closest_snaps: @@ -405,15 +406,19 @@ class Snap(bonsai.core.tool.Snap): detected_snaps.append(point) else: # If it is a solid object that is closest to camera it ignores all the rest - if "is_closest_to_camera" in snap and snap["is_closest_to_camera"] and snap["group"] == "Object": - closest_snap = [snap] # discards objects that aren't the closest + if ( + "is_closest_to_camera" in snap + and snap["is_closest_to_camera"] + and snap["group"] == "Object" + ): + closest_snap = [snap] # discards objects that aren't the closest if "face_index" in snap and snap["face_index"] is not None: snap_points = tool.Raycast.ray_cast_by_proximity_2d(context, event, snap_obj) for point in snap_points: point["group"] = "Object" closest_snap.append(point) detected_snaps = closest_snap - + # snap to cut geometry (e.g. in plan view) if CutDecorator.installed: cut_snaps = [] diff --git a/src/bonsai/bonsai/tool/spatial.py b/src/bonsai/bonsai/tool/spatial.py index 7bbdb28904..11a41672bc 100644 --- a/src/bonsai/bonsai/tool/spatial.py +++ b/src/bonsai/bonsai/tool/spatial.py @@ -997,9 +997,7 @@ class Spatial(bonsai.core.tool.Spatial): return obj @classmethod - def set_obj_origin_to_polygon_center( - cls, obj: bpy.types.Object, poly: Polygon, polygon_is_si: bool = True - ) -> None: + def set_obj_origin_to_polygon_center(cls, obj: bpy.types.Object, poly: Polygon, polygon_is_si: bool = True) -> None: unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) centroid = poly.centroid if polygon_is_si: @@ -1007,7 +1005,6 @@ class Spatial(bonsai.core.tool.Spatial): else: obj.location = Vector((centroid.x * unit_scale, centroid.y * unit_scale, 0)) - @classmethod def get_2d_vertices_from_polygon( cls, @@ -1195,7 +1192,6 @@ class Spatial(bonsai.core.tool.Spatial): ) -> None: bonsai.core.type.assign_type(ifc, tool.Model, type, element=element, type=relating_type) - @classmethod def set_space_visibility(cls, is_visible: bool) -> None: if tool.Ifc.get().schema == "IFC2X3": diff --git a/src/ifcedit/tests/test_foreach.py b/src/ifcedit/tests/test_foreach.py index e51c3c03be..d464fedcf3 100644 --- a/src/ifcedit/tests/test_foreach.py +++ b/src/ifcedit/tests/test_foreach.py @@ -36,7 +36,9 @@ class TestRunForeach: def test_rename_single(self, model): items = self._items(model, "IfcWall") - result = run_foreach(model, "attribute", "edit_attributes", {"product": "{id}", "attributes": '{"Name": "R"}'}, items) + result = run_foreach( + model, "attribute", "edit_attributes", {"product": "{id}", "attributes": '{"Name": "R"}'}, items + ) assert result["ok"] is True assert result["count"] == 1 assert result["errors"] == [] @@ -44,7 +46,9 @@ class TestRunForeach: def test_rename_multiple(self, model): items = self._items(model, "IfcElement") - result = run_foreach(model, "attribute", "edit_attributes", {"product": "{id}", "attributes": '{"Name": "X"}'}, items) + result = run_foreach( + model, "attribute", "edit_attributes", {"product": "{id}", "attributes": '{"Name": "X"}'}, items + ) assert result["ok"] is True assert result["count"] == len(items) @@ -73,7 +77,9 @@ class TestRunForeach: {"id": wall_id, "type": "IfcWall", "name": "W"}, {"id": 999999, "type": "IfcWall", "name": "Bad"}, ] - result = run_foreach(model, "attribute", "edit_attributes", {"product": "{id}", "attributes": '{"Name": "Ok"}'}, items) + result = run_foreach( + model, "attribute", "edit_attributes", {"product": "{id}", "attributes": '{"Name": "Ok"}'}, items + ) assert result["ok"] is False assert result["count"] == 1 assert len(result["errors"]) == 1 diff --git a/src/ifcedit/tests/test_main.py b/src/ifcedit/tests/test_main.py index 63bf75a217..00585d5172 100644 --- a/src/ifcedit/tests/test_main.py +++ b/src/ifcedit/tests/test_main.py @@ -111,8 +111,13 @@ class TestForeachCommand: def test_foreach_rename(self, model, model_file): walls_json = self._select_json(model, "IfcWall") stdout, stderr, rc = run_ifcedit( - "foreach", model_file, "attribute.edit_attributes", - "--product", "{id}", "--attributes", '{"Name": "Renamed"}', + "foreach", + model_file, + "attribute.edit_attributes", + "--product", + "{id}", + "--attributes", + '{"Name": "Renamed"}', stdin=walls_json, ) assert rc == 0, f"stderr: {stderr}" @@ -129,8 +134,13 @@ class TestForeachCommand: items = json.loads(elements_json) assert len(items) >= 2 stdout, stderr, rc = run_ifcedit( - "foreach", model_file, "attribute.edit_attributes", - "--product", "{id}", "--attributes", '{"Name": "Bulk"}', + "foreach", + model_file, + "attribute.edit_attributes", + "--product", + "{id}", + "--attributes", + '{"Name": "Bulk"}', stdin=elements_json, ) assert rc == 0, f"stderr: {stderr}" @@ -140,8 +150,13 @@ class TestForeachCommand: def test_foreach_empty_list(self, model_file): stdout, stderr, rc = run_ifcedit( - "foreach", model_file, "attribute.edit_attributes", - "--product", "{id}", "--attributes", '{"Name": "X"}', + "foreach", + model_file, + "attribute.edit_attributes", + "--product", + "{id}", + "--attributes", + '{"Name": "X"}', stdin="[]", ) assert rc == 0 @@ -151,7 +166,11 @@ class TestForeachCommand: def test_foreach_invalid_json_stdin(self, model_file): stdout, stderr, rc = run_ifcedit( - "foreach", model_file, "root.remove_product", "--product", "{id}", + "foreach", + model_file, + "root.remove_product", + "--product", + "{id}", stdin="not json", ) assert rc != 0 @@ -159,7 +178,11 @@ class TestForeachCommand: def test_foreach_not_array_stdin(self, model_file): stdout, stderr, rc = run_ifcedit( - "foreach", model_file, "root.remove_product", "--product", "{id}", + "foreach", + model_file, + "root.remove_product", + "--product", + "{id}", stdin='{"id": 1}', ) assert rc != 0 @@ -167,12 +190,19 @@ class TestForeachCommand: def test_foreach_output_to_different_file(self, model, model_file, tmp_path): import os + output = str(tmp_path / "out.ifc") walls_json = self._select_json(model, "IfcWall") stdout, stderr, rc = run_ifcedit( - "foreach", model_file, "attribute.edit_attributes", - "-o", output, - "--product", "{id}", "--attributes", '{"Name": "OutFile"}', + "foreach", + model_file, + "attribute.edit_attributes", + "-o", + output, + "--product", + "{id}", + "--attributes", + '{"Name": "OutFile"}', stdin=walls_json, ) assert rc == 0, f"stderr: {stderr}" diff --git a/src/ifcmcp/ifcmcp/__main__.py b/src/ifcmcp/ifcmcp/__main__.py index 47419dc209..da3f1deab7 100644 --- a/src/ifcmcp/ifcmcp/__main__.py +++ b/src/ifcmcp/ifcmcp/__main__.py @@ -31,9 +31,9 @@ def main(): from mcp.server.fastmcp import FastMCP # noqa: F401 except ImportError: import sys + print( - "error: the 'mcp' package is required to run the server.\n" - "Install it with: pip install mcp", + "error: the 'mcp' package is required to run the server.\n" "Install it with: pip install mcp", file=sys.stderr, ) sys.exit(1) diff --git a/src/ifcopenshell-python/ifcopenshell/__init__.py b/src/ifcopenshell-python/ifcopenshell/__init__.py index 6398c98842..7d247c6fab 100644 --- a/src/ifcopenshell-python/ifcopenshell/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/__init__.py @@ -137,7 +137,11 @@ def open( def open(path: Union[os.PathLike, str], format: SupportedFormat = None, *, should_stream: Literal[True]) -> _stream: ... @overload def open( - path: Union[os.PathLike, str], format: SupportedFormat = None, *, should_stream: bool = False, readonly: bool = False + path: Union[os.PathLike, str], + format: SupportedFormat = None, + *, + should_stream: bool = False, + readonly: bool = False, ) -> Union[_file, sqlite, _stream]: ... def open( path: Union[os.PathLike, str], diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_topology_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_topology_representation.py index 14c4fa9dcc..e01284fd4f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_topology_representation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_topology_representation.py @@ -21,7 +21,6 @@ from typing import Optional import ifcopenshell - _ITEM_TYPE_TO_REP_TYPE = { "IfcVertex": "Vertex", "IfcVertexPoint": "Vertex", diff --git a/src/ifcopenshell-python/test/api/cost/test_edit_cost_value.py b/src/ifcopenshell-python/test/api/cost/test_edit_cost_value.py index c8935c8e9b..38b6775c08 100644 --- a/src/ifcopenshell-python/test/api/cost/test_edit_cost_value.py +++ b/src/ifcopenshell-python/test/api/cost/test_edit_cost_value.py @@ -62,9 +62,7 @@ class TestEditCostValue(test.bootstrap.IFC4): attributes={"UnitBasis": {"ValueComponent": 1.0, "UnitComponent": unit}}, ) assert value.UnitBasis is not None - ifcopenshell.api.cost.edit_cost_value( - self.file, cost_value=value, attributes={"UnitBasis": None} - ) + ifcopenshell.api.cost.edit_cost_value(self.file, cost_value=value, attributes={"UnitBasis": None}) assert value.UnitBasis is None diff --git a/src/ifcopenshell-python/test/api/georeference/test_add_georeferencing.py b/src/ifcopenshell-python/test/api/georeference/test_add_georeferencing.py index 44e45dec28..4eb0e4e609 100644 --- a/src/ifcopenshell-python/test/api/georeference/test_add_georeferencing.py +++ b/src/ifcopenshell-python/test/api/georeference/test_add_georeferencing.py @@ -64,10 +64,18 @@ class TestAddGeoreferencing(test.bootstrap.IFC4): def test_recovering_from_orphan_coordinate_operation(self): ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcProject") context = ifcopenshell.api.context.add_context(self.file, "Model") - self.file.create_entity("IfcMapConversion", SourceCRS=context, TargetCRS=self.file.create_entity("IfcProjectedCRS", Name="EPSG:1234")) + self.file.create_entity( + "IfcMapConversion", + SourceCRS=context, + TargetCRS=self.file.create_entity("IfcProjectedCRS", Name="EPSG:1234"), + ) ifcopenshell.api.georeference.remove_georeferencing(self.file) # Simulate orphan by re-adding just a conversion without CRS - self.file.create_entity("IfcMapConversion", SourceCRS=context, TargetCRS=self.file.create_entity("IfcProjectedCRS", Name="EPSG:1234")) + self.file.create_entity( + "IfcMapConversion", + SourceCRS=context, + TargetCRS=self.file.create_entity("IfcProjectedCRS", Name="EPSG:1234"), + ) self.file.remove(self.file.by_type("IfcProjectedCRS")[0]) assert len(self.file.by_type("IfcProjectedCRS")) == 0 assert len(self.file.by_type("IfcCoordinateOperation")) == 1 diff --git a/src/ifcopenshell-python/test/api/resource/test_remove_resource_quantity.py b/src/ifcopenshell-python/test/api/resource/test_remove_resource_quantity.py index c91e2f27c6..74182d1c69 100644 --- a/src/ifcopenshell-python/test/api/resource/test_remove_resource_quantity.py +++ b/src/ifcopenshell-python/test/api/resource/test_remove_resource_quantity.py @@ -24,9 +24,7 @@ class TestRemoveResourceQuantity(test.bootstrap.IFC4): def test_removing_a_resource_quantity(self): self.file.create_entity("IfcProject") resource = ifcopenshell.api.resource.add_resource(self.file, ifc_class="IfcLaborResource") - ifcopenshell.api.resource.add_resource_quantity( - self.file, resource=resource, ifc_class="IfcQuantityTime" - ) + ifcopenshell.api.resource.add_resource_quantity(self.file, resource=resource, ifc_class="IfcQuantityTime") assert resource.BaseQuantity is not None ifcopenshell.api.resource.remove_resource_quantity(self.file, resource=resource) assert resource.BaseQuantity is None diff --git a/src/ifcquery/ifcquery/relations.py b/src/ifcquery/ifcquery/relations.py index 334a3241a3..556ee0324d 100644 --- a/src/ifcquery/ifcquery/relations.py +++ b/src/ifcquery/ifcquery/relations.py @@ -167,7 +167,11 @@ def _collect_elements(data: Any, seen: set[int], result: list[dict[str, Any]]) - eid = data["id"] if eid not in seen: seen.add(eid) - result.append({"id": data["id"], "type": data.get("type"), "name": data.get("name")} if "name" in data else {"id": data["id"], "type": data.get("type")}) + result.append( + {"id": data["id"], "type": data.get("type"), "name": data.get("name")} + if "name" in data + else {"id": data["id"], "type": data.get("type")} + ) for v in data.values(): _collect_elements(v, seen, result) elif isinstance(data, list):