Get Volume and Area of Shapes in FreeCAD

~2 minutes read


Extract with a macro the volume and area values of shapes in FreeCAD.


To extract the values of all shapes from a tree structure, it is necessary to proceed as follows:

  1. Iterate though the tree view to parse all existing shapes, and doing so recursively.
  2. For each shape, get:
    • Label
    • Volume
    • Area
    • Bounding Box
    • Or add any other information.
  3. Add these values to the dictionary containing the info for that shape.
  4. Then, write the dictionary into a file in the same folder as the FreeCAD document.

The following code shows an implementation of the steps described above:

import FreeCAD
import json


original_stdout = sys.stdout
tree_objects = []
properties_objects = []


def iterate_document_tree(obj, level=0) -> None:
    """
    Recursively iterates through the FreeCAD document tree, starting from a given object.
    """
    tree_objects.append({"ObjectName": obj.Name, "Label": obj.Label})

    # Check if the object has children (e.g., a group or a body)
    if hasattr(obj, "Group") and obj.Group:
        for child_obj in obj.Group:
            iterate_document_tree(child_obj, level + 1)
    elif hasattr(obj, "Shape") and hasattr(obj.Shape, "ChildObjects") and obj.Shape.ChildObjects:
        for child_obj in obj.Shape.ChildObjects:
            iterate_document_tree(child_obj, level + 1)

def get_properties() -> None:
    for object in tree_objects:
        try:
            target_object = App.ActiveDocument.getObjectsByLabel(
                object["Label"])
            properties_objects.append({"part_number": object["Label"],
                                       "volume": f"{round(target_object[0].Shape.Volume, 2)} mm\u00b3",
                                       "area": f"{round(target_object[0].Shape.Area, 2)} mm\u00b2",
                                       "bbox_x": f"{round(target_object[0].Shape.BoundBox.XLength, 2)} mm",
                                       "bbox_y": f"{round(target_object[0].Shape.BoundBox.YLength, 2)} mm",
                                       "bbox_z": f"{round(target_object[0].Shape.BoundBox.ZLength, 2)} mm"})
        except Exception as err:
            print(f"Error on: {object["Label"]}: {err}")
        finally:
            pass


# Get the active document
doc = FreeCAD.ActiveDocument

if doc:
    print("Iterating through the FreeCAD document tree.")
    # Iterate through all top-level objects in the document
    for obj in doc.Objects:
        iterate_document_tree(obj)
    get_properties()

    # To save in the same folder as the parsed document.
    try:
        with open(doc.FileName.rsplit("/", 1)[0] + "/freecad-data.json", "w") as file:
            json.dump(properties_objects, file, indent=4)
            print("File saved successfully.")
    except Exception as err:
        print(err)
        print("File could not be saved.")
    finally:
        pass

else:
    print("No active FreeCAD document found.")

The exported file (which should be in the same folder that the file.FCStd) should return a list (depending on the design):

[
    {
        "part_number": "SU-A-0001L",
        "volume": "32148.47 mm\u00b3",
        "area": "25682.94 mm\u00b2",
        "bbox_x": "112.98 mm",
        "bbox_y": "42.6 mm",
        "bbox_z": "59.45 mm"
    },
    {
        "part_number": "SU-P-0001L",
        "volume": "8205.05 mm\u00b3",
        "area": "6962.71 mm\u00b2",
        "bbox_x": "61.48 mm",
        "bbox_y": "24.6 mm",
        "bbox_z": "50.8 mm"
    }
]

This can be added to FreeCAD as a macro. Be sure to save the CAD document first before starting the macro.

Note: This has not been tested on Windows. There might be an issue due to file path formats using backslash ( \ ) on Windows instead of forward slash ( / ) on Unix-like system.


Resources

Published

Category

CAD

Stay Connected