Youtube lessons

David

Active CAD practitioner
Here is some snippet:
BRepAlgoAPI_Cut aBuilder;
...
aBuilder->Build();
ShapeUpgrade_UnifySameDomain shapeUpgrade_aWorkPiece(aWorkPieceCopy); // important after Boolean operation
shapeUpgrade_aWorkPiece.Build(); //so far normal Cut Operation
BRepAlgoAPI_Section asect(aWorkPiece, aTool, Standard_False); // getting the intersection
asect.Approximation(Standard_True);
asect.Build();
TopoDS_Shape aInterface = asect.Shape();

//doing HLR to get the projection of the aInterface on a plane
myAlgo->Add(aInterface);
myAlgo->Update();
HLRBRep_HLRToShape aHLRToShape(myAlgo);
TopoDS_Shape aProjectedContactFace; //projected shape of contact
aProjectedContactFace = aHLRToShape.VCompound(); //visible projected shapes are used
ShapeFixIt(aProjectedContactFace); //some healing
Handle(TopTools_HSequenceOfShape) EdgesQ = new TopTools_HSequenceOfShape(); // exploring all Edges
Handle(TopTools_HSequenceOfShape) Wires = new TopTools_HSequenceOfShape(); // to create Wires
TopExp_Explorer anEdgeExplorer(aProjectedContactFace, TopAbs_EDGE);
while (anEdgeExplorer.More())
{
TopoDS_Edge anEdge = TopoDS::Edge(anEdgeExplorer.Current());
ShapeFixIt(anEdge); //some healing
EdgesQ->Append(anEdge);
anEdgeExplorer.Next();
}
ShapeAnalysis_FreeBounds::ConnectEdgesToWires(EdgesQ, 5.0, false, Wires); // 5.0 worked frequently
Handle(TopTools_HSequenceOfShape) ClosedWires = new TopTools_HSequenceOfShape();
Handle(TopTools_HSequenceOfShape) OpenWires = new TopTools_HSequenceOfShape();
ShapeAnalysis_FreeBounds::SplitWires(Wires, 1.0, false, ClosedWires, OpenWires);
//from here onwards we only use the ClosedWires for further area calculation (making faces and then asking MassPropoerties).

Problem still is: the projected Contact Face (aProjectedContactFace) looks like a scattered out contour but the ConnectEdgesToWires is able to get a closed wire from it.
 

Attachments

  • face.jpg
    face.jpg
    36.4 KB · Views: 6

Quaoar

Administrator
Staff member
@David do you mean that ConnectEdgesToWires does not do the job? I would expect that HLR ends up with an unsorted result, possibly containing overlapping edges and all kinds of handling geometries. This kind of issue is pretty similar to what one may face, e.g., after reading a DXF drawing. Do you need to recover an outer contour as a single WIRE? Could there be any inner contours? What is this projection used for later on?

Update: if you tell more about your use case, it will help.
 
Last edited:

David

Active CAD practitioner
@Quaoar: that is exactly my problem. I would need a single outer contours as a single WIRE, but I get overlapping edges and all kind of geometries.
What I need is just to show the projection to the user and to calculate its area. Any idea ?

By the way, I forgot to mention, that I highly appreciate your excellent youtube videos and your efforts like this forum. This brings OCCT really a big step furhter in acceptance.
 

Quaoar

Administrator
Staff member
@David if all you need is a single contour and the corresponding covered area, what if you discretize the input first (using GCPnt package) and then recover a K-neighbors hull (you'll have to implement one or you can use some existing 3-rd party)? You will naturally lose all analytical geometries and inner contours but from what you're saying, I understood that's kinda Ok. Won't this work?
 

karim

CAD community veteran
I wonder if anybody else feels the need for more lessons on the TDataStd_* types ? I think some more explanations in this direction can be very helpful.
 

Quaoar

Administrator
Staff member
@karim Fair enough. I added your proposal to the list, although I'm having a hard time trying to allocate time for lessons. Hopefully, that's only till the end of the year.
 

David

Active CAD practitioner
@David if all you need is a single contour and the corresponding covered area, what if you discretize the input first (using GCPnt package) and then recover a K-neighbors hull (you'll have to implement one or you can use some existing 3-rd party)? You will naturally lose all analytical geometries and inner contours but from what you're saying, I understood that's kinda Ok. Won't this work?
I used the K-neighbors approach for the concave hull without any third party library (to keep it simple). But I realized it is slow due to the incremental increase of neighbours if not all points had been considered. And secondly, it did not find solutions in a lot of cases where a high number of internal points existed. Therefore I used successfull another implementation based on a paper from M. Duckham, L. Kulik, M. F. Worboys, and A. Galton. "Efficient generation of simple polygons for characterizing the shape of a set of points in the plane". Pattern Recognition, 41:3224–3236, 2008.. The paper uses the term characteristic shape to refer to the shapes generated by the algorithm. The concavity of the shape depends on a scalar ranging from 0 to 1 called the chi factor. A value of 0 leads to the most concave, and 1 leads to the convex hull.
There is already a c++ version based on this paper under: https://github.com/senhorsolar/concavehull/blob/main/src/concavehull.hpp.
 

rajendrasp79

Looking around for some CAD
I would love to understand in depth selection of entities in OCC. How can we select entities and how can we add manipulators to help drag/rotate/ etc? As an example Solidworks has this UI (coordinate system) to help with dragging the model. Is that available in OCC and if not, how can we add it?

1643634505798.png
 

natalia

Moderator
Staff member
Hi guys

The sample how select, detect and use OCCT manipulator is now available in 'extras_viewerManipulator' of https://gitlab.com/ssv/lessons/-/tree/viewerManipulator

Some snapshot how it works is:

occt_manipulator.gif

AIS_Manipulator is an object that attached to other OCCT presentations and changes transformation of them depending on the selected part of the manipulator. The main ‘magic’ of it is implemented in ‘ProcessDragging’ method. We need to call it when the mouse events are processed.

In the sample above the ViewerInteractor class is responsible for this. The mousePressEvent/mouseMoveEvent/mouseReleaseEvent methods there either call processing the drag by presentation or perform the viewer action like pan/rotate/zoom or select the clicked object.


Also, a sample how to use this manipulator is available by a ‘vmanipulator’ DRAW command of OCCT with implementation in ViewerTest.
 

rajendrasp79

Looking around for some CAD
Thank you @natalia , I just tried using the sample code and it works. Really awesome!
However, it seems that transformation that this manipulator movement applies is only on presentation layer it seems. Because I moved a surface with using manipulator and saved the result as IGES, but the resulting geometry was not moved. Is that something I need to implement myself? Or is it implemented and I am missing using it? I appreciate the help.

1661083369900.png

Resuling IGES:

1661083410091.png
 
Last edited:

natalia

Moderator
Staff member
Hi @rajendrasp79,

You are right, the transformation in the sample is applied only to visu presentation. The source TopoDS_Shape stays not touched. AIS_Manipulator only changes the local transformation of the visu presentation.

In your case, we need to change the location of the TopoDS_Shape. The code below will do this but have in mind than the initial location of the shape will be lost. We need this shape change either by each mouse release(implemented in initial sample) or before dump the shape into IGES.

C++:
  void applyLocalTransformation(const Handle(AIS_InteractiveObject)&  dobject,
                                const Handle(AIS_InteractiveContext)& context)
  {
    // synchronize topology object location with the presenation location
    auto manipulator = Handle(AIS_Manipulator)::DownCast(dobject);
    if (manipulator.IsNull())
      return;
    auto shapePrs = Handle(AIS_Shape)::DownCast(manipulator->Object());
    if (shapePrs.IsNull())
      return;
    TopoDS_Shape shape = shapePrs->Shape();
    const gp_Trsf prsTrsf = shapePrs->LocalTransformation();
    if (Abs(Abs(prsTrsf.ScaleFactor()) - 1.) > TopLoc_Location::ScalePrec())
      return; // the shape cannot be scaled by manipulator according to #27457

    // update shape's location
    shape = shape.Located(prsTrsf.Multiplied(shape.Location().Transformation()));

    // The code below should be called only if we want to update shape inside visu presentation.
    shapePrs->SetShape(shape);
    // reset local transformation of the presentation as we use this transformation inside shape now.
    shapePrs->ResetTransformation();
    // redisplay presentation.
    // Now, the presentaiton has zero transformation and moved shape.
    // If selection is deactivated, the 'RecomputePrsOnly' should be called instead of 'Redisplay' as quicker.
    // But do not forget to recompute selection when activate selection back on the presentation. 
    // The second parameter 'true' means the viewer update. If several changes happens, give 'false' here
    // to update viewer after all changed are done.
    context->Redisplay(shapePrs, true);
  }

(This correction is integrated into the same Git repository: https://gitlab.com/ssv/lessons/-/tree/viewerManipulator)

Please do not forget, that setting the local transformation gives the best performance. So during drag we usually change the presentation local transformation. And by the mouse release the result transformation is set to the initial object (TopoDS_Shape of AIS_Shape or other). So we call the attached method after the mouse release processing. In the sample it's in mouseReleaseEvent of ViewerInteractor class from the sample.

Also, please note that scale change is not applicable to TopoDS_Shape when using ‘Located’ method of TopoDS_Shape. The reason is geometry definition described in OCCT issue (https://tracker.dev.opencascade.org/view.php?id=27457).

The check of exporting to IGES is performed in this way:

1. The shape is stored into IGES using the next method:
Code:
  void writeIges(const Handle(AIS_InteractiveObject)& dobject,
                 const TCollection_AsciiString&       fileName)
  {
    auto manipulator = Handle(AIS_Manipulator)::DownCast(dobject);
    if (manipulator.IsNull())
      return;

    // write result to IGES
    IGESCAFControl_Writer writer;

    Handle(TDocStd_Document) doc = new TDocStd_Document("IGES document");
    Handle(XCAFDoc_ShapeTool) shapeTool = XCAFDoc_DocumentTool::ShapeTool(doc->Main());

    auto shapePrs = Handle(AIS_Shape)::DownCast(manipulator->Object());
    if (!shapePrs.IsNull())
      shapeTool->AddShape(shapePrs->Shape(), Standard_True);

    Message_ProgressRange progressRange;
    writer.Transfer(doc, progressRange);
    writer.Write(fileName.ToCString());
  }

2. This IGES is loaded and checked in OCCT DRAW using the next script:
Code:
Draw[1]> pload ALL
1
Draw[2]> vinit
Driver1/Viewer1/View1
Draw[3]> ReadIges doc d:/tmp/iges/result.iges
Total number of loaded entities 30.

 File IGES to read : d:/tmp/iges/result.iges
Document saved with name doc
Draw[4]> XShow doc
Draw[5]> vtrihedron t

3. The result is that the shape is moved:
drag_to_IGES.png

Regards, Natalia
 

DougB

CAD practitioner
I studied Lesson 12: CAD assemblies with OpenCascade and I learned a lot of very useful information from it. But in a real CAD application, I think it would be more likely to already have a document representing a project with all its shapes and assembly hierarchy and then we might want to add another component or assembly by importing a STEP file. In order to add the imported step geometry to the assembly structure of our existing project, it would be nice if we could paste the step document onto our project document. Is this possible? If so, I wonder if you could provide any guidance on how we might go about pasting one or more documents onto an existing document.
 

Quaoar

Administrator
Staff member
Hello @DougB and welcome to the forum!

Your remark is very valid, although this whole business is very dependent on the architecture of the project where you want to insert your shape. Do you mean that such an existing project is also based on OCAF? If so, then you have some options:
  1. Inject you shape with TNaming_NamedShape attribute into one of the labels reserved in your existing document. This way you loose the metadata such as colors and names, but the hierarchy is sort of preserved in the compound structure of the shape itself.
  2. Represent the entire hierarchy as another OCAF document. In such situation, I would not recommend injecting XDE document structure right into your existing document. The thing here is that XDE is a pretty self-contained and isolated format that was not designed for being embedded. Still, you might have it as a separate document in memory and address its sections from the outside.
Both approaches are used in Analysis Situs. Having said that, I cannot avoid making another remark about assemblies as such. People often prefer storing them separately as databases (e.g., in SQLite or MongoDB) because such storage is just better suited for handling large amounts of data. They also give you a query language and are, all in all, designed for customization (you can easily add tables/records of your domain-specific objects). A good example here is Shapr3D, which arranges the assembly hierarchy as a database while storing all shapes as BLOBs.

I remember you asked (on youtube likely) if we happened to have any documentation on Analysis Situs. Unfortunately, not yet (except for this: http://analysissitus.org/features.html). But you can always ask questions here, and we'll try to guide you the best we can.
 

DougB

CAD practitioner
Hello @DougB and welcome to the forum!

Your remark is very valid, although this whole business is very dependent on the architecture of the project where you want to insert your shape. Do you mean that such an existing project is also based on OCAF? If so, then you have some options:
  1. Inject you shape with TNaming_NamedShape attribute into one of the labels reserved in your existing document. This way you loose the metadata such as colors and names, but the hierarchy is sort of preserved in the compound structure of the shape itself.
  2. Represent the entire hierarchy as another OCAF document. In such situation, I would not recommend injecting XDE document structure right into your existing document. The thing here is that XDE is a pretty self-contained and isolated format that was not designed for being embedded. Still, you might have it as a separate document in memory and address its sections from the outside.
Both approaches are used in Analysis Situs. Having said that, I cannot avoid making another remark about assemblies as such. People often prefer storing them separately as databases (e.g., in SQLite or MongoDB) because such storage is just better suited for handling large amounts of data. They also give you a query language and are, all in all, designed for customization (you can easily add tables/records of your domain-specific objects). A good example here is Shapr3D, which arranges the assembly hierarchy as a database while storing all shapes as BLOBs.

I remember you asked (on youtube likely) if we happened to have any documentation on Analysis Situs. Unfortunately, not yet (except for this: http://analysissitus.org/features.html). But you can always ask questions here, and we'll try to guide you the best we can.
Yes, my goal is to build a CAD app using OpenCascade (or PythonOCC). As you have explained in some of your tutorials, OpenCascade has provided much of the "machinery", so to speak, needed by a CAD application. OCAF includes access to lots of useful functionality, such as copy/paste, undo/redo, and load/save in various formats. Also, I think I recall you saying in one of your tutorials that OpenCascade has a way of allowing a shape to be modified while keeping its place in the assembly structure. So, if OCAF can do all that, I would say: "Why not use it?"
Toward that goal, I am trying to come up the learning curve on how to use OCAF, hence my question about using copy/paste. I would gladly settle for having assemblies and parts with names and colors. I don't need any of the information contained on XDE labels beyond shapes, names and colors. Dose OCAF do that? If so, I think It would work OK for me. So maybe I need to use a different STEP reader that will produce an OCAF document (instead of XCAF) and then maybe I can copy/paste it onto my project document. If not, I suppose I can always write a function that would take the CAD geometry imported from STEP and add labels to my project document in order to inject it into my assembly tree. That should be pretty straightforward.
 

Quaoar

Administrator
Staff member
So we have the last lesson devoted to offsets. I want to keep talking about more or less fundamental things, and the next lesson will likely be devoted to splines: how to allocate spline objects in OpenCascade, what you can do with them, etc. What do you guys think of it? I know that some of you want to hear more about somewhat general software engineering with OpenCascade (like OpenCascade + Qt, OpenCascade + VTK, etc.). This is yet to be decided; maybe our other team members can take over these topics.
 

DougB

CAD practitioner
Yes, my goal is to build a CAD app using OpenCascade (or PythonOCC). As you have explained in some of your tutorials, OpenCascade has provided much of the "machinery", so to speak, needed by a CAD application. OCAF includes access to lots of useful functionality, such as copy/paste, undo/redo, and load/save in various formats. Also, I think I recall you saying in one of your tutorials that OpenCascade has a way of allowing a shape to be modified while keeping its place in the assembly structure. So, if OCAF can do all that, I would say: "Why not use it?"
Toward that goal, I am trying to come up the learning curve on how to use OCAF, hence my question about using copy/paste. I would gladly settle for having assemblies and parts with names and colors. I don't need any of the information contained on XDE labels beyond shapes, names and colors. Dose OCAF do that? If so, I think It would work OK for me. So maybe I need to use a different STEP reader that will produce an OCAF document (instead of XCAF) and then maybe I can copy/paste it onto my project document. If not, I suppose I can always write a function that would take the CAD geometry imported from STEP and add labels to my project document in order to inject it into my assembly tree. That should be pretty straightforward.
I gave this a try. I wrote a C++ file that imports a step file and loads it into an XDE document as a component of root. The document is then saved in both step format and .xbf format. The step file written seems to be pretty good but not perfect. CAD Assistant loads it, but a couple of the names are wonky. The name of the component label referring to as1 is wrong. Also, the name of nut_1 in rod-assembly is messed up. Just the fact that the step file is almost OK makes me think that I might be on the right track. However, the .xbf file is another story. It gets written but CAD Assistant aborts trying to load it. So I am thinking that I must be doing something wrong. Ant hints? I pasted my code below.

cad-assist-under_top.png


C++:
/*
Imports a step file and loads it into an XDE document
as a component of root. The document is then saved in
both step format and .xbf format.
*/

// OpenCascade includes
#include <BRep_Builder.hxx>
#include <Interface_Static.hxx>
#include <TopoDS_Compound.hxx>
#include <BinXCAFDrivers.hxx>
#include <STEPCAFControl_Reader.hxx>
#include <STEPCAFControl_Writer.hxx>
#include <TDataStd_Name.hxx>
#include <TDocStd_Application.hxx>
#include <TDocStd_Document.hxx>
#include <TDocStd_XLinkTool.hxx>
#include <XCAFDoc_ColorTool.hxx>
#include <XCAFDoc_DocumentTool.hxx>
#include <XCAFDoc_ShapeTool.hxx>
#include <XSControl_Writer.hxx>
#include <XSControl_WorkSession.hxx>

const char* filename = "../models/as1-oc-214.stp";
const char* save_step_file_name = "/home/doug/Desktop/under_top.stp";
const char* save_doc_file_name = "/home/doug/Desktop/under_top.xbf";

struct t_DocAppStruct
{
  Handle(TDocStd_Document)    doc;
  Handle(TDocStd_Application) app;
};

struct t_prototype
{
  TopoDS_Shape shape;
  TDF_Label    label;
};

namespace
{
  t_DocAppStruct createDoc()
  {
    // Create XDE document
    Handle(TDocStd_Application) app = new TDocStd_Application;
    BinXCAFDrivers::DefineFormat(app);
    //
    Handle(TDocStd_Document) doc;
    app->NewDocument("BinXCAF", doc);
    //
    // Create a DocAppStruct
    t_DocAppStruct docApp;
    docApp.app = app;
    docApp.doc = doc;

    return docApp;
  }
}

int main(int argc, char** argv)
{
  // Step section:

  STEPCAFControl_Reader Reader;

  // Create XDE document and app for step data
  t_DocAppStruct stepDocApp = ::createDoc();
  Handle(TDocStd_Application) stepApp = stepDocApp.app;
  Handle(TDocStd_Document) stepDoc = stepDocApp.doc;

  // Read CAD and associated data from file
  IFSelect_ReturnStatus outcome = Reader.ReadFile(filename);
  //
  if ( outcome != IFSelect_RetDone )
  {
    stepApp->Close(stepDoc);
  }

  if ( !Reader.Transfer(stepDoc) )
  {
    stepApp->Close(stepDoc);
  }

  // Tools for step doc
  Handle(XCAFDoc_ShapeTool)
    SST = XCAFDoc_DocumentTool::ShapeTool( stepDoc->Main() ); // Shape tool.
  Handle(XCAFDoc_ColorTool)
    SCT = XCAFDoc_DocumentTool::ColorTool( stepDoc->Main() ); // Color tool.

  // Get root label of step data to paste (sourceLabel)
  TDF_LabelSequence stepLabels;
  SST->GetShapes(stepLabels);
  TDF_Label sourceLabel = stepLabels.Value(1);

  // Project section:

  // Create XDE document and app for project data
  t_DocAppStruct projDocApp = ::createDoc();
  Handle(TDocStd_Application) app = projDocApp.app;
  Handle(TDocStd_Document) doc = projDocApp.doc;

  // Tools for project doc
  Handle(XCAFDoc_ShapeTool)
    ST = XCAFDoc_DocumentTool::ShapeTool( doc->Main() ); // Shape tool.
  Handle(XCAFDoc_ColorTool)
    CT = XCAFDoc_DocumentTool::ColorTool( doc->Main() ); // Color tool.

  // Create Root assembly in project document
  TopoDS_Compound rootComp;
  BRep_Builder rbuilder;
  rbuilder.MakeCompound(rootComp);

  // Create root prototype.
  t_prototype rootProto;
  rootProto.shape = rootComp;
  rootProto.label = ST->AddShape(rootComp, true); // Add assy to the document.
  TDataStd_Name::Set( rootProto.label, TCollection_ExtendedString("Root") );

  // Add a compound shape as a component of root label
  TopoDS_Compound comp;
  BRep_Builder cbuilder;
  cbuilder.MakeCompound(comp);
  TDF_Label cLabel = ST->AddComponent(rootProto.label, comp, true);
  TDataStd_Name::Set(cLabel, TCollection_ExtendedString("step_data"));

  // Adding the compound shape as a component of root creates a
  // 'sibling' label at root level holding the new prototype shape.
  // This label will be the target for pasting the step root label.
  TDF_Label refLabel;
  ST->GetReferredShape(cLabel, refLabel);
 
  // Copy source label of step doc to target label of project doc
  TDocStd_XLinkTool XLinkTool;
  XLinkTool.Copy(refLabel, sourceLabel);
  ST->UpdateAssemblies();

  // Save project doc to step file
  STEPCAFControl_Writer Writer;
  // To make subshape names work, we have to turn on the following static
  // variable of OpenCascade.
  Interface_Static::SetIVal("write.stepcaf.subshapes.name", 1);
  // Write XDE document to file

  if ( !Writer.Transfer(doc, STEPControl_AsIs) )
  {
    std::cout << "The document cannot be translated or gives no result" << std::endl;
    app->Close(doc);
  }

  const IFSelect_ReturnStatus ret = Writer.Write(save_step_file_name);

  if ( ret != IFSelect_RetDone )
  {
    std::cout << "The document could not be written to file" << std::endl;
    app->Close(doc);
  }
  std::cout << "Document saved in STEP format." << std::endl;

   // Write doc out to file
  PCDM_StoreStatus sstatus = app->SaveAs(doc, save_doc_file_name);
  //
  if ( sstatus != PCDM_SS_OK )
  {
    app->Close(doc);
    std::cout << "Cannot write XCAF document." << std::endl;
    return 1;
  }
  std::cout << "Wrote XCAF document in .xbf format." << std::endl;

  app->Close(doc);
  return 0;
}
 

natalia

Moderator
Staff member
Hello, DougB

I reproduced your case, XBF is indeed not opened with this code. Here, if we save the initial step document in XBF, it's opened correctly. (Not in CADAssistant, but if we call Open of the file in own application).
I need to investigate it in more details now.

Regarding wrong naming, the next row sets a new name that is shown on the attached picture:
C++:
TDataStd_Name::Set(cLabel, TCollection_ExtendedString("step_data"));
Here, we need to get the name value from the initial document. I'll try doing this and write about the result.

Will keep you informed, Natalia
 
Top