fix covering test and fix bug adding coverings in non-meters projects

Noticed that adding a covering in millimeters project didn't worked - the reason was that shapely was using tolerance in ifc project units when get_obj_base_points was returning points in si (blender always operates in meters internally).
I've changed get_obj_base_points to return data in project units and get_bmesh_from_polygon to convert it back to meters for Blender.
It's probably would be better to always operate in si until we need to save data to ifc but I'm not that familiar with that module to just change it everywhere.

Ping @maxfb87 just in case.
This commit is contained in:
Andrej730
2024-08-01 13:42:03 +05:00
parent 5ac84e2be7
commit cb80081473
2 changed files with 49 additions and 24 deletions
+18 -22
View File
@@ -490,13 +490,15 @@ class Spatial(blenderbim.core.tool.Spatial):
selected_objects = bpy.context.selected_objects
boundary_elements = cls.get_boundary_elements(selected_objects)
polys = cls.get_polygons(boundary_elements)
converted_tolerance = cls.get_converted_tolerance(tolerance=0.03)
converted_tolerance = cls.get_converted_tolerance(tolerance_si=0.03)
union = shapely.ops.unary_union(polys).buffer(
converted_tolerance,
cap_style=shapely.constructive.BufferCapStyle.flat,
join_style=shapely.constructive.BufferJoinStyle.mitre,
)
union = cls.get_purged_inner_holes_poly(union_geom=union, min_area=cls.get_converted_tolerance(tolerance=0.1))
union = cls.get_purged_inner_holes_poly(
union_geom=union, min_area=cls.get_converted_tolerance(tolerance_si=0.1)
)
return union
@classmethod
@@ -526,28 +528,20 @@ class Spatial(blenderbim.core.tool.Spatial):
@classmethod
def get_obj_base_points(cls, obj: bpy.types.Object) -> dict[str, tuple[float, float]]:
x_values = [(obj.matrix_world @ Vector(v)).x for v in obj.bound_box]
y_values = [(obj.matrix_world @ Vector(v)).y for v in obj.bound_box]
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
bbox_ws = [obj.matrix_world @ Vector(v) / si_conversion for v in obj.bound_box]
return {
"low_left": (x_values[0], y_values[0]),
"high_left": (x_values[3], y_values[3]),
"low_right": (x_values[4], y_values[4]),
"high_right": (x_values[7], y_values[7]),
"low_left": (bbox_ws[0].x, bbox_ws[0].y),
"high_left": (bbox_ws[3].x, bbox_ws[3].y),
"low_right": (bbox_ws[4].x, bbox_ws[4].y),
"high_right": (bbox_ws[7].x, bbox_ws[7].y),
}
@classmethod
def get_converted_tolerance(cls, tolerance: float) -> float:
def get_converted_tolerance(cls, tolerance_si: float) -> float:
model = tool.Ifc.get()
project_unit = ifcopenshell.util.unit.get_project_unit(model, "LENGTHUNIT")
prefix = getattr(project_unit, "Prefix", None)
return ifcopenshell.util.unit.convert(
value=tolerance,
from_prefix=None,
from_unit="METRE",
to_prefix=prefix,
to_unit=project_unit.Name,
)
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(model)
return tolerance_si / si_conversion
@classmethod
def get_purged_inner_holes_poly(cls, union_geom: Polygon, min_area: float) -> Polygon:
@@ -580,7 +574,7 @@ class Spatial(blenderbim.core.tool.Spatial):
@classmethod
def get_buffered_poly_from_linear_ring(cls, linear_ring: shapely.LinearRing) -> Polygon:
poly = Polygon(linear_ring)
converted_tolerance = cls.get_converted_tolerance(tolerance=0.03)
converted_tolerance = cls.get_converted_tolerance(tolerance_si=0.03)
poly = poly.buffer(
converted_tolerance,
single_sided=True,
@@ -597,8 +591,10 @@ class Spatial(blenderbim.core.tool.Spatial):
bm.edges.index_update()
mat_invert = mat.inverted()
new_verts = [bm.verts.new(mat_invert @ Vector([v[0], v[1], 0])) for v in poly.exterior.coords[0:-1]]
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
new_verts = [
bm.verts.new(mat_invert @ (Vector([v[0], v[1], 0]) * si_conversion)) for v in poly.exterior.coords[0:-1]
]
[bm.edges.new((new_verts[i], new_verts[i + 1])) for i in range(len(new_verts) - 1)]
bm.edges.new((new_verts[len(new_verts) - 1], new_verts[0]))
@@ -8,7 +8,36 @@ Scenario: Execute generate flooring coverings from walls
And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType"
And the variable "element_type" is "[e for e in {ifc}.by_type('IfcWallType') if e.Name == 'WAL100'][0].id()"
And I set "scene.BIMModelProperties.relating_type_id" to "{element_type}"
# 1st wall
And I press "bim.add_constr_type_instance"
And the object "IfcWall/Wall" is selected
When I press "bim.add_instance_flooring_coverings_from_walls"
Then nothing happens
And I press "bim.change_layer_length(length=3.6)"
# 2nd wall
And the cursor is at "3.6,0.1,3"
And I set "scene.BIMModelProperties.length" to "2.0"
And I press "bim.add_constr_type_instance"
# 3rd wall
And the cursor is at "3.5,2.1,3"
And I set "scene.BIMModelProperties.length" to "3.5"
And I press "bim.add_constr_type_instance"
# 4th wall
And the cursor is at "0,2.0,0"
And I set "scene.BIMModelProperties.length" to "1.9"
And I press "bim.add_constr_type_instance"
# add_instance_flooring_coverings_from_walls is expecting FLOORING predefined type.
And the object "IfcCoveringType/COV30" is selected
And I press "bim.enable_editing_attributes(obj='IfcCoveringType/COV30')"
And I set "active_object.BIMAttributeProperties.attributes[6].enum_value" to "FLOORING"
And I press "bim.edit_attributes(obj='IfcCoveringType/COV30')"
# Run the operator.
When the object "IfcWall/Wall" is selected
And additionally the object "IfcWall/Wall.001" is selected
And additionally the object "IfcWall/Wall.002" is selected
And additionally the object "IfcWall/Wall.003" is selected
And I set "scene.BIMModelProperties.ifc_class" to "IfcCoveringType"
And the variable "element_type" is "[e for e in {ifc}.by_type('IfcCoveringType') if e.Name == 'COV30'][0].id()"
And I set "scene.BIMModelProperties.relating_type_id" to "{element_type}"
And I press "bim.add_instance_flooring_coverings_from_walls"
Then the object "IfcCovering/Covering0" exists
And the object "IfcCovering/Covering0" is at "1.8,1.05,0.0"
And the object "IfcCovering/Covering0" dimensions are "3.4,1.9,0.03"