2008-12-03

Adding colors and names to your application. Part 3

(continued)

* Visualization of the shapes*

The XDE framework provides functionality to display contents in 3D viewer with the help of XCAFPrs_AISObject, which eventually inherits AIS_InteractiveObject and thus can be used in a usual manner.

Since XDE is OCAF-based you should couple it with AIS in OCAF-specific way, i.e. associating a driver (TPrsStd_Driver descendant) that creates an interactive object. In this case the driver is XCAFPrs_Driver that creates an instance of XCAFPrs_AISObject. Below is a code skeleton:

TDF_Label anAccess = aDoc->GetData()->Root();
Handle(TPrsStd_AISViewer) anAISViewer;
if (!TPrsStd_AISViewer::Find (anAccess, anAISViewer)) {
Handle(V3d_Viewer) aViewer = ...;
anAISViewer = TPrsStd_AISViewer::New (anAcces, aViewer);
}

// collect sequence of labels to display
Handle(XCAFDoc_ShapeTool) aShapeTool = XCAFDoc_DocumentTool::ShapeTool (aDoc->Main());
TDF_LabelSequence seq;
aShapeTool->GetFreeShapes (seq);

// set presentations and show
for ( Standard_Integer i=1; i <= seq.Length(); i++ ) {
Handle(TPrsStd_AISPresentation) prs;
if ( ! seq.Value(i).FindAttribute ( TPrsStd_AISPresentation::GetID(), prs ) ) {
prs = TPrsStd_AISPresentation::Set(seq.Value(i),XCAFPrs_Driver::GetID());
}
prs->Display(Standard_True);
}
TPrsStd_AISViewer::Update(aDoc->GetData()->Root());

Here’s a 3D viewer screenshot.



In order to enable display of names, then XCAFPrs::SetViewNameMode() must be called with Standard_True (before display). Below is an example of 3D view with names display turned on:



Note that displayed text labels adversely impact performance, and in the case of numerous displayed labels, your viewer can become significantly less responsive.

* Non-OCAF based documents *

If for any reason you don’t use OCAF and want to exchange attributes with IGES and STEP, you will have to do this on your own directly accessing objects representing file entities. Look at source code of STEPCAFControl and IGESCAFControl packages to copy XDE behavior.

(The end)
Please rate this article using the voting buttons under the text.

14 comments:

  1. Dear Roman, as you know, in fact, XDE is just a set of useful tool for data exchange process. I.e, import/export IGES and STEP with assigned colors, names, layers, dimension tolerances, etc. But it is not so easy to implement a real application based of XDE. The XCAF tools is just a tools and example how developer can organize assignment of additional features and properties to their objects. Implementation of own application requires more flexible realization of data mode structure.
    So, please, pay attention to application architecture in your articles to help Open CASCADE community in ther projects. What kind of OCC experience they are need to analyse to construct serious appication based on OCAF, but not a monster with several hands :)
    Bst Rgrds, PTv.

    ReplyDelete
  2. Thank you Roman for the article.
    I have learned some new things.
    For Anonymous and Roman and other:

    I think, a good programmer should choise himself his own application architecture. There are a lot of kind of application based on OCC. There is no "the way" for everebody. But in order to choise the application architecture the programmer should know, what the system(OCC) is able to do.
    Roman' example is only the first step to get to know the basic of OCC.

    For the the application architecture
    It#s important at first to list the requirements of the application(s).

    For example:
    Application A1
    1. Read 3D Model from step or IGES File
    2. Show this Model

    Application A2:
    1. all items of A1 +
    2. Show this Model in diffent modes (shaded, wires usw.

    Application A3:
    1. A2+
    2. Change the entitys in the 3D Model. Colors, Location usw.
    3. Make new entitys....

    And may be Roman can say us, what the classes , what tools has OCC to solve our problems.
    Has somebody other position ?

    Dmitry

    ReplyDelete
  3. 2 Pavel (PTV) and Dmitry.

    Indeed, data model of a custom user's application should be tailored to its paradigm and features.
    We could compare offered OCAF attributes with sand, cement, bricks, wires and some other fine-granular construction elements. XDE is a bit more than that, e.g. like a wall panels or up to a simple house pattern.
    In Dmitry's list XDE is just fine for A1 and almost A2 (unless you want to store attributes of A2.2). Building your own house may require you to arrange construction elements and perhaps derive some new elements (read attributes).

    ReplyDelete
  4. Hi Roman,

    I used the above code to read a step file (from occ installer) and display it on screen.
    While it iterates successfully over the Label sequence and calls prs->Display(Standard_True) nothing is shown on screen besides the Trihedron I added to my V3d_View. I successfully created a V3d_Viewer and V3d_view and used the V3d_View member var in the call to
    anAISViewer = TPrsStd_AISViewer::New (anAcces, m_hViewer);

    Right after I the call to prs->Display(1), prs->IsDisplayed() is called and returns 0

    Is there a specific way to setup the V3d_Viewer
    and V3d_View to use it with xde? That is the only thing that is not shown in your tutorial so maybe I am doing something wrong there?

    P.S. The notes you post are really helpfull so I hope you continue with it!

    Regards,

    Arjan.

    ReplyDelete
  5. Hi Arjan,
    There is no magic in using XDE within your application. XCAFPrs_AISObject is just an ordinary AIS_InteractiveObject subclass and you may use it like any others. There was a thread on the forum - http://www.opencascade.org/org/forum/thread_15706/ - where I gave more specific details to initialize a document and connect with V3d.
    If you don't want to debug and find out where your problem is, you may work-around TPrsStd_AISPresentation and directly use XCAFPrs_AISObject once you build it with XCAFPrs_Driver. Use AIS_InteractiveContext's methods directly. You will lose undo/redo and persistence capabilities but perhaps this is OK in your case.
    Roman

    ReplyDelete
  6. Hi Roman,

    Thanks for the pointers, I finally managed to display my step file, I was missing the following statements.

    Handle(XCAFPrs_Driver) tDriver = new XCAFPrs_Driver();
    TPrsStd_DriverTable::Get()->InitStandardDrivers();
    TPrsStd_DriverTable::Get()->AddDriver(XCAFPrs_Driver::GetID(), tDriver);


    Only now I am facing a huge memory leak when I close my program. It seems related to

    TPrsStd_DriverTable::InitStandardDrivers

    I do call

    TPrsStd_DriverTable::Get()->RemoveDriver(XCAFPrs_Driver::GetID());
    TPrsStd_DriverTable::Get()->Clear();

    but that doesn't help. Or am I seeing a false positive from VS because of the global

    static Handle(TPrsStd_DriverTable) drivertable;

    in TPrsStd_DriverTable.hxx

    Anyway, your help is much appreciated.

    Arjan.

    ReplyDelete
  7. Arjan,
    Initialization of the driver table and adding XCAFPrs_Driver is done in XCAFApp_Application constructor. So, as long as you create its instance with XCAFApp_Application::GetApplication() it does it for you automatically. You can't inherit XCAFApp_Application for the reason explained in the post.
    On memory leak - I doubt it related to TPrsStd_DriverTable. I do use XCAFApp_Application and MSVS does not complain about memory leak. Another thing to not forget when working with OCAF is to close TDocStd_Document when you close your custom document class (call TDocStd_Application::Close()).

    ReplyDelete
  8. Hello Roman,

    I'm using Open Cascade from C# via a SWIG-generated wrapper in order to display and export shapes.

    Currently, my application uses a self-made tree data structure in C#. Every node of the tree can contain a TopoDS_Shape and different attributes like color, material, name. In order to display the shapes, every node creates an AIS_Shape.
    For export, I simply use IGESControl_Writer and STEPControl_Writer.

    Now, I should add names for shapes in the IGES and STEP export files.

    My application isn’t based on OCAF, because it seemed complicated and not necessary for my purposes.

    So, now I have to decide if I should deal with OCAF in order to integrate it into my application and then being able to use XDE. Or would it be easier to look at the XDE classes and to learn how XDE adds names to the IGES and STEP entities and to implement it myself in C#?

    What do you think, how I should proceed? What is the advantage of using OCAF, if I don't need undo/redo and persistence? Can you assess how much work it would be to integrate OCAF?


    Kind regards,
    Timo
    (www.cfturbo.com)

    ReplyDelete
  9. Hi Timo,

    I believe you don't need XDE to just export colors and other attributes you store in your data structure. Indeed just repeat an approach used in XDE for your case. Take a look at IGESCAFControl_Writer to see how it maps associated data from the OCAF document into the IGES model.

    Roman

    ReplyDelete
  10. Hello Roman,

    basically, exporting names to IGES wasn't hard to realize, but I had some problems:

    1) Adding names to vertices didn't work, because
    FP->FindTypedTransient ( mapper, STANDARD_TYPE(IGESData_IGESEntity), Igesent )
    returned false.
    Do you have an idea, why?

    2) Layers were not added to compounds.


    Here is a piece of code:

    Handle(TopTools_HSequenceOfShape) aSequence = new TopTools_HSequenceOfShape();

    TopoDS_Shape shape1 = BRepBuilderAPI_MakeVertex(gp_Pnt(10,10,10));
    TopoDS_Shape shape2 = BRepBuilderAPI_MakeEdge(gp_Pnt(20,10,10), gp_Pnt(40,10,10));

    TopoDS_Compound compound;
    BRep_Builder b;
    b.MakeCompound(compound);

    TopoDS_Shape shape3 = BRepBuilderAPI_MakeVertex(gp_Pnt(1,1,1));
    b.Add( compound, shape3);
    TopoDS_Shape shape4 = BRepBuilderAPI_MakeVertex(gp_Pnt(1,1,3));
    b.Add( compound, shape4);

    aSequence->Append(shape1);
    aSequence->Append(shape2);
    aSequence->Append(compound);

    for (Standard_Integer i=1;i<=aSequence->Length();i++)
    {
    TopoDS_Shape Sh = aSequence->Value(i);
    ICW.AddShape (Sh);

    // Add name and layer
    Handle(Transfer_FinderProcess) FP = ICW.TransferProcess();
    Handle(IGESData_IGESEntity) Igesent;
    Handle(TransferBRep_ShapeMapper) mapper = TransferBRep::ShapeMapper ( FP, Sh );
    if ( FP->FindTypedTransient ( mapper, STANDARD_TYPE(IGESData_IGESEntity), Igesent ) )
    {
    //Add name
    Handle(TCollection_HAsciiString) HAname = new TCollection_HAsciiString ( "Testname" );
    char ch = (char)i+'0';
    HAname->SetValue(8,(Standard_Character)ch);
    Igesent->SetLabel( HAname, 44 );

    //Add layer
    Igesent->InitLevel( 0, 25 );
    }
    }


    The directory part of the resulting IGES file:

    116 1 0 0 0 0 0 000000000D0000001
    116 0 0 1 0 0D0000002
    110 2 0 0 25 0 0 000000000D0000003
    110 0 0 1 0 Testnam2 44D0000004
    402 3 0 0 0 0 0 000000000D0000005
    402 0 0 1 1 Testnam3 44D0000006
    116 4 0 0 0 0 0 000020400D0000007
    116 0 0 1 0 0D0000008
    116 5 0 0 0 0 0 000020400D0000009
    116 0 0 1 0 0D0000010


    Does IGES support only 8 character long names or is the name property able for longer names?
    But the following test didn't produce any name in the IGES file:

    Handle(IGESBasic_Name) nameEntity = new IGESBasic_Name();
    nameEntity->Init(1, HAname);
    Igesent->AddProperty(nameEntity);

    Regards,
    Timo

    ReplyDelete
  11. Hi Timo,
    I have made some checks (but not thorough) of your problems.
    1. Vertices results are simply not mapped during IGES export (i.e. not stored inside FinderProcess). This looks like OCC limitation never discovered due to no/low use.
    Can't say about layers - I'd check how XDE does that.

    2. Names. IGES has 2 ways to associate labels - directly in the DE section or with a property. One in DE is a construct of label (8 chars max) + subscription index. The name entity can be of any length. As code in IGESData_IGESEntity::Name() suggests, the 2nd case is checked first. Your code with IGESBasic_Name seems correct, if this does not result in a new entity in the IGES file this can be limitation of the IGES writer again. Please send me an email to roman dot lygin at gmail dot com, if you want to discuss this further.

    ReplyDelete
  12. Hello Roman,

    Maybe you can help me out, I use XDE to load and display a step file. All is well and I am able to select faces and solids in local context. The problem comes when I try to change the color of a face when that face is part of an assembly. when GetFreeShapes returns an assembly I can't change a face its color but only a solids color. If GetFreeShapes returns a solid I can change the face color and the solid's color.

    The problem is that it can't find a correct label for the face if the free shape is an assembly.

    Can you see what's wrong with my code?

    void Model::onSelectionDone()
    {
    TDF_Label label;

    Handle(XCAFDoc_ShapeTool) hShapeTool = XCAFDoc_DocumentTool::ShapeTool(m_hDocument->Main());

    for(m_hContext->InitSelected(); m_hContext->MoreSelected(); m_hContext->NextSelected())
    {
    TopoDS_Shape shape = m_hContext->SelectedShape();

    if(shape.ShapeType() == TopAbs_SOLID)
    {
    if(hShapeTool->SearchUsingMap(shape, label, Standard_False, Standard_False))
    {
    setColor(label, Quantity_NOC_GRAY);
    }
    }

    if(shape.ShapeType() == TopAbs_FACE)
    {
    if(hShapeTool->Search(shape, label))
    {
    setColor(label, Quantity_NOC_RED);
    }
    }

    m_hContext->Redisplay(m_hContext->SelectedInteractive(), Standard_False);
    }

    m_hContext->CloseLocalContext();

    TPrsStd_AISViewer::Update(m_hDocument->GetData()->Root());
    }

    void Model::setColor(const TDF_Label& label, Quantity_NameOfColor color )
    {
    Handle(XCAFDoc_ColorTool) hColorTool = XCAFDoc_DocumentTool::ColorTool(m_hDocument->Main());
    Quantity_Color Col(color);
    hColorTool->SetColor(label, Col, XCAFDoc_ColorGen);
    }

    I tried different scenarios with
    ShapeTool->Search
    ShapeTool->searchUsingMap
    ShapeTool->FindShape
    ShapeTool->FindSubShape

    Is there a better way of mapping a shape to a label?

    Also is using m_hContext->Redisplay the correct way of updating the view?
    I thought I would have to use the TPrsStd_AISPresentation attached to the free shape but did not succeed.

    With kind regards,

    Arjan.

    P.S. I hope you can read the code like this without formatting

    ReplyDelete
  13. Hi,
    No immediate answer but I would rather consider using XCAFDoc_ColorTool::Get/SetColor() accepting TopoDS_Shape rather than trying to search for a label of the subshape and then feeding it.
    If there is any difference in how subshapes of assemblies or ordinary shapes are treated, then you might want to have a look at STEPCAFControl_Reader to see specifics of assemblies. I myself did not check though ...

    Hope this will be of any help. Otherwise don't hesitate to ask on the forum. There must be some people working with XDE.
    Good luck!
    Roman

    ReplyDelete
  14. Boy do I feel stupid, another case of RTFM.
    I never new those overloads existed.

    Thank you very much, I will try 'm this weekend and also find out what they do under the hood.

    Arjan.

    ReplyDelete