在打开级联中修改几何体时,如何在STEP文件中保留颜色?

2024-09-30 03:23:49 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在用python编写一个脚本,使用开放式级联技术(使用Anaconda的pyOCCT包)导入STEP文件,按程序将其破坏并重新导出。我希望尽可能保留产品的层次结构、名称和颜色。当前,脚本可以导入STEP文件,简化所有几何体,同时大致保留层次结构并重新导出STEP文件。问题是,无论我如何处理这个问题,我都无法使它在某些特定情况下保持STEP文件的颜色。
以下是我传递给脚本的模型: Input 下面是简化的结果: Simplified 在这种情况下,简化工作正常,但一些尸体的颜色没有得到保留。共同的线索是,失去颜色的主体是只有其他主体作为其子产品的产品的子产品(即:它们不包含子产品)。 这似乎与OpenCascade导入步骤文件的方式有关,步骤文件的翻译如下:
OCCT translation results

好的,现在来看一些代码:

from OCCT.STEPControl import STEPControl_Reader, STEPControl_Writer, STEPControl_AsIs
from OCCT.BRepAlgoAPI import BRepAlgoAPI_Defeaturing
from OCCT.TopAbs import TopAbs_FACE, TopAbs_SHAPE, TopAbs_COMPOUND
from OCCT.TopExp import TopExp_Explorer
from OCCT.ShapeFix import ShapeFix_Shape
from OCCT.GProp import GProp_GProps
from OCCT.BRepGProp import BRepGProp
from OCCT.TopoDS import TopoDS
from OCCT.TopTools import TopTools_ListOfShape
from OCCT.BRep import BRep_Tool
from OCCT.Quantity import Quantity_ColorRGBA
from OCCT.ShapeBuild import ShapeBuild_ReShape

from OCCT.STEPCAFControl import STEPCAFControl_Reader, STEPCAFControl_Writer
from OCCT.XCAFApp import XCAFApp_Application
from OCCT.XCAFDoc import XCAFDoc_DocumentTool, XCAFDoc_ColorGen, XCAFDoc_ColorSurf 
from OCCT.XmlXCAFDrivers import XmlXCAFDrivers
from OCCT.TCollection import TCollection_ExtendedString
from OCCT.TDF import TDF_LabelSequence
from OCCT.TDataStd import TDataStd_Name
from OCCT.TDocStd import TDocStd_Document
from OCCT.TNaming import TNaming_NamedShape
from OCCT.Interface import Interface_Static

# DBG
def export_step(shape, path):
    writer = STEPControl_Writer()
    writer.Transfer( shape, STEPControl_AsIs )
    writer.Write(path)

# DBG
def print_shape_type(label, shapeTool):
    if shapeTool.IsFree_(label):
        print("Free")
    if shapeTool.IsShape_(label):
        print("Shape")
    if shapeTool.IsSimpleShape_(label):
        print("SimpleShape")
    if shapeTool.IsReference_(label):
        print("Reference")
    if shapeTool.IsAssembly_(label):
        print("Assembly")
    if shapeTool.IsComponent_(label):
        print("Component")
    if shapeTool.IsCompound_(label):
        print("Compound")
    if shapeTool.IsSubShape_(label):
        print("SubShape")

# Returns a ListOfShape containing the faces to be removed in the defeaturing
# NOTE: For concisness I've simplified this algorithm and as such it *MAY* not produce exactly 
# the same output as shown in the screenshots but should still do SOME simplification
def select_faces(shape):
    exp = TopExp_Explorer(shape, TopAbs_FACE)
    selection = TopTools_ListOfShape()
    nfaces = 0
    while exp.More():
        rgb = None
        s = exp.Current()
        exp.Next()
        nfaces += 1

        face = TopoDS.Face_(s)
        gprops = GProp_GProps()
        BRepGProp.SurfaceProperties_(face, gprops)
        area = gprops.Mass()

        surf = BRep_Tool.Surface_(face)

        if area < 150:
            selection.Append(face)
            #log(f"\t\tRemoving face with area: {area}")

    return selection, nfaces

# Performs the defeaturing
def simplify(shape):
    defeaturer = BRepAlgoAPI_Defeaturing()
    defeaturer.SetShape(shape)

    sel = select_faces(shape)
    if sel[0].Extent() == 0:
        return shape

    defeaturer.AddFacesToRemove(sel[0])
    defeaturer.SetRunParallel(True)
    defeaturer.SetToFillHistory(False)
    defeaturer.Build()

    if (not defeaturer.IsDone()):
        return shape# TODO: Handle errors
    return defeaturer.Shape()

# Given the label of an entity it finds it's displayed colour. If the entity has no defined colour the parents are searched for defined colours as well.
def find_color(label, colorTool):
    col = Quantity_ColorRGBA()
    status = False
    while not status and label != None:
        try:
            status = colorTool.GetColor(label, XCAFDoc_ColorSurf, col)
        except:
            break
        label = label.Father()
    return (col.GetRGB().Red(), col.GetRGB().Green(), col.GetRGB().Blue(), col.Alpha(), status, col)

# Finds all child shapes and simplifies them recursively. Returns true if there were any subshapes.
# For now this assumes all shapes passed into this are translated as "SimpleShape". 
# "Assembly" entities should be skipped as we don't need to touch them, "Compound" entities should work with this as well, though the behaviour is untested. 
# Use the print_shape_type(shapeLabel, shapeTool) method to identify a shape.
def simplify_subshapes(shapeLabel, shapeTool, colorTool, set_colours=None):
    labels = TDF_LabelSequence()
    shapeTool.GetSubShapes_(shapeLabel, labels)
    #print_shape_type(shapeLabel, shapeTool)
    #print(f"{shapeTool.GetShape_(shapeLabel).ShapeType()}")
    cols = {}

    for i in range(1, labels.Length()+1):
        label = labels.Value(i)
        currShape = shapeTool.GetShape_(label)
        print(f"\t{currShape.ShapeType()}")
        if currShape.ShapeType() == TopAbs_COMPOUND:
            # This code path should never be taken as far as I understand
            simplify_subshapes(label, shapeTool, colorTool, set_colours)
        else:
            ''' See the comment at the bottom of the main loop for an explanation of the function of this block
            col = find_color(label, colorTool)
            #print(f"{name} RGBA: {col[0]:.5f} {col[1]:.5f} {col[2]:.5f} {col[3]:.5f} defined={col[4]}")
            cols[label.Tag()] = col

            if set_colours != None:
                colorTool.SetColor(label, set_colours[label.Tag()][5], XCAFDoc_ColorSurf)'''

            # Doing both of these things seems to result in colours being reset but the geometry doesn't get replaced
            nshape = simplify(currShape)
            shapeTool.SetShape(label, nshape) # This doesn't work

    return labels.Length() > 0, cols

# Set up XCaf Document
app = XCAFApp_Application.GetApplication_()
fmt = TCollection_ExtendedString('MDTV-XCAF')
doc = TDocStd_Document(fmt)
app.InitDocument(doc)

shapeTool = XCAFDoc_DocumentTool.ShapeTool_(doc.Main())
colorTool = XCAFDoc_DocumentTool.ColorTool_(doc.Main())

# Import the step file
reader = STEPCAFControl_Reader()
reader.SetNameMode(True)
reader.SetColorMode(True)
Interface_Static.SetIVal_("read.stepcaf.subshapes.name", 1) # Tells the importer to import subshape names

reader.ReadFile("testcolours.step")
reader.Transfer(doc)
labels = TDF_LabelSequence()
shapeTool.GetShapes(labels)

# Simplify each shape that was imported
for i in range(1, labels.Length()+1):
    label = labels.Value(i)
    shape = shapeTool.GetShape_(label)

    # Assemblies are just made of other shapes, so we'll skip this and simplify them individually...
    if shapeTool.IsAssembly_(label):
        continue

    # This function call here is meant to be the fix for the bug described.
    # The idea was to check if the TopoDS_Shape we're looking at is a COMPOUND and if so we would simplify and call SetShape() 
    # on each of the sub-shapes instead in an attempt to preserve the colours stored in the sub-shape's labels.
    #status, loadedCols = simplify_subshapes(label, shapeTool, colorTool)
    #if status:
        #continue

    shape = simplify(shape)
    shapeTool.SetShape(label, shape)

    # The code gets a bit messy here because this was another attempt at fixing the problem by building a dictionary of colours 
    # before the shapes were simplified and then resetting the colours of each subshape after simplification. 
    # This didn't work either.
    # But the idea was to call this function once to generate the dictionary, then simplify, then call it again passing in the dictionary so it could be re-applied.
    #if status:
    #    simplify_subshapes(label, shapeTool, colorTool, loadedCols)

shapeTool.UpdateAssemblies()

# Re-export
writer = STEPCAFControl_Writer()
Interface_Static.SetIVal_("write.step.assembly", 2) 
Interface_Static.SetIVal_("write.stepcaf.subshapes.name", 1)
writer.Transfer(doc, STEPControl_AsIs)
writer.Write("testcolours-simplified.step")

这里有很多关于最小可复制示例的内容,但程序的一般流程是我们导入step文件:

reader.ReadFile("testcolours.step")
reader.Transfer(doc)

然后我们迭代文件中的每个标签(基本上是树中的每个节点):

labels = TDF_LabelSequence()
shapeTool.GetShapes(labels)

# Simplify each shape that was imported
for i in range(1, labels.Length()+1):
    label = labels.Value(i)
    shape = shapeTool.GetShape_(label)

我们跳过任何标记为程序集的标签,因为它们包含子项,我们只想简化单个实体。然后调用simplify(shape)执行简化并返回一个新的形状,然后调用shapeTool.SetShape()将新形状绑定到旧标签。
这里不起作用的是,如前所述,Component3和Component4没有被标记为组件,而是被视为简单图形,当它们被简化为一个形状时,颜色就会丢失

我尝试的一个解决方案是调用一个方法simplify_subshapes(),该方法将遍历每个子图形,并执行与主循环相同的操作,简化它们,然后调用SetShape()。这最终变得更糟,因为它导致这些身体根本没有被简化,但仍然失去了它们的颜色

我还尝试使用simplify_subshapes()方法制作子形状所有颜色的字典,然后简化复合形状,然后再次调用相同的方法,这次使用字典将颜色重新应用于子形状(注释了代码,并解释了它的作用)

            col = find_color(label, colorTool)
            #print(f"{name} RGBA: {col[0]:.5f} {col[1]:.5f} {col[2]:.5f} {col[3]:.5f} defined={col[4]}")
            cols[label.Tag()] = col

            if set_colours != None:
                colorTool.SetColor(label, set_colours[label.Tag()][5], XCAFDoc_ColorSurf)

在我看来,这个问题可以通过开放级联将组件3和组件4作为组件导入来解决,也可以通过找到一种方法使SetShape()在子图形上按预期工作来解决

以下是指向测试文件的链接: testcolours.step


Tags: thetofromimportlabelsifcolsimplify

热门问题