3
$\begingroup$

The 3D print toolbox currently only outputs the volume/area of all faces.

How can I get the volume of selected mesh objects, ignoring certain faces?


Asking on behalf of a user who mailed me.

$\endgroup$
2
$\begingroup$

This can be done using the Python API to get the mesh with modifiers applied, then applying the transform matrix.

The following script applies to all selected objects, showing a popup with the final volume. Not that it does not use units or scene scale.

See is_face_volume for the method of checking if a face should be included in volume calculations.

import bpy
from bpy import context

# Quick way to test with/without hidden faces
USE_FILTER_FACES = True


def report_info(title="", message=""):
    # show in console
    print(title, message)
    # show in popup
    def draw_cb(self, context):
        self.layout.label(text=message)
    context.window_manager.popup_menu(draw_func=draw_cb, title=title, icon='INFO')


def is_face_skip(f):
    """Ignore faces that pass this test!"""
    return f.hide is False
    # You may want to filter based on material.
    # return f.material_index == 0


def bmesh_from_object_final(ob):
    import bmesh
    matrix = ob.matrix_world
    me = ob.to_mesh(context.scene, apply_modifiers=True, settings='PREVIEW')
    me.transform(matrix)
    bm = bmesh.new()
    bm.from_mesh(me)
    bpy.data.meshes.remove(me)
    if USE_FILTER_FACES:
        faces_remove = [f for f in bm.faces if not is_face_skip(f)]
        for f in faces_remove:
            bm.faces.remove(f)
    return (bm, matrix.is_negative)


def volume_and_area_from_object(ob):
    bm, is_negative = bmesh_from_object_final(ob)
    volume = bm.calc_volume(signed=True)
    area = sum(f.calc_area() for f in bm.faces)
    bm.free()
    if is_negative:
        volume = -volume
    return volume, area


def main():
    volume = 0.0
    area = 0.0
    object_count = 0
    for ob in context.selected_objects:
        if ob.type != 'MESH':
            continue
        volume_single, area_single = volume_and_area_from_object(ob)
        volume += volume_single
        area += area_single
        object_count += 1
    volume = abs(volume)

    report_info(
        title="From %d object(s)" % object_count,
        message="Volume: %.4f, Area: %.4f" % (volume, area),
    )

if __name__ == "__main__":
    main()
$\endgroup$
  • $\begingroup$ The Object.to_mesh method only takes in the preserve_all_data_layers and depsgraph parameters on Blender 2.8 with the 3D Printing Toolkit addon installed. Am I missing something still? $\endgroup$ – Ziad Sep 30 at 9:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.