Now that we have our system configure, the test beds working and our IDE project set up, we are now ready to embed our OpenCascade Window into our wxWidget Window.
There’s a handful of tutorials on ways to do this, but they’re either out of date by over a decade or are missing some important points that cause segmentation faults, X11 Bad Window errors, or simply just weird crashes.
Creating the Control
The initial tests kept OpenCascade as a separate entity being parented into a wxPanel during creation of the window at runtime. This was great for a first try, but it becomes tricky too get automatic refreshing and resizing working.
Includes
There’s a large list of includes needed even for a simple file. Instead of adding dozens of lines to this post, you can go over too here and see the required #includes.
Global Variables
The set of global variables needed are shown and described below:
//////////////////////////// VARIABLES ///////////////////////////////The OCE WindowHandle(Xw_Window) wind;//The Panel which holds the OCE WindowwxPanel* ctrl;//Used for connecting too the Graphics CardHandle(Aspect_DisplayConnection) aDisplayConnection;//OpenCascade Context (Used for adding Shapes)Handle(AIS_InteractiveContext) myContext;//The OCE ViewHandle(V3d_View) mView;Handle(V3d_Viewer) myViewer;//////////////////////////// FUNCTIONS ///////////////////////////////A Helper Function to create theHandle(V3d_Viewer) Viewer (wxWindow* panel,const Standard_ExtString theName,const Standard_CString theDomain,const Standard_Real theViewSize,const V3d_TypeOfOrientation theViewProj,const Standard_Boolean theComputedMode,const Standard_Boolean theDefaultComputedMode );//Catched the resizing of the wxScrolledWindowvoid OnSize(wxSizeEvent& event);//Redraw the Controlvoid OnIdle(wxIdleEvent& event);
view raw wxOCE Global Variables.h hosted with ❤ by GitHub
Constructor
We then need to fill in the constructor. Note the Gist shown below is only an exert of a the full constructor.
class wxOCEWindow: public wxScrolledWindow{public:wxOCEWindow(wxWindow *parent, const wxPoint& pos, const wxSize& size, long style);...TCollection_ExtendedString a3DName ("Visu3D");//Create the Global wxPanel too hold the wxOCE Controlctrl = new wxPanel(this);//Capture the wxScrollWindows events and send them to the wxPanelctrl->SetEventHandler(this);//Create the ViewermyViewer = Viewer (ctrl, a3DName.ToExtString(), "", 1000.0, V3d_XposYnegZpos, Standard_True, Standard_True);//Set LightingmyViewer->SetDefaultLights();myViewer->SetLightOn();//Create ContextmyContext = new AIS_InteractiveContext (myViewer);...}
view raw wxOCE Constructor Example.cpp hosted with ❤ by GitHub
The line which we need too dive into now is the Viewer(…) method. This is where the X11 window needs grabs the wxPanel Widget handle and creates a wrapper window around it.
Creating a OCC Window
But before we wrap any windows, first we want to make sure we can create one to make sure everything’s working.
This first gist shows creating the OCC window separate from wxWindow.
//Get wxPanel handle.GtkWidget* widget = panel->GetHandle();//Dig through too get x11 handleWindow w = gdk_x11_drawable_get_xid(gtk_widget_get_window(panel->GetHandle()));//Current line crashes with Bad Window error//wind = new Xw_Window(aDisplayConnection, w);//Currently possible too only create seperate Window for Open CascadeHandle(Xw_Window) wind = new Xw_Window(aDisplayConnection,"test", 20,20,400,400);//Set and Map OpenCascade Window too viewmView->SetWindow(wind);if(!wind->IsMapped())wind->Map();
view raw X11 Embedding - Original Not Working.cpp hosted with ❤ by GitHub
Embeding the OCC Window in wxWidgets
Too embed the OCC window into awxPanel control, First the Handle is needed of the wxPanel.
GtkWidget\* widget = panel->GetHandle();
A major issue is when the OCC window is created, the GTK window hasn’t always been fully realized yet and this causes random Segment Faults and crashes. To get around this, we run the next two lines.
gtk\_widget\_realize( widget );
gtk\_widget\_set\_double\_buffered(widget, 0);
The next line is too access the window from the Handle that was retrieved from the wxPanel.
Window wid = GDK\_WINDOW\_XWINDOW( widget->window );
Note that the Window object is a X11 window.
From there, we can now use the second Xw\_Window constructor which creates the OCC window wrapped around a previous control.
wind = new Xw\_Window(aDisplayConnection, wid);
The final thing too do is too tell OCC which Xw\_Window too use and too map the window.
//Set Window
mView->SetWindow(wind);
if(!wind->IsMapped()) wind->Map();
When it’s all put together, the Viewer(..) method looks like it does below.
Handle(V3d\_Viewer) wxOCEWindow::Viewer (wxWindow\* panel,
const Standard\_ExtString theName,
const Standard\_CString theDomain,
const Standard\_Real theViewSize,
const V3d\_TypeOfOrientation theViewProj,
const Standard\_Boolean theComputedMode,
const Standard\_Boolean theDefaultComputedMode )
{
static Handle(OpenGl\_GraphicDriver) aGraphicDriver;
aDisplayConnection = new Aspect\_DisplayConnection();
if (aGraphicDriver.IsNull())
{
aDisplayConnection = new Aspect\_DisplayConnection ();
aGraphicDriver = new OpenGl\_GraphicDriver (aDisplayConnection);
}
Handle(V3d\_Viewer) v = new V3d\_Viewer (aGraphicDriver,
theName,
theDomain,
theViewSize,
theViewProj,
Quantity\_NOC\_GRAY30,
V3d\_ZBUFFER,
V3d\_GOURAUD,
V3d\_WAIT,
theComputedMode,
theDefaultComputedMode,
V3d\_TEX\_NONE);
//Create the View
mView = v->CreateView();
//This works in GTK 1,0 apperantly but breaks in GTK 2.0...need a better solution
//GdkWindow\* draw\_window = GTK\_PIZZA(this)->bin\_window;
//First Retreive the GTK Widget (This is handled differently in MSW and MAC)
GtkWidget\* widget = panel->GetHandle();
// Mandatory. Otherwise, a segfault happens. (Learned this one the hard way)
gtk\_widget\_realize( widget );
//Old Habbts I guess...
gtk\_widget\_set\_double\_buffered(widget, 1);
//Now retireve the X11 Window
Window wid = GDK\_WINDOW\_XWINDOW( widget->window );
//The above line can be done with the following as well.
// Window wid = gdk\_x11\_drawable\_get\_xid(gtk\_widget\_get\_window(panel->GetHandle()));
XSync(GDK\_WINDOW\_XDISPLAY(widget->window), False);
//Now create the Xw window as a wrapper of the window we've so tirelessly had to go and retreive.
wind = new Xw\_Window(aDisplayConnection, wid);
//Un Comment this line below to create a seperate window (defeats the purpose of this hole exercise though)
//Handle(Xw\_Window) wind = new Xw\_Window(aDisplayConnection,"test", 20,20,400,400);
//Now set the Window to the View
mView->SetWindow(wind);
//Don't forget to Map the Window
if(!wind->IsMapped()) wind->Map();
//Now Draw the Scene
mView->Redraw();
return v;
}
view raw View Creation.cpp hosted with ❤ by GitHub
Calling Draw and OnSize
With all that down, all we need to do now is handle resizing and drawing. It’s simply down by setting up the following methods.
void wxOCEWindow::OnIdle(wxIdleEvent& event)
{
mView->MustBeResized(); // if the user changes the window size
mView->Redraw();
}
void wxOCEWindow::OnSize(wxSizeEvent& event)
{
int width, height;
this->GetClientSize(&width, &height);
ctrl->SetSize(width, height);
if (!mView.IsNull())
mView->MustBeResized(); // if the user changes the window size
// this will re-center the Cascade View
mView->Redraw();
Refresh();
Update();
event.Skip();
}
view raw wxOCC Methods.cpp hosted with ❤ by GitHub
And that’s that.
Actually Rendering something!
I’ll cover opening model files in another post, but for now, It’s simple too Create the ‘Demo Bottle’ from the DRAWEXE test bed. Simply add the following method to your class and call it at the end of the constructor after everything else.
void wxOCEWindow::CreateBottle()
{
TopoDS\_Shape aBottle=MakeBottle(150,210,90);
Handle(AIS\_Shape) AISBottle=new AIS\_Shape(aBottle);
myContext->SetMaterial(AISBottle,Graphic3d\_NOM\_GOLD);
myContext->SetDisplayMode(AISBottle,1,Standard\_False);
myContext->Display(AISBottle, Standard\_False);
DoZoomFit();
}
TopoDS\_Shape wxOCEWindow::MakeBottle(const Standard\_Real myWidth, const Standard\_Real myHeight, const Standard\_Real myThickness)
{
// Profile : Define Support Points
gp\_Pnt aPnt1(-myWidth / 2., 0, 0);
gp\_Pnt aPnt2(-myWidth / 2., -myThickness / 4., 0);
gp\_Pnt aPnt3(0, -myThickness / 2., 0);
gp\_Pnt aPnt4(myWidth / 2., -myThickness / 4., 0);
gp\_Pnt aPnt5(myWidth / 2., 0, 0);
// Profile : Define the Geometry
Handle(Geom\_TrimmedCurve) anArcOfCircle = GC\_MakeArcOfCircle(aPnt2,aPnt3,aPnt4);
Handle(Geom\_TrimmedCurve) aSegment1 = GC\_MakeSegment(aPnt1, aPnt2);
Handle(Geom\_TrimmedCurve) aSegment2 = GC\_MakeSegment(aPnt4, aPnt5);
// Profile : Define the Topology
TopoDS\_Edge anEdge1 = BRepBuilderAPI\_MakeEdge(aSegment1);
TopoDS\_Edge anEdge2 = BRepBuilderAPI\_MakeEdge(anArcOfCircle);
TopoDS\_Edge anEdge3 = BRepBuilderAPI\_MakeEdge(aSegment2);
TopoDS\_Wire aWire = BRepBuilderAPI\_MakeWire(anEdge1, anEdge2, anEdge3);
// Complete Profile
gp\_Ax1 xAxis = gp::OX();
gp\_Trsf aTrsf;
aTrsf.SetMirror(xAxis);
BRepBuilderAPI\_Transform aBRepTrsf(aWire, aTrsf);
TopoDS\_Shape aMirroredShape = aBRepTrsf.Shape();
TopoDS\_Wire aMirroredWire = TopoDS::Wire(aMirroredShape);
BRepBuilderAPI\_MakeWire mkWire;
mkWire.Add(aWire);
mkWire.Add(aMirroredWire);
TopoDS\_Wire myWireProfile = mkWire.Wire();
// Body : Prism the Profile
TopoDS\_Face myFaceProfile = BRepBuilderAPI\_MakeFace(myWireProfile);
gp\_Vec aPrismVec(0, 0, myHeight);
TopoDS\_Shape myBody = BRepPrimAPI\_MakePrism(myFaceProfile, aPrismVec);
// Body : Apply Fillets
BRepFilletAPI\_MakeFillet mkFillet(myBody);
TopExp\_Explorer anEdgeExplorer(myBody, TopAbs\_EDGE);
while(anEdgeExplorer.More()){
TopoDS\_Edge anEdge = TopoDS::Edge(anEdgeExplorer.Current());
//Add edge to fillet algorithm
mkFillet.Add(myThickness / 12., anEdge);
anEdgeExplorer.Next();
}
myBody = mkFillet.Shape();
// Body : Add the Neck
gp\_Pnt neckLocation(0, 0, myHeight);
gp\_Dir neckAxis = gp::DZ();
gp\_Ax2 neckAx2(neckLocation, neckAxis);
Standard\_Real myNeckRadius = myThickness / 4.;
Standard\_Real myNeckHeight = myHeight / 10.;
BRepPrimAPI\_MakeCylinder MKCylinder(neckAx2, myNeckRadius, myNeckHeight);
TopoDS\_Shape myNeck = MKCylinder.Shape();
myBody = BRepAlgoAPI\_Fuse(myBody, myNeck);
// Body : Create a Hollowed Solid
TopoDS\_Face faceToRemove;
Standard\_Real zMax = -1;
for(TopExp\_Explorer aFaceExplorer(myBody, TopAbs\_FACE); aFaceExplorer.More(); aFaceExplorer.Next()){
TopoDS\_Face aFace = TopoDS::Face(aFaceExplorer.Current());
// Check if <aFace> is the top face of the bottle's neck
Handle(Geom\_Surface) aSurface = BRep\_Tool::Surface(aFace);
if(aSurface->DynamicType() == STANDARD\_TYPE(Geom\_Plane)){
Handle(Geom\_Plane) aPlane = Handle(Geom\_Plane)::DownCast(aSurface);
gp\_Pnt aPnt = aPlane->Location();
Standard\_Real aZ = aPnt.Z();
if(aZ > zMax){
zMax = aZ;
faceToRemove = aFace;
}
}
}
TopTools\_ListOfShape facesToRemove;
facesToRemove.Append(faceToRemove);
myBody = BRepOffsetAPI\_MakeThickSolid(myBody, facesToRemove, -myThickness / 50, 1.e-3);
// Threading : Create Surfaces
Handle(Geom\_CylindricalSurface) aCyl1 = new Geom\_CylindricalSurface(neckAx2, myNeckRadius \* 0.99);
Handle(Geom\_CylindricalSurface) aCyl2 = new Geom\_CylindricalSurface(neckAx2, myNeckRadius \* 1.05);
// Threading : Define 2D Curves
gp\_Pnt2d aPnt(2. \* M\_PI, myNeckHeight / 2.);
gp\_Dir2d aDir(2. \* M\_PI, myNeckHeight / 4.);
gp\_Ax2d anAx2d(aPnt, aDir);
Standard\_Real aMajor = 2. \* M\_PI;
Standard\_Real aMinor = myNeckHeight / 10;
Handle(Geom2d\_Ellipse) anEllipse1 = new Geom2d\_Ellipse(anAx2d, aMajor, aMinor);
Handle(Geom2d\_Ellipse) anEllipse2 = new Geom2d\_Ellipse(anAx2d, aMajor, aMinor / 4);
Handle(Geom2d\_TrimmedCurve) anArc1 = new Geom2d\_TrimmedCurve(anEllipse1, 0, M\_PI);
Handle(Geom2d\_TrimmedCurve) anArc2 = new Geom2d\_TrimmedCurve(anEllipse2, 0, M\_PI);
gp\_Pnt2d anEllipsePnt1 = anEllipse1->Value(0);
gp\_Pnt2d anEllipsePnt2 = anEllipse1->Value(M\_PI);
Handle(Geom2d\_TrimmedCurve) aSegment = GCE2d\_MakeSegment(anEllipsePnt1, anEllipsePnt2);
// Threading : Build Edges and Wires
TopoDS\_Edge anEdge1OnSurf1 = BRepBuilderAPI\_MakeEdge(anArc1, aCyl1);
TopoDS\_Edge anEdge2OnSurf1 = BRepBuilderAPI\_MakeEdge(aSegment, aCyl1);
TopoDS\_Edge anEdge1OnSurf2 = BRepBuilderAPI\_MakeEdge(anArc2, aCyl2);
TopoDS\_Edge anEdge2OnSurf2 = BRepBuilderAPI\_MakeEdge(aSegment, aCyl2);
TopoDS\_Wire threadingWire1 = BRepBuilderAPI\_MakeWire(anEdge1OnSurf1, anEdge2OnSurf1);
TopoDS\_Wire threadingWire2 = BRepBuilderAPI\_MakeWire(anEdge1OnSurf2, anEdge2OnSurf2);
BRepLib::BuildCurves3d(threadingWire1);
BRepLib::BuildCurves3d(threadingWire2);
// Create Threading
BRepOffsetAPI\_ThruSections aTool(Standard\_True);
aTool.AddWire(threadingWire1);
aTool.AddWire(threadingWire2);
aTool.CheckCompatibility(Standard\_False);
TopoDS\_Shape myThreading = aTool.Shape();
// Building the Resulting Compound
TopoDS\_Compound aRes;
BRep\_Builder aBuilder;
aBuilder.MakeCompound (aRes);
aBuilder.Add (aRes, myBody);
aBuilder.Add (aRes, myThreading);
return aRes;
}
view raw wxOCC_CreateBottle.cpp hosted with ❤ by GitHub
Putting everything together, you should get something looking like the following:

But what’s that DoZoomFit(); method in that last Gist??? I’ll show that and more Input controls and orientation controls in the next Tutorial.