4
$\begingroup$
  1. First, start with an arbitrary, closed, manifold prism like so in Figure 1 (i.e. an arbitrary shaped polygon extruded up in the Z direction)

enter image description here

  1. Define a cut line as accurately as possible (not by eyeballing) as shown in Figure 2

enter image description here

  1. Using that cut line, split the prism into two separate objects. Each object must still be manifold and closed. Notice how at the cut line, there are faces closing off the object (like the "fill" feature of Bisect).

enter image description here

How might I achieve this in the least amount, and simplest amount of steps?

I have considered the bisect tool, but it does not allow cutting along arbitrary cut lines - it only allows infinite plane cutting.

I have considered the knife tool, but it is not very accurate. The knife project tool solves the accuracy portion, but it does not fill in the gaps like the bisect feature. It is quite a few steps to select faces of each portion, separate into a new object (p), then manually fill in the gaps.

Thoughts?

$\endgroup$
2
  • $\begingroup$ Not an answer, but if you have to do this a lot, then cutting in the flat and using Solidify to extrude would seem more efficient? It might even be worth duping the top/bottom faces of existing prisms to start with $\endgroup$
    – Robin Betts
    Oct 26, 2021 at 9:42
  • $\begingroup$ Yeah unfortunately this geometry comes from an external source and is very common in the AEC industry where floor slabs are already modeled from external software. Good idea though! $\endgroup$
    – Dion Moult
    Oct 26, 2021 at 22:04

1 Answer 1

5
$\begingroup$

Assuming your cutter line is extruded along the Z axis, you can use it in a Boolean modifier and invert its normals to get one part or the other.

It requires duplicating the mesh beforehand, and it seems it works reliably only with the Exact method. It might also break with more complex or non-manifold geometry. It's pretty reliable but very slow on dense meshes.

Starting with this setup :

enter image description here

enter image description here

As a python script :

import bpy
import bmesh

cutter = bpy.context.active_object
cutter_mesh = cutter.data

col = cutter.users_collection[0]
bm = bmesh.new()
bm.from_mesh(cutter_mesh)
bm_flipped = bm.copy()
for f in bm_flipped.faces:
    f.normal_flip()

selected_objects = bpy.context.selected_objects

for i in range(2):
    for obj in selected_objects:
        if obj.type != "MESH" or obj == cutter:
            continue
        part = obj.copy()
        part.data = obj.data.copy()

        col.objects.link(part)

        mod = part.modifiers.new(type="BOOLEAN", name="Bool")
        mod.object = cutter
        bpy.ops.object.modifier_apply({"object": part}, modifier="Bool")
    bm_flipped.to_mesh(cutter_mesh)

bm.to_mesh(cutter_mesh)  # Restore previous normals
bm.free()  # Not mandatory, but it can help if the mesh is modified elsewhere in code

Or to modify the object mesh in place (might be more optimized ways) :

import bpy
import bmesh

cutter = bpy.context.active_object
cutter_mesh = cutter.data

col = cutter.users_collection[0]
bm = bmesh.new()
bm.from_mesh(cutter_mesh)
bm_flipped = bm.copy()
for f in bm_flipped.faces:
    f.normal_flip()

selected_objects = [o for o in bpy.context.selected_objects if o.type == "MESH" and o != cutter]
parts = []

for i in range(2):
    for obj in selected_objects:
        part = obj.copy()
        part.data = obj.data.copy()

        col.objects.link(part)
        mod = part.modifiers.new(type="BOOLEAN", name="Bool")
        mod.object = cutter
        bpy.ops.object.modifier_apply({"object": part}, modifier="Bool")
        
        parts.append(part)
    bm_flipped.to_mesh(cutter_mesh)

for i, obj in enumerate(selected_objects):
    mesh = obj.data
    offset = 0
    verts = []
    faces = []
    for j in range(2):
        part_mesh = parts[i * 2 + j].data
        verts.extend([v.co for v in part_mesh.vertices])
        for face in part_mesh.polygons:
            faces.append([idx + offset for idx in face.vertices])        
        offset = len(part_mesh.vertices)
        bpy.data.objects.remove(parts[i* 2 + j])
        bpy.data.meshes.remove(part_mesh)
    mesh.clear_geometry()
    mesh.from_pydata(verts, (), faces)
    mesh.update()


bm.to_mesh(cutter_mesh)
bm.free()
bm_flipped.free()
$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .