From a06c70ec00e64296ca973dffb19f5b36bbd36d6f Mon Sep 17 00:00:00 2001 From: Jeremy Wright Date: Wed, 22 Oct 2025 11:44:52 -0400 Subject: [PATCH 1/7] Initial implementation of an assembly material class which wraps Graphic3d_MaterialAspect --- cadquery/__init__.py | 3 +- cadquery/assembly.py | 15 ++++++++- cadquery/occ_impl/assembly.py | 57 +++++++++++++++++++++++++++++++++++ tests/test_assembly.py | 33 ++++++++++++++++++++ 4 files changed, 106 insertions(+), 2 deletions(-) diff --git a/cadquery/__init__.py b/cadquery/__init__.py index 38b06cbee..7f4a13623 100644 --- a/cadquery/__init__.py +++ b/cadquery/__init__.py @@ -37,7 +37,7 @@ ) from .sketch import Sketch from .cq import CQ, Workplane -from .assembly import Assembly, Color, Constraint +from .assembly import Assembly, Color, Constraint, Material from . import selectors from . import plugins @@ -48,6 +48,7 @@ "Assembly", "Color", "Constraint", + "Material", "plugins", "selectors", "Plane", diff --git a/cadquery/assembly.py b/cadquery/assembly.py index 65c024702..33038ebd4 100644 --- a/cadquery/assembly.py +++ b/cadquery/assembly.py @@ -19,7 +19,7 @@ from .cq import Workplane from .occ_impl.shapes import Shape, Compound, isSubshape from .occ_impl.geom import Location -from .occ_impl.assembly import Color +from .occ_impl.assembly import Color, Material from .occ_impl.solver import ( ConstraintKind, ConstraintSolver, @@ -88,6 +88,7 @@ class Assembly(object): loc: Location name: str color: Optional[Color] + material: Optional[Material] metadata: Dict[str, Any] obj: AssemblyObjects @@ -110,6 +111,7 @@ def __init__( loc: Optional[Location] = None, name: Optional[str] = None, color: Optional[Color] = None, + material: Optional[Material] = None, metadata: Optional[Dict[str, Any]] = None, ): """ @@ -119,6 +121,7 @@ def __init__( :param loc: location of the root object (default: None, interpreted as identity transformation) :param name: unique name of the root object (default: None, resulting in an UUID being generated) :param color: color of the added object (default: None) + :param material: material (for visual and/or physical properties) of the added object (default: None) :param metadata: a store for user-defined metadata (default: None) :return: An Assembly object. @@ -138,6 +141,7 @@ def __init__( self.loc = loc if loc else Location() self.name = name if name else str(uuid()) self.color = color if color else None + self.material = material if material else None self.metadata = metadata if metadata else {} self.parent = None @@ -179,6 +183,7 @@ def add( loc: Optional[Location] = None, name: Optional[str] = None, color: Optional[Color] = None, + material: Optional[Material] = None, ) -> Self: """ Add a subassembly to the current assembly. @@ -190,6 +195,8 @@ def add( the subassembly being used) :param color: color of the added object (default: None, resulting in the color stored in the subassembly being used) + :param material: material (for visual and/or physical properties) of the added object + (default: None) """ ... @@ -200,6 +207,7 @@ def add( loc: Optional[Location] = None, name: Optional[str] = None, color: Optional[Color] = None, + material: Optional[Material] = None, metadata: Optional[Dict[str, Any]] = None, ) -> Self: """ @@ -211,6 +219,8 @@ def add( :param name: unique name of the root object (default: None, resulting in an UUID being generated) :param color: color of the added object (default: None) + :param material: material (for visual and/or physical properties) of the added object + (default: None) :param metadata: a store for user-defined metadata (default: None) """ ... @@ -234,6 +244,9 @@ def add(self, arg, **kwargs): subassy.loc = kwargs["loc"] if kwargs.get("loc") else arg.loc subassy.name = kwargs["name"] if kwargs.get("name") else arg.name subassy.color = kwargs["color"] if kwargs.get("color") else arg.color + subassy.material = ( + kwargs["material"] if kwargs.get("material") else arg.material + ) subassy.metadata = ( kwargs["metadata"] if kwargs.get("metadata") else arg.metadata ) diff --git a/cadquery/occ_impl/assembly.py b/cadquery/occ_impl/assembly.py index d76422bb7..25b5a7dfd 100644 --- a/cadquery/occ_impl/assembly.py +++ b/cadquery/occ_impl/assembly.py @@ -13,6 +13,7 @@ from typing_extensions import Protocol, Self from math import degrees, radians +from OCP.TCollection import TCollection_AsciiString from OCP.TDocStd import TDocStd_Document from OCP.TCollection import TCollection_ExtendedString from OCP.XCAFDoc import ( @@ -37,6 +38,7 @@ from OCP.BOPAlgo import BOPAlgo_GlueEnum, BOPAlgo_MakeConnected from OCP.TopoDS import TopoDS_Shape from OCP.gp import gp_EulerSequence +from OCP.Graphic3d import Graphic3d_MaterialAspect, Graphic3d_NameOfMaterial from vtkmodules.vtkRenderingCore import ( vtkActor, @@ -57,6 +59,61 @@ AssemblyObjects = Union[Shape, Workplane, None] +class Material(object): + """ + Wrapper for the OCCT material object Graphic3d_MaterialAspect. + Graphic3d_MaterialAspect is mainly for rendering purposes, so this + Material class could be extended to also contain physical material + properties. + """ + + wrapped: Graphic3d_MaterialAspect + + def __init__(self, name: str | Graphic3d_NameOfMaterial | None = None, **kwargs): + """ + Can be passed an arbitrary string name, a string name of an OCC material, + or a Graphic3d_NameOfMaterial object. If nothing is passed, the default OCC + material is used. + """ + + # Get the name from the positional arguments or the kwargs + material_name = ( + name + if name is not None + else kwargs["name"] + if name in kwargs.keys() + else None + ) + + # The caller wants a default materials object + if material_name is None: + self.wrapped = Graphic3d_MaterialAspect() + # If we have a string, there may be some additional lookup that needs to happen + elif isinstance(material_name, str): + # Check to see if the name is one of the pre-defined materials in OpenCASCADE + if material_name in dir(Graphic3d_NameOfMaterial): + occ_mat = getattr(Graphic3d_NameOfMaterial, material_name) + self.wrapped = Graphic3d_MaterialAspect(occ_mat) + else: + # An arbitrary user-defined name is being used + self.wrapped = Graphic3d_MaterialAspect( + Graphic3d_NameOfMaterial.Graphic3d_NameOfMaterial_UserDefined + ) + self.wrapped.SetMaterialName(TCollection_AsciiString(material_name)) + # The caller is passing a direct OCC material type + elif isinstance(material_name, Graphic3d_NameOfMaterial): + self.wrapped = Graphic3d_MaterialAspect(material_name) + else: + raise ValueError(f"Invalid material name type: {type(material_name)}") + + @property + def name(self) -> str: + """ + Read-only property to get a simple string name from the material. + """ + return self.wrapped.StringName().ToCString() + + class Color(object): """ Wrapper for the OCCT color object Quantity_ColorRGBA. diff --git a/tests/test_assembly.py b/tests/test_assembly.py index 4e73b518d..d0e4d9aa0 100644 --- a/tests/test_assembly.py +++ b/tests/test_assembly.py @@ -36,6 +36,7 @@ from OCP.TDF import TDF_ChildIterator from OCP.Quantity import Quantity_ColorRGBA, Quantity_TOC_sRGB, Quantity_NameOfColor from OCP.TopAbs import TopAbs_ShapeEnum +from OCP.Graphic3d import Graphic3d_NameOfMaterial @pytest.fixture(scope="function") @@ -1523,6 +1524,38 @@ def test_colors_assy1(assy_fixture, request, tmpdir, kind): check_assy(assy, assy_i) +def test_materials(): + # Test a default material + mat_0 = cq.Material() + assert mat_0.wrapped.StringName().ToCString() == "Default" + + # Simple objects to be added to the assembly with the material + wp_1 = cq.Workplane().box(10, 10, 10) + wp_2 = cq.Workplane().box(5, 5, 5) + wp_3 = cq.Workplane().box(2.5, 2.5, 2.5) + + # Add the object to the assembly with the material + assy = cq.Assembly() + + # Test with the string name of a standard pre-defined material from OpenCASCADE + mat_1 = cq.Material(name="Graphic3d_NameOfMaterial_Brass") + assy.add(wp_1, material=mat_1) + assert assy.children[0].material.wrapped.StringName().ToCString() == "Brass" + assert assy.children[0].material.name == "Brass" + + # Test with a user-defined material + mat_2 = cq.Material(name="test") + assy.add(wp_2, material=mat_2) + assert assy.children[1].material.wrapped.StringName().ToCString() == "test" + assert assy.children[1].material.name == "test" + + # Test with an OpenCASCADE material type + mat_3 = cq.Material(Graphic3d_NameOfMaterial.Graphic3d_NameOfMaterial_Stone) + assy.add(wp_3, material=mat_3) + assert assy.children[2].material.wrapped.StringName().ToCString() == "Stone" + assert assy.children[2].material.name == "Stone" + + @pytest.mark.parametrize( "assy_fixture, expected", [ From 98984da7031a30c4c08184c8f637353d881e27e7 Mon Sep 17 00:00:00 2001 From: Jeremy Wright Date: Wed, 22 Oct 2025 12:26:40 -0400 Subject: [PATCH 2/7] Allow passing of material as a string when adding a subassembly --- cadquery/assembly.py | 11 +++++++++-- tests/test_assembly.py | 5 +++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/cadquery/assembly.py b/cadquery/assembly.py index 33038ebd4..17f69a269 100644 --- a/cadquery/assembly.py +++ b/cadquery/assembly.py @@ -82,6 +82,13 @@ def _define_grammar(): _grammar = _define_grammar() +def _ensure_material(material): + """ + Convert string to Material if needed. + """ + return Material(material) if isinstance(material, str) else material + + class Assembly(object): """Nested assembly of Workplane and Shape objects defining their relative positions.""" @@ -207,7 +214,7 @@ def add( loc: Optional[Location] = None, name: Optional[str] = None, color: Optional[Color] = None, - material: Optional[Material] = None, + material: Optional[Union[Material, str]] = None, metadata: Optional[Dict[str, Any]] = None, ) -> Self: """ @@ -244,7 +251,7 @@ def add(self, arg, **kwargs): subassy.loc = kwargs["loc"] if kwargs.get("loc") else arg.loc subassy.name = kwargs["name"] if kwargs.get("name") else arg.name subassy.color = kwargs["color"] if kwargs.get("color") else arg.color - subassy.material = ( + subassy.material = _ensure_material( kwargs["material"] if kwargs.get("material") else arg.material ) subassy.metadata = ( diff --git a/tests/test_assembly.py b/tests/test_assembly.py index d0e4d9aa0..0263218d6 100644 --- a/tests/test_assembly.py +++ b/tests/test_assembly.py @@ -1533,6 +1533,7 @@ def test_materials(): wp_1 = cq.Workplane().box(10, 10, 10) wp_2 = cq.Workplane().box(5, 5, 5) wp_3 = cq.Workplane().box(2.5, 2.5, 2.5) + wp_4 = cq.Workplane().box(1.25, 1.25, 1.25) # Add the object to the assembly with the material assy = cq.Assembly() @@ -1555,6 +1556,10 @@ def test_materials(): assert assy.children[2].material.wrapped.StringName().ToCString() == "Stone" assert assy.children[2].material.name == "Stone" + # Test plain material string passed when adding subassembly + assy.add(wp_4, material="Unobtanium") + assert assy.children[3].material.name == "Unobtanium" + @pytest.mark.parametrize( "assy_fixture, expected", From d73391de3163a7ffdbda711bfc40df77957a678e Mon Sep 17 00:00:00 2001 From: Jeremy Wright Date: Wed, 22 Oct 2025 14:39:12 -0400 Subject: [PATCH 3/7] Fixed metadata bug because of _copy call --- cadquery/assembly.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/cadquery/assembly.py b/cadquery/assembly.py index 17f69a269..d025581bb 100644 --- a/cadquery/assembly.py +++ b/cadquery/assembly.py @@ -167,7 +167,9 @@ def _copy(self) -> "Assembly": Make a deep copy of an assembly """ - rv = self.__class__(self.obj, self.loc, self.name, self.color, self.metadata) + rv = self.__class__( + self.obj, self.loc, self.name, self.color, self.material, self.metadata + ) rv._subshape_colors = dict(self._subshape_colors) rv._subshape_names = dict(self._subshape_names) @@ -190,7 +192,6 @@ def add( loc: Optional[Location] = None, name: Optional[str] = None, color: Optional[Color] = None, - material: Optional[Material] = None, ) -> Self: """ Add a subassembly to the current assembly. @@ -202,8 +203,6 @@ def add( the subassembly being used) :param color: color of the added object (default: None, resulting in the color stored in the subassembly being used) - :param material: material (for visual and/or physical properties) of the added object - (default: None) """ ... @@ -257,12 +256,17 @@ def add(self, arg, **kwargs): subassy.metadata = ( kwargs["metadata"] if kwargs.get("metadata") else arg.metadata ) + subassy.parent = self self.children.append(subassy) self.objects.update(subassy._flatten()) else: + # Convert the material string to a Material object, if needed + if "material" in kwargs: + kwargs["material"] = _ensure_material(kwargs["material"]) + assy = self.__class__(arg, **kwargs) assy.parent = self From b7102ea3dc7e3fcddec6a44a844c40341eb92aa8 Mon Sep 17 00:00:00 2001 From: Jeremy Wright Date: Thu, 23 Oct 2025 08:28:40 -0400 Subject: [PATCH 4/7] mypy fixes --- cadquery/occ_impl/assembly.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cadquery/occ_impl/assembly.py b/cadquery/occ_impl/assembly.py index 25b5a7dfd..82210bc33 100644 --- a/cadquery/occ_impl/assembly.py +++ b/cadquery/occ_impl/assembly.py @@ -295,6 +295,7 @@ def add( loc: Optional[Location] = None, name: Optional[str] = None, color: Optional[Color] = None, + material: Optional[Union[Material, str]] = None, metadata: Optional[Dict[str, Any]] = None, ) -> Self: ... @@ -305,6 +306,7 @@ def add( loc: Optional[Location] = None, name: Optional[str] = None, color: Optional[Color] = None, + material: Optional[Union[Material, str]] = None, metadata: Optional[Dict[str, Any]] = None, **kwargs: Any, ) -> Self: From 89dcce5b0fc79a208cd3386e6cbc2b3f53b87f22 Mon Sep 17 00:00:00 2001 From: Jeremy Wright Date: Thu, 23 Oct 2025 09:57:34 -0400 Subject: [PATCH 5/7] Removed test for type of material variable since types in signature take care of that --- cadquery/occ_impl/assembly.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/cadquery/occ_impl/assembly.py b/cadquery/occ_impl/assembly.py index 82210bc33..6bb95fbe2 100644 --- a/cadquery/occ_impl/assembly.py +++ b/cadquery/occ_impl/assembly.py @@ -103,8 +103,6 @@ def __init__(self, name: str | Graphic3d_NameOfMaterial | None = None, **kwargs) # The caller is passing a direct OCC material type elif isinstance(material_name, Graphic3d_NameOfMaterial): self.wrapped = Graphic3d_MaterialAspect(material_name) - else: - raise ValueError(f"Invalid material name type: {type(material_name)}") @property def name(self) -> str: From 274307fd121bffc7c1968d2ff182c532abe55f50 Mon Sep 17 00:00:00 2001 From: Jeremy Wright Date: Wed, 29 Oct 2025 10:06:46 -0400 Subject: [PATCH 6/7] Reworked Material class to wrap XCAFDoc_Material and XCAFDoc_VisMaterial instead --- cadquery/occ_impl/assembly.py | 100 +++++++++++++++++++++++----------- tests/test_assembly.py | 39 ++++++------- 2 files changed, 89 insertions(+), 50 deletions(-) diff --git a/cadquery/occ_impl/assembly.py b/cadquery/occ_impl/assembly.py index 6bb95fbe2..ae75ebe5e 100644 --- a/cadquery/occ_impl/assembly.py +++ b/cadquery/occ_impl/assembly.py @@ -13,13 +13,15 @@ from typing_extensions import Protocol, Self from math import degrees, radians -from OCP.TCollection import TCollection_AsciiString +from OCP.TCollection import TCollection_HAsciiString from OCP.TDocStd import TDocStd_Document from OCP.TCollection import TCollection_ExtendedString from OCP.XCAFDoc import ( XCAFDoc_DocumentTool, XCAFDoc_ColorType, XCAFDoc_ColorGen, + XCAFDoc_Material, + XCAFDoc_VisMaterial, ) from OCP.XCAFApp import XCAFApp_Application from OCP.BinXCAFDrivers import BinXCAFDrivers @@ -38,7 +40,6 @@ from OCP.BOPAlgo import BOPAlgo_GlueEnum, BOPAlgo_MakeConnected from OCP.TopoDS import TopoDS_Shape from OCP.gp import gp_EulerSequence -from OCP.Graphic3d import Graphic3d_MaterialAspect, Graphic3d_NameOfMaterial from vtkmodules.vtkRenderingCore import ( vtkActor, @@ -61,19 +62,19 @@ class Material(object): """ - Wrapper for the OCCT material object Graphic3d_MaterialAspect. - Graphic3d_MaterialAspect is mainly for rendering purposes, so this - Material class could be extended to also contain physical material - properties. + Wrapper for the OCCT material classes XCAFDoc_Material and XCAFDoc_VisMaterial. + XCAFDoc_Material is focused on physical material properties and + XCAFDoc_VisMaterial is for visual properties to be used when rendering. """ - wrapped: Graphic3d_MaterialAspect + wrapped: XCAFDoc_Material + wrapped_vis: XCAFDoc_VisMaterial - def __init__(self, name: str | Graphic3d_NameOfMaterial | None = None, **kwargs): + def __init__(self, name: str | None = None, **kwargs): """ - Can be passed an arbitrary string name, a string name of an OCC material, - or a Graphic3d_NameOfMaterial object. If nothing is passed, the default OCC - material is used. + Can be passed an arbitrary string name for the material along with keyword + arguments defining some other characteristics of the material. If nothing is + passed, arbitrary defaults are used. """ # Get the name from the positional arguments or the kwargs @@ -85,31 +86,68 @@ def __init__(self, name: str | Graphic3d_NameOfMaterial | None = None, **kwargs) else None ) - # The caller wants a default materials object - if material_name is None: - self.wrapped = Graphic3d_MaterialAspect() - # If we have a string, there may be some additional lookup that needs to happen - elif isinstance(material_name, str): - # Check to see if the name is one of the pre-defined materials in OpenCASCADE - if material_name in dir(Graphic3d_NameOfMaterial): - occ_mat = getattr(Graphic3d_NameOfMaterial, material_name) - self.wrapped = Graphic3d_MaterialAspect(occ_mat) - else: - # An arbitrary user-defined name is being used - self.wrapped = Graphic3d_MaterialAspect( - Graphic3d_NameOfMaterial.Graphic3d_NameOfMaterial_UserDefined - ) - self.wrapped.SetMaterialName(TCollection_AsciiString(material_name)) - # The caller is passing a direct OCC material type - elif isinstance(material_name, Graphic3d_NameOfMaterial): - self.wrapped = Graphic3d_MaterialAspect(material_name) + # Create the default material object and prepare to set a few defaults + self.wrapped = XCAFDoc_Material() + + # Default values in case the user did not set any others + aName = "Default" + aDescription = "Default material with properties similar to low carbon steel" + aDensity = 7.85 + aDensityName = "Mass density" + aDensityTypeName = "g/cm^3" + + # See if there are any non-defaults to be set + if material_name is not None: + aName = name + if "description" in kwargs.keys(): + aDescription = kwargs["description"] + if "density" in kwargs.keys(): + aDensity = kwargs["density"] + if "densityUnit" in kwargs.keys(): + aDensityTypeName = kwargs["densityUnit"] + + # Set the properties on the material object + self.wrapped.Set( + TCollection_HAsciiString(aName), + TCollection_HAsciiString(aDescription), + aDensity, + TCollection_HAsciiString(aDensityName), + TCollection_HAsciiString(aDensityTypeName), + ) + + # Create the default visual material object and allow it to be used just with + # the OCC layer, for now. When this material class is expanded to include visual + # attributes, the OCC docs say that XCAFDoc_VisMaterialTool should be used to + # manage those attributes on the XCAFDoc_VisMaterial class. + self.wrapped_vis = XCAFDoc_VisMaterial() @property def name(self) -> str: """ - Read-only property to get a simple string name from the material. + Get the string name of the material. + """ + return self.wrapped.GetName().ToCString() + + @property + def description(self) -> str: + """ + Get the string description of the material. + """ + return self.wrapped.GetDescription().ToCString() + + @property + def density(self) -> float: + """ + Get the density value of the material. + """ + return self.wrapped.GetDensity() + + @property + def densityUnit(self) -> str: + """ + Get the units that the material density is defined in. """ - return self.wrapped.StringName().ToCString() + return self.wrapped.GetDensValType().ToCString() class Color(object): diff --git a/tests/test_assembly.py b/tests/test_assembly.py index 0263218d6..6ef117332 100644 --- a/tests/test_assembly.py +++ b/tests/test_assembly.py @@ -1525,40 +1525,41 @@ def test_colors_assy1(assy_fixture, request, tmpdir, kind): def test_materials(): - # Test a default material + # Test a default material not attached to an assembly mat_0 = cq.Material() - assert mat_0.wrapped.StringName().ToCString() == "Default" + assert mat_0.name == "Default" # Simple objects to be added to the assembly with the material wp_1 = cq.Workplane().box(10, 10, 10) wp_2 = cq.Workplane().box(5, 5, 5) wp_3 = cq.Workplane().box(2.5, 2.5, 2.5) - wp_4 = cq.Workplane().box(1.25, 1.25, 1.25) # Add the object to the assembly with the material assy = cq.Assembly() - # Test with the string name of a standard pre-defined material from OpenCASCADE - mat_1 = cq.Material(name="Graphic3d_NameOfMaterial_Brass") + # Test with a default material + mat_1 = cq.Material() assy.add(wp_1, material=mat_1) - assert assy.children[0].material.wrapped.StringName().ToCString() == "Brass" - assert assy.children[0].material.name == "Brass" + assert assy.children[0].material.name == "Default" + assert ( + assy.children[0].material.description + == "Default material with properties similar to low carbon steel" + ) + assert assy.children[0].material.density == 7.85 + assert assy.children[0].material.densityUnit == "g/cm^3" - # Test with a user-defined material - mat_2 = cq.Material(name="test") + # Test with a user-defined material when passing properties in constructor + mat_2 = cq.Material( + "test", description="Test material", density=1.0, densityUnit="lb/in^3" + ) assy.add(wp_2, material=mat_2) - assert assy.children[1].material.wrapped.StringName().ToCString() == "test" assert assy.children[1].material.name == "test" + assert assy.children[1].material.description == "Test material" + assert assy.children[1].material.density == 1.0 + assert assy.children[1].material.densityUnit == "lb/in^3" - # Test with an OpenCASCADE material type - mat_3 = cq.Material(Graphic3d_NameOfMaterial.Graphic3d_NameOfMaterial_Stone) - assy.add(wp_3, material=mat_3) - assert assy.children[2].material.wrapped.StringName().ToCString() == "Stone" - assert assy.children[2].material.name == "Stone" - - # Test plain material string passed when adding subassembly - assy.add(wp_4, material="Unobtanium") - assert assy.children[3].material.name == "Unobtanium" + # The visualization material is left for later expansion + assert assy.children[1].material.wrapped_vis.IsEmpty() @pytest.mark.parametrize( From a0c45c34b072f700e4d6c4c7efa2b9a811b75c2b Mon Sep 17 00:00:00 2001 From: Jeremy Wright Date: Wed, 29 Oct 2025 12:16:41 -0400 Subject: [PATCH 7/7] mypy fix --- cadquery/occ_impl/assembly.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/cadquery/occ_impl/assembly.py b/cadquery/occ_impl/assembly.py index ae75ebe5e..416e29261 100644 --- a/cadquery/occ_impl/assembly.py +++ b/cadquery/occ_impl/assembly.py @@ -77,15 +77,6 @@ def __init__(self, name: str | None = None, **kwargs): passed, arbitrary defaults are used. """ - # Get the name from the positional arguments or the kwargs - material_name = ( - name - if name is not None - else kwargs["name"] - if name in kwargs.keys() - else None - ) - # Create the default material object and prepare to set a few defaults self.wrapped = XCAFDoc_Material() @@ -97,7 +88,7 @@ def __init__(self, name: str | None = None, **kwargs): aDensityTypeName = "g/cm^3" # See if there are any non-defaults to be set - if material_name is not None: + if name: aName = name if "description" in kwargs.keys(): aDescription = kwargs["description"]