FreeCADで動くPythonスクリプト その5(板厚に応じたモデルの着色)

FreeCADでFEMワークベンチを使っていると,モデル上で板厚や材質を確認することが難しくて困ることがあります(; ・`д・´)
これは板厚や材質といった情報がAnalysisコンテナ内で保持されるため,ShapeやFaceと直接紐付けられていないということに起因します

そこで今回はFreeCADモデルを板厚に応じて着色するPythonスクリプトを説明します

前提条件として
 FreeCADのサーフェースモデルを対象とします
 板厚は‏ElementGeometry2DのShell plate thicknessの値を参照します
 Referencesを指定していないElementGeometry2DをDefault値として扱います
 Matplotlibのカラーマップを使用します (Choosing Colormaps in Matplotlib — Matplotlib 3.10.1 documentation

Pythonスクリプトを以下に示します

colormapping_thickness.py

# Color-mapping for surface-model according to plate's thickness

import FreeCAD, FreeCADGui
import re
import matplotlib.pyplot as plt

TYPEID_EG2D = "Fem::FeaturePython"  # TypeId of ElementGeometry2D
MIN_THICKNESS =  6                  # Min default value of thickness (mm)
MAX_THICKNESS = 30                  # Max default value of thickness (mm)
UNDEFINED_COLOR = (0, 0, 0)         # Black color as undefined thickness
NAME_CMAP = 'rainbow'               # https://matplotlib.org/stable/users/explain/colors/colormaps.html

def extract_number(s: str) -> float:
    """ Extract a numerical value from a string using regular expression """
    match = re.search(r"[-+]?\d*\.?\d+", s)
    return float(match.group()) if match else 0.0

def get_color(value: float):
    """ Get colors using a colormap of matplotlib """
    cmap = plt.get_cmap(NAME_CMAP)
    norm = plt.Normalize(vmin=min_thickness, vmax=max_thickness)
    return cmap(norm(value))[:3]

def gen_colorbar():
    """ Generate colorbar-legend using a colormap of matplotlib """
    cmap = plt.get_cmap(NAME_CMAP)
    norm = plt.Normalize(vmin=min_thickness, vmax=max_thickness)
    fig, ax = plt.subplots(figsize=(2, 6))   # Width, height in inches.
    fig.subplots_adjust(left=0.4, right=0.5) # Size adjustment for borders
    cb = plt.colorbar(plt.cm.ScalarMappable(norm, cmap), cax=ax)
    cb.set_label("Plate thickness [mm]")     # Label
    plt.show()
    return

# Make a dictionaly of plate thicknesses
dict_thickness = {}
default_thickness = 0
objects = FreeCAD.ActiveDocument.Objects
for obj in objects:
    # Get thickness from ElementGeometry2D
    if obj.TypeId == TYPEID_EG2D:
        if(len(obj.References) == 0): # Default of thickness
            default_thickness = extract_number(str(obj.Thickness)) if obj.Thickness else 0
        else: # Define thickness from References
            faces = obj.References[0][1]
            for face in faces:
                dict_thickness[face] = extract_number(str(obj.Thickness))

# Add default thickness to dict_thickness
if default_thickness != 0:
    dict_thickness["default"] = default_thickness

if not dict_thickness:
    FreeCAD.Console.PrintError("-------- No plate thickness defined! --------")
else:
    # Set min/max thickness from dict_thickness
    min_thickness = min(dict_thickness.values(), default=MIN_THICKNESS)
    max_thickness = max(dict_thickness.values(), default=MAX_THICKNESS)
    
    # Get object from BooleanFragments
    obj_name = "BooleanFragments"
    obj = FreeCAD.ActiveDocument.getObject(obj_name)
    if obj is None:
        print(f"Error: '{obj_name}' not found.")
    else:
        num_faces = len(obj.Shape.Faces)
        # Make color-list from each plate's thickness
        colors = []
        undefined_faces = []
        for i, face in enumerate(obj.Shape.Faces, start=1):
            face_name = f"Face{i}"
            if face_name in dict_thickness:
                thickness = dict_thickness[face_name]
                colors.append(get_color(thickness))
            elif default_thickness != 0:
                colors.append(get_color(default_thickness))
            else:
                colors.append(UNDEFINED_COLOR)
                undefined_faces.append(face_name)
        
        obj.ViewObject.DiffuseColor = colors
        FreeCADGui.updateGui()
        FreeCAD.Console.PrintMessage(f"Applied thickness-based colors to {num_faces} faces. ({min_thickness}/{max_thickness})\n")

        if default_thickness == 0: # Default thickness was not defined
            if undefined_faces:
                FreeCAD.Console.PrintError("Some faces have no specified thickness. They were colored black!\n")
            else:
                FreeCAD.Console.PrintMessage("No default thickness was defined, but all faces had a specified thickness.\n")
    
    # Generate colorbar-legend
    gen_colorbar()


【使い方】
・対象となるサーフェースモデルを表示した状態で本スクリプトを実行してください
・NAME_CMAPに使用したいMatplotlibのカラーマップを指定してください(サンプルコードでは'rainbow')
・正常に実行されればサーフェースモデルが着色され,凡例が表示されます
・Default値が設定されていない場合,板厚の定義がされていないFaceはカラーマップに関係なく黒色で着色されます

FreeCADで具体的な形状(複数のH型鋼)に適用した例を示します