mikedh / trimesh

Python library for loading and using triangular meshes.
https://trimesh.org
MIT License
3.02k stars 583 forks source link

dict_to_path is broken for Line objects #2303

Open Tomaubier opened 1 month ago

Tomaubier commented 1 month ago

Currently working with the dict_to_path function to generate parametric meshes from dictionaries, I ran across a bug where passing Line objects raises AttributeError: property 'closed' of 'Line' object has no setter. I managed to find a patch consisting in checking the entity type and forwarding the closedargument accordingly.

import trimesh

def dict_to_path_patched(as_dict):
    """
    Turn a pure dict into a dict containing entity objects that
    can be sent directly to a Path constructor.

    Parameters
    ------------
    as_dict : dict
      Has keys: 'vertices', 'entities'

    Returns
    ------------
    kwargs : dict
      Has keys: 'vertices', 'entities'
    """
    # start kwargs with initial value
    result = as_dict.copy()
    # map of constructors
    loaders = {"Arc": trimesh.path.entities.Arc, "Line": trimesh.path.entities.Line}
    # pre- allocate entity array
    entities = [None] * len(as_dict["entities"])
    # run constructor for dict kwargs
    for entity_index, entity in enumerate(as_dict["entities"]):

        # --- start of edited section ---

        if entity["type"] == 'Line':
            entities[entity_index] = loaders[entity["type"]](
                points=entity["points"]
            )
        else:
            entities[entity_index] = loaders[entity["type"]](
                points=entity["points"], closed=entity["closed"]
            )

        # --- end of edited section ---
        # --- start of original section ---

        entities[entity_index] = loaders[entity["type"]](
            points=entity["points"], closed=entity["closed"]
        )

        # --- end of original section ---

    result["entities"] = entities

    return result

Here is minimum working example demonstrating the issue and the proposed fix.

x, y, z = 2, 3, 5

path_2d_dict = {
    'entities': [
        {'type': 'Line', 'points': [0, 1, 2, 3, 0], 'closed': False},
        ],
    'vertices': [
        [-x/2, y/2],
        [x/2, y/2],
        [x/2, -y/2],
        [-x/2, -y/2],
    ]
}

path_2d_from_dict = trimesh.path.exchange.load.load_path(
    trimesh.path.exchange.misc.dict_to_path(path_2d_dict) # Default function -> raises AttributeError: property 'closed' of 'Line' object has no setter
    # dict_to_path_patched(path_2d_dict) # Patched function -> Works as expected
)

path_2d_from_dict.show()

Also removing closed: False from the entities list ('entities': [{'type': 'Line', 'points': [0, 1, 2, 3, 0]}],) raises KeyError: 'closed'.

System infos