I have a python script that gets material names from polysurface objects and tagging the objects in layouts with text objects. This works well, except that I want to get the material names from faces of polysurfaces as many of the files I’m working with have materials assigned to faces, not the whole polysurface.
I tried using materials_table.FindIndex(brep_face.MaterialChannelIndex) but this doesn’t return the correct material.
Clearly I don’t understand the MaterialChannelIndex property but cannot find any other way to get the material assigned to a face. I checked all the topics talking about materials on objects, no joy.
Anyone know how to get this?
Hi @GaryC, below might be helpful to get started:
#! python 2
import Rhino
import rhinoscriptsyntax as rs
def SubobjectMaterialNames():
brep_id = rs.GetObject("Select brep with subobject materials", 16)
if not brep_id: return
brep_obj = rs.coercerhinoobject(brep_id, True, True)
if not brep_obj.HasSubobjectMaterials: return
material_names = set()
for component_index in brep_obj.SubobjectMaterialComponents:
render_material = brep_obj.GetMaterial(component_index)
material_names.add(render_material.Name)
print list(material_names)
SubobjectMaterialNames()
_
c.
Thanks @clement. That definitely helps. Now I’m struggling with how to match up the subobject, in this case a face, with the material. I want to get the dimensions of the face so I can calculate the surface area that has that material assigned. I tried various things, but can’t seem to match up the material with the face.
Thanks again for helping out. Really appreciate it.
I figured it out. Here is a little script that does the trick.
If there is a cleaner way, I’m open to suggestions. But this works for surfaces and polysurfaces.
#! python3
import rhinoscriptsyntax as rs
import scriptcontext as sc
import Rhino
msg="Select objects to get material"
objs = rs.GetObjects(msg, 8 + 16, preselect=True)
if objs:
for obj in objs:
rh_obj = rs.coercerhinoobject(obj, True, True)
if rh_obj.HasSubobjectMaterials:
print("Object has sub-object materials.")
for ci in rh_obj.SubobjectMaterialComponents:
for f in rh_obj.Geometry.Faces:
if ci.Index == f.ComponentIndex().Index:
area = Rhino.Geometry.AreaMassProperties.Compute(f).Area
rm = rh_obj.GetMaterial(ci)
print(f"{rm.Name} = {area}")
break
else:
print(f"Object has no sub-object materials. Object material is {rh_obj.RenderMaterial.Name}")
Hi @GaryC, you might get along with below. I guess the ComponentIndex.Index
gives you the index of the brep face without iteration. I’ve not been sure if you need the area per face + material or the total area of all subobject materials, the script should give you both:
#! python 2
import Rhino
import rhinoscriptsyntax as rs
from collections import defaultdict
def DoSomething():
brep_id = rs.GetObject("Select brep with subobject materials", 16)
if not brep_id: return
brep_obj = rs.coercerhinoobject(brep_id, True, True)
if not brep_obj.HasSubobjectMaterials: return
brep = brep_obj.Geometry
dict_areas = defaultdict(lambda: 0.0)
for ci in brep_obj.SubobjectMaterialComponents:
mat = brep_obj.GetMaterial(ci)
amp = Rhino.Geometry.AreaMassProperties.Compute(brep.Faces[ci.Index])
msg = "Face: {} Material: {} Area: {}"
print msg.format(ci.Index, mat.Name, amp.Area)
dict_areas[mat.Name] += amp.Area
for mat_name, total_area in dict_areas.iteritems():
print "Material: {} TotalArea: {}".format(mat_name, total_area)
DoSomething()
btw. if you have materials with the same name (which is allowed) you might work with material Id
instead of Name
when computing the cumulative (total) area for each material.
_
c.
Very nice! Thanks very much.