The color problem of Boolean operation

In the Open Cascade, the Boolean operation Class need to input Topods_Shape. However the TopoDS_Shape doesn't contain color information. If the two topoDS_shape have differnt color,  Can OCC keep the color after boolean operation?  For example, I want to do boolean union of a red box and a green box. In the end,  the union box will be red on one side, green on the other side. Can Open Cascade do that? Which class do I need to use?

Kirill Gavrilov's picture

As you have been mentioned, TopoDS_Shape is only geometry placeholder - it does not define any attributes like color, material, text or similar.
Such attributes can be stored in different ways like simple data map in AIS_ColoredShape, XCAF document or application-specific data model based on TObj/different kind.

OCCT algorithms (Boolean operations and similar) operate on geometry/topology level - e.g. work with TopoDS_Shape and do not deal with application-specific attributes stored elsewhere.
This separation is robust and clear - therefore, it is a responsibility of application-level modification operation to preserve necessary attributes attached to shapes, when it is necessary and actually possible.

While preserving entire shape attributes is more or less straightforward, dealing with sub-shape attributes involves some complexity (and ambiguity in some cases).
This kind of functionality is normally relies on a Shape modification History, which is provided by all topological API algorithms in OCCT.
So that basically application should re-map shape attributes from old shapes in history to new ones in some way defined by application itself.

Thomas Delorme's picture

Hey, digging up some old bones here :)

As the OP above, I am trying to keep track of colors during geometry creation. The application is receiving a csg tree to create a solid. Each node of the csg tree (i.e. box, torus, extrude, etc...) has a color. My strategy was to use the XCAF document as you mentioned above. It works well with basic csg tree such as a unique box node with color blue. However, the tricky part comes with boolean operations (again, as the OP mentioned).

Here is the method to register a shape in the XCAF document:

void GeometryCreationContext::RegisterShape(const TopoDS_Shape &shape, const Quantity_Color &color, const std::string &name) const
{
    const TDF_Label shapeLabel = m_shapeTool->AddShape(shape, false);

    const std::string shapeIdentifier = name + std::to_string(rand() % 1000);
    TDataStd_Name::Set(shapeLabel, shapeIdentifier.data());

    TopTools_IndexedMapOfShape faces;
    TopExp::MapShapes(shape, TopAbs_FACE, faces);
    for (int i = 1; i <= faces.Extent(); ++i)
    {
        TopoDS_Face face = TopoDS::Face(faces(i));
        TDF_Label faceLabel = m_shapeTool->AddSubShape(shapeLabel, face);
        if (faceLabel.IsNull())
        {
            LOG_WARN("Label of sub-shape {} is null for shape {} ", i, name);
            continue;
        }
        m_colorTool->SetColor(faceLabel, color, XCAFDoc_ColorGen);
        LOG_TRACE("Created sub-shape {} with color ({}, {}, {}) in {} ", i, color.Red(), color.Green(), color.Blue(), name);

        TDataStd_Name::Set(faceLabel, ("face-" + std::to_string(i)).data());
    }
}

The boolean operation history is great when it comes to identify the shape that were generated, deleted or modified. But I didn't see any functionality to see which face was. Did I missed it?

For reference, this is my operation node (cut, fuse and common) main method:

void OperatorNode::Do(const TopoDS_Shape &left, const TopoDS_Shape &right, GeometryCreationContext& context, TopoDS_Shape &outShape) const
{
    // ...

    BRepAlgoAPI_BooleanOperation booleanOperation;
    booleanOperation.SetOperation(m_operationType);

    TopTools_ListOfShape aLS;
    aLS.Append(left);
    TopTools_ListOfShape aLT;
    aLT.Append(right);
    booleanOperation.SetArguments(aLS);
    booleanOperation.SetTools(aLT);
    booleanOperation.SetFuzzyValue(1e-45);
    booleanOperation.SetRunParallel(true);

    booleanOperation.Build();
    outShape = booleanOperation.Shape();

    const Handle(BRepTools_History) history = booleanOperation.History();

    // ... trying to register my shape here
    // context.RegisterShape(outShape, Quantity_NOC_MAGENTA, m_name + "_outShape");
}

The way I see it, OCCT is just not suitable for color tracking during geometry construction. I might have to live with that. If you have any example proving otherwise, I'd me more than happy to look at them.

Have a good one!

gkv311 n's picture

Thomas Delorme wrote:

The boolean operation history is great when it comes to identify the shape that were generated, deleted or modified. But I didn't see any functionality to see which face was. Did I missed it?

I guess you are trying to put several complex things together - Boolean operations, colors on Solids, colors on Faces, XCAF document... Implementing a final solution will require handling a plenty of various options.

For instance, if you allow assigning colors to Solids, then you'll to explode modified Solids into Faces if you'll want to merge colors of both originating Solids... And then you'll need to maintain list of subshapes in XCAF document in a way as it is expected to work in XCAF.

Lets localize one of the problem and try to solve a particular step. For instance, we have 2 simple Solids with different colors and we want to propagate original colors to new Faces in result of Boolean operation. In this Draw Harness sample we will avoid using XCAF and instead assign colors to subshapes via AIS_ColoredShape class (by the way XCAFPrs_AISObject inherits this class and dispatches colors from XCAF document):

pload MODELING VISUALIZATION

# create 2 solids
box b 100 200 300
psphere s 100

# display solids with different face colors
vinit View1
vdisplay b s -dispMode 1
vfit
set bf [explode b F]
set sf [explode s F]
vaspects b -subshapes {*}$bf -color RED
vaspects s -subshapes {*}$sf -color GREEN

# perform Boolean operation
bcut r s b

# iterate over history
savehistory h
compound bf_m
compound bf_d
foreach fi $bf {
  # get modification results
  if { [info exists m] } { unset m }
  modified m h $fi;
  if { [info exists m] } { add m bf_m }
  # get deletion status
  if { [isdeleted h $fi] == "Deleted." } { add $fi bf_d }
}

compound sf_m
compound sf_d
foreach fi $sf {
  # get modification results
  if { [info exists m] } { unset m }
  modified m h $fi;
  if { [info exists m] } { add m sf_m }
  # get deletion status
  if { [isdeleted h $fi] == "Deleted." } { add $fi sf_d }
}

# display Boolean result and apply subshape colors
vclear
vdisplay r -dispMode 1
vaspects r -subshapes {*}[explode bf_m F] -color RED
vaspects r -subshapes {*}[explode sf_m F] -color GREEN

In this particular sample every Face from original Solids is either 'modified' or 'deleted'. Here we iterate over Faces in original Solids (via TopExp_Explorer(theSolid, TopAbs_FACE)), check if Face has been deleted (BRepTools_History::IsRemoved()) or get modified Face (BRepTools_History::Modified()).

Then we display result of Boolean operations via AIS_ColoredShape and assign colors AIS_ColoredShape::SetCustomColor() to each Face. For simplicity, I use colors of original Solids, but the logic could be improved to maintain original per-Face colors.

The 'generated' (BRepTools_History::Generated()) output of original Faces will be Edges, which we are probably not interested in our case. In real case we will need to also handle unmodified Faces and keep their colors.

bop_colors.png

Attachments: 
gkv311 n's picture

2Thomas Delorme

There is also an OCAF mechanism called Topological Naming which is supposed to aim to track such kind of shape modifications in the document.

You may also find some insights about the topological naming problem within FreeCAD wiki.