You can now split objects using a drawn edge, especially useful when creating slab pour breaks and fixing lazily modeled walls and columns. Thanks Gorgious56!

This commit is contained in:
Dion Moult
2021-10-27 12:06:12 +11:00
parent 04b3b9b60e
commit f69de3324a
12 changed files with 117 additions and 4 deletions
+3 -2
View File
@@ -243,11 +243,12 @@ endif
# Required by IFC4D
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/b0/bb/9c4dddd3ca173cb56241cfb2eddfae24690dc676d357ac4cab17d0a36d9d/PyP6Xer-1.13.0.tar.gz
cd dist/working && tar -xzvf xerparser*
cp -r dist/working/xerparser-1.13.0/xerparser dist/blenderbim/libs/site/packages/
cd dist/working && tar -xzvf PyP6Xer*
cp -r dist/working/PyP6Xer-1.13.0/xerparser dist/blenderbim/libs/site/packages/
rm -rf dist/working
# Required by xerparser and IFC4D
# TODO: remove this dependency. It's only used to show a progress bar.
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/9f/7b/76c4e5ef1a1b528fcaada4133f972e77d33c252831676cf414119ca6093d/tqdm-4.50.0.tar.gz
cd dist/working && tar -xzvf tqdm*
@@ -24,6 +24,7 @@ classes = (
operator.SetOverrideColour,
operator.SetViewportShadowFromSun,
operator.SnapSpacesTogether,
operator.SplitAlongEdge,
prop.BIMMiscProperties,
ui.BIM_PT_misc_utilities,
)
@@ -120,8 +120,21 @@ class ResizeToStorey(bpy.types.Operator, Operator):
@classmethod
def poll(cls, context):
return context.selected_objects
return context.selected_objects and tool.Ifc.get()
def _execute(self, context):
for obj in context.selected_objects:
core.resize_to_storey(tool.Misc, obj=obj)
class SplitAlongEdge(bpy.types.Operator, Operator):
bl_idname = "bim.split_along_edge"
bl_label = "Split Along Edge"
bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
return context.selected_objects and tool.Ifc.get()
def _execute(self, context):
core.split_along_edge(tool.Misc, cutter=context.active_object, objs=context.selected_objects)
@@ -40,3 +40,5 @@ class BIM_PT_misc_utilities(bpy.types.Panel):
row.operator("bim.snap_spaces_together")
row = layout.row()
row.operator("bim.resize_to_storey")
row = layout.row()
row.operator("bim.split_along_edge")
@@ -42,7 +42,7 @@ class BIM_PT_authoring(Panel):
else:
col.label(text="No IFC Class", icon="FILE_VOLUME")
enabled = False
if type_prop.getAvailableTypes(tprops, context):
if type_prop.get_relating_type(tprops, context):
col.prop(tprops, "relating_type", text="", icon="FILE_3D")
else:
col.label(text="No Relating Type", icon="FILE_3D")
+8
View File
@@ -28,3 +28,11 @@ def resize_to_storey(misc, obj=None):
misc.move_object_to_elevation(obj, misc.get_storey_elevation_in_si(storey))
misc.scale_object_to_height(obj, height)
misc.mark_object_as_edited(obj)
def split_along_edge(misc, cutter=None, objs=None):
new_objs = misc.split_objects_with_cutter(objs, cutter)
for obj in new_objs:
misc.run_root_copy_class(obj=obj)
for obj in objs:
misc.mark_object_as_edited(obj)
+2
View File
@@ -104,8 +104,10 @@ class Misc:
def get_storey_height_in_si(cls, storey): pass
def mark_object_as_edited(cls, obj): pass
def move_object_to_elevation(cls, obj, elevation): pass
def run_root_copy_class(cls, obj=None): pass
def scale_object_to_height(cls, obj, height): pass
def set_object_origin_to_bottom(cls, obj): pass
def split_objects_with_cutter(cls, objs, cutter): pass
@interface
+41
View File
@@ -17,8 +17,10 @@
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import bpy
import bmesh
import ifcopenshell
import blenderbim.core.tool
import blenderbim.core.root
import blenderbim.tool as tool
from mathutils import Vector, Matrix
from blenderbim.bim.ifc import IfcStore
@@ -76,6 +78,10 @@ class Misc(blenderbim.core.tool.Misc):
def move_object_to_elevation(cls, obj, elevation):
obj.matrix_world.translation[2] = elevation
@classmethod
def run_root_copy_class(cls, obj=None):
return blenderbim.core.root.copy_class(tool.Ifc, tool.Collector, tool.Geometry, tool.Root, obj=obj)
@classmethod
def scale_object_to_height(cls, obj, height):
absolute_bound_box = [obj.matrix_world @ Vector(c) for c in obj.bound_box]
@@ -91,3 +97,38 @@ class Misc(blenderbim.core.tool.Misc):
@classmethod
def mark_object_as_edited(cls, obj):
IfcStore.edited_objs.add(obj)
@classmethod
def split_objects_with_cutter(cls, objs, cutter):
cutter_mesh = cutter.data
bm = bmesh.new()
bm.from_mesh(cutter_mesh)
bm_flipped = bm.copy()
for f in bm_flipped.faces:
f.normal_flip()
new_objs = []
for obj in objs:
if obj.type != "MESH" or obj == cutter:
continue
new_obj = obj.copy()
new_obj.data = obj.data.copy()
obj.users_collection[0].objects.link(new_obj)
mod = new_obj.modifiers.new(type="BOOLEAN", name="Boolean")
mod.object = cutter
bpy.ops.object.modifier_apply({"object": new_obj}, modifier="Boolean")
bm_flipped.to_mesh(cutter_mesh)
mod = obj.modifiers.new(type="BOOLEAN", name="Boolean")
mod.object = cutter
bpy.ops.object.modifier_apply({"object": obj}, modifier="Boolean")
new_objs.append(new_obj)
bm.to_mesh(cutter_mesh)
bm.free()
return new_objs
@@ -35,3 +35,16 @@ Scenario: Resize to storey
And I press "bim.assign_container(structure={storey})"
When I press "bim.resize_to_storey"
Then nothing happens
Scenario: Split along edge
Given an empty IFC project
And I add a cube
And the object "Cube" is selected
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
And I press "bim.assign_class"
And I add a plane of size "4" at "0,0,0"
And the object "IfcWall/Cube" is selected
And additionally the object "Plane" is selected
When I press "bim.split_along_edge"
Then the object "IfcWall/Cube" is an "IfcWall"
And the object "IfcWall/Cube.001" is an "IfcWall"
+6
View File
@@ -105,6 +105,12 @@ def i_add_a_cube_of_size_size_at_location(size, location):
bpy.ops.mesh.primitive_cube_add(size=float(size), location=[float(co) for co in location.split(",")])
@given(parsers.parse('I add a plane of size "{size}" at "{location}"'))
@when(parsers.parse('I add a plane of size "{size}" at "{location}"'))
def i_add_a_plane_of_size_size_at_location(size, location):
bpy.ops.mesh.primitive_plane_add(size=float(size), location=[float(co) for co in location.split(",")])
@given(parsers.parse('I press "{operator}"'))
@when(parsers.parse('I press "{operator}"'))
def i_press_operator(operator):
+8
View File
@@ -39,3 +39,11 @@ class TestResizeToStorey:
misc.get_object_storey("obj").should_be_called().will_return("storey")
misc.get_storey_height_in_si("storey").should_be_called().will_return(None)
subject.resize_to_storey(misc, obj="obj")
class TestSplitAlongEdge:
def test_run(self, misc):
misc.split_objects_with_cutter(["obj"], "cutter").should_be_called().will_return(["new_obj"])
misc.run_root_copy_class(obj="new_obj").should_be_called()
misc.mark_object_as_edited("obj").should_be_called()
subject.split_along_edge(misc, cutter="cutter", objs=["obj"])
+18
View File
@@ -134,6 +134,11 @@ class TestMoveObjectToElevation(test.bim.bootstrap.NewFile):
assert list(obj.matrix_world.translation) == [0.0, 0.0, 3.0]
class TestRunRootCopyClass(test.bim.bootstrap.NewFile):
def test_nothing(self):
pass
class TestScaleObjectToHeight(test.bim.bootstrap.NewFile):
def test_run(self):
bpy.ops.mesh.primitive_cube_add()
@@ -149,3 +154,16 @@ class TestMarkObjectAsEdited(test.bim.bootstrap.NewFile):
obj = bpy.data.objects.new("Cube", None)
subject.mark_object_as_edited(obj)
assert obj in IfcStore.edited_objs
class TestSplitObjectsWithCutter(test.bim.bootstrap.NewFile):
def test_run(self):
bpy.ops.mesh.primitive_cube_add()
bpy.ops.mesh.primitive_plane_add(size=4)
obj = bpy.data.objects.get("Cube")
cutter = bpy.data.objects.get("Plane")
assert obj.dimensions[2] == 2
new_objs = subject.split_objects_with_cutter([obj], cutter)
assert new_objs[0].name == "Cube.001"
assert obj.dimensions[2] == 1
assert new_objs[0].dimensions[2] == 1