From ba9548c55b89f8c185f5ff1e0d71575a3c7f3d60 Mon Sep 17 00:00:00 2001 From: kgv Date: Thu, 13 Jan 2022 15:51:40 +0300 Subject: [PATCH] 0032668: Documentation - add tutorial for creating a custom AIS Interactive Object --- dox/FILES_HTML.txt | 1 + dox/samples/ais_object.md | 911 ++++++++++++++++++ .../images/ais_object_step1_bndbox.png | Bin 0 -> 3169 bytes .../images/ais_object_step1_highlight.png | Bin 0 -> 14313 bytes .../images/ais_object_step1_shaded.png | Bin 0 -> 3308 bytes .../images/ais_object_step1_shaded_wf.png | Bin 0 -> 5330 bytes .../images/ais_object_step2_segments.png | Bin 0 -> 3706 bytes .../images/ais_object_step3_quadrics_10.png | Bin 0 -> 5872 bytes .../images/ais_object_step3_quadrics_25.png | Bin 0 -> 5873 bytes .../images/ais_object_step3_quadrics_disk.png | Bin 0 -> 4652 bytes .../images/ais_object_step3_quadrics_fin.png | Bin 0 -> 4625 bytes .../images/ais_object_step4_highlight1.png | Bin 0 -> 3734 bytes .../images/ais_object_step4_highlight2.png | Bin 0 -> 6179 bytes .../images/ais_object_step4_highlight3.png | Bin 0 -> 4024 bytes dox/samples/samples.md | 3 + src/QADraw/FILES | 2 +- src/QADraw/QADraw.cxx | 28 +- src/QADraw/QADraw.hxx | 38 +- src/QADraw/QADraw_Additional.cxx | 29 - src/QADraw/QADraw_Tutorials.cxx | 349 +++++++ tests/demo/samples/aisobject | 14 + 21 files changed, 1305 insertions(+), 70 deletions(-) create mode 100644 dox/samples/ais_object.md create mode 100644 dox/samples/images/ais_object_step1_bndbox.png create mode 100644 dox/samples/images/ais_object_step1_highlight.png create mode 100644 dox/samples/images/ais_object_step1_shaded.png create mode 100644 dox/samples/images/ais_object_step1_shaded_wf.png create mode 100644 dox/samples/images/ais_object_step2_segments.png create mode 100644 dox/samples/images/ais_object_step3_quadrics_10.png create mode 100644 dox/samples/images/ais_object_step3_quadrics_25.png create mode 100644 dox/samples/images/ais_object_step3_quadrics_disk.png create mode 100644 dox/samples/images/ais_object_step3_quadrics_fin.png create mode 100644 dox/samples/images/ais_object_step4_highlight1.png create mode 100644 dox/samples/images/ais_object_step4_highlight2.png create mode 100644 dox/samples/images/ais_object_step4_highlight3.png delete mode 100644 src/QADraw/QADraw_Additional.cxx create mode 100644 src/QADraw/QADraw_Tutorials.cxx create mode 100644 tests/demo/samples/aisobject diff --git a/dox/FILES_HTML.txt b/dox/FILES_HTML.txt index d8c8a633a8..487bef712a 100644 --- a/dox/FILES_HTML.txt +++ b/dox/FILES_HTML.txt @@ -22,6 +22,7 @@ samples/samples.md samples/ocaf.md samples/ocaf_func.md samples/draw_scripts.md +samples/ais_object.md samples/novice_guide.md tutorial/tutorial.md diff --git a/dox/samples/ais_object.md b/dox/samples/ais_object.md new file mode 100644 index 0000000000..cf5a17e7c5 --- /dev/null +++ b/dox/samples/ais_object.md @@ -0,0 +1,911 @@ +AIS: Custom Presentation {#tutorials__ais_object} +======== + +@tableofcontents + +@section intro Getting Started + +OCCT provides a strong set of built-in Interactive Objects for rapid application development, +but the real power and flexibility of **Application Interactive Services** (@c AIS) could be revealed by subclassing and implementing custom presentations. +In this tutorial we will focus on the development of a custom @c AIS_InteractiveObject and show the basics step by step. + +Let's start from the very beginning and try subclassing @c AIS_InteractiveObject object: + +~~~~{.cpp} +class MyAisObject : public AIS_InteractiveObject +{ + DEFINE_STANDARD_RTTI_INLINE(MyAisObject, AIS_InteractiveObject) +public: + MyAisObject() {} +public: + virtual void Compute (const Handle(PrsMgr_PresentationManager)& thePrsMgr, + const Handle(Prs3d_Presentation)& thePrs, + const Standard_Integer theMode) override {} + + virtual void ComputeSelection (const Handle(SelectMgr_Selection)& theSel, + const Standard_Integer theMode) override {} + + virtual bool AcceptDisplayMode (const Standard_Integer theMode) const override + { return true; } +}; +~~~~ + +@c DEFINE_STANDARD_RTTI_INLINE() macro will register the new class within the OCCT Run-Time Type Information (RTTI) system. +This step is optional (you may skip it if you are not going to use methods like @c Standard_Transient::DynamicType() in application code), but it is a common practice while subclassing OCCT classes. + +The @c AIS_InteractiveObject interface defines only a couple of pure virtual methods - @c @::Compute() defining an object presentation and @c @::ComputeSelection() defining a selectable (pickable) volume. +Selection and presentation are two independent mechanisms in **AIS**. Presentation rendering is done with help of OpenGL or a similar low-level graphics library, while selection doesn't depend on a graphic driver at all. +Providing an empty implementation of these two methods would be enough for adding the object to @c AIS_InteractiveContext (@c @::Display()), but obviously nothing will appear on the screen. + +@section prs_builders Presentation builders + +To go ahead, we need to define some presentation of our object. +OCCT provides a set of presentation building tools for common elements like arrows, shapes, boxes, etc. +These tools could be found within @c Prs3d, @c StdPrs and @c DsgPrs packages: + +- **Prs3d** + provides builders for simple geometric elements. + - @c Prs3d_Arrow, @c Prs3d_BndBox, @c Prs3d_Point, @c Prs3d_Text, @c Prs3d_ToolCylinder, @c Prs3d_ToolDisk, @c Prs3d_ToolSector, @c Prs3d_ToolSphere, @c Prs3d_ToolTorus +- **StdPrs** + provides builders for analytical geometry and B-Rep shapes (@c TopoDS_Shape). + - @c StdPrs_WFShape, @c StdPrs_ShadedShape, @c StdPrs_BRepTextBuilder +- **DsgPrs** + provides builders for datums, dimensions and relations. + +Presentation builders are reusable bricks for constructing @c AIS objects. +Standard OCCT interactive objects highly rely on them, so that you may easily replicate @c AIS_Shape presentation for displaying a shape with just a couple of lines calling @c StdPrs_ShadedShape: + +~~~~{.cpp} +void MyAisObject::Compute (const Handle(PrsMgr_PresentationManager)& thePrsMgr, + const Handle(Prs3d_Presentation)& thePrs, + const Standard_Integer theMode) +{ + TopoDS_Shape aShape = BRepPrimAPI_MakeCylinder (100.0, 100.0); + StdPrs_ShadedShape::Add (thePrs, aShape, myDrawer); +} +... +Handle(AIS_InteractiveContext) theCtx; +Handle(MyAisObject) aPrs = new MyAisObject(); +theCtx->Display (aPrs, true); +~~~~ + +@figure{ais_object_step1_shaded.png,"@c StdPrs_ShadedShape presentation builder.",409} height=409px + +@c PrsMgr_PresentableObject::Compute() method takes three arguments: +- **Presentation Manager** (@c PrsMgr_PresentationManager). + Rarely used parameter, but might be necessary for some advanced use cases. +- **Presentation** (@c Prs3d_Presentation or @c Graphic3d_Structure). + Defines the structure to fill in with presentation elements. +- **Display Mode** (integer number). + Specifies the display mode to compute. + **0** is a default display mode, if not overridden by @c AIS_InteractiveObject::SetDisplayMode() or by @c AIS_InteractiveContext::Display(). + +For each supported display mode, the **Presentation Manager** creates a dedicated @c Prs3d_Presentation and stores it within the object itself as a list of presentations @c PrsMgr_PresentableObject::Presentations(). +It is a good practice to reject unsupported display modes within @c @::Compute() method: + +~~~~{.cpp} +void MyAisObject::Compute (const Handle(PrsMgr_PresentationManager)& thePrsMgr, + const Handle(Prs3d_Presentation)& thePrs, + const Standard_Integer theMode) +{ + if (theMode != 0) { return; } // reject non-zero display modes + + TopoDS_Shape aShape = BRepPrimAPI_MakeCylinder (100.0, 100.0); + StdPrs_ShadedShape::Add (thePrs, aShape, myDrawer); +} +~~~~ + +This wouldn't, however, prevent application from displaying the object with another display mode like this: + +~~~~{.cpp} +Handle(AIS_InteractiveContext) theCtx; +Handle(MyAisObject) aPrs = new MyAisObject(); +theCtx->Display (aPrs, 100, -1, true); +~~~~ + +The code above will display @c MyAisObject with display mode equal to 100, and after @c @::Compute() modifications nothing will be displayed on the screen. +@c AIS will still create a presentation with specified display mode, but it will be empty - method @c @::AcceptDisplayMode() could be overridden to disallow even creation of an empty presentation: + +~~~~{.cpp} +bool MyAisObject::AcceptDisplayMode (const Standard_Integer theMode) const +{ + return theMode == 0; // reject non-zero display modes +} +~~~~ + +@c AIS_InteractiveContext::Display() checks if requested display mode is actually supported by the object, and uses default display mode (_**0**_) if it is not. +@c StdPrs_ShadedShape prepares a shaded (triangulated) presentation of a shape, while @c StdPrs_WFShape creates a wireframe presentation with B-Rep wire boundaries: + +~~~~{.cpp} +void MyAisObject::Compute (const Handle(PrsMgr_PresentationManager)& thePrsMgr, + const Handle(Prs3d_Presentation)& thePrs, + const Standard_Integer theMode) +{ + if (!AcceptDisplayMode (theMode)) { return; } + + TopoDS_Shape aShape = BRepPrimAPI_MakeCylinder (100.0, 100.0); + StdPrs_ShadedShape::Add (thePrs, aShape, myDrawer); // add shading + StdPrs_WFShape::Add (thePrs, aShape, myDrawer); // add wireframe +} +~~~~ + +@figure{ais_object_step1_shaded_wf.png,"Result of @c StdPrs_ShadedShape + @c StdPrs_WFShape presentation builders.",409} height=409px + +Presentation builders take the @c Prs3d_Drawer object defining various attributes - material of shaded shape, number of isolines in wireframe mode, tessellation quality, line colors and many others. +@c PrsMgr_PresentableObject defines @c myDrawer property with default attributes. +@c StdPrs makes it easy to display topological shapes. +With the help of @c Prs3d tools we may display elements like arrows, boxes or text labels. +Let's extend our presentation with a second **display mode 1** showing a bounding box using @c Prs3d_BndBox builder: + +~~~~{.cpp} +bool MyAisObject::AcceptDisplayMode (const Standard_Integer theMode) const +{ + return theMode == 0 || theMode == 1; +} +void MyAisObject::Compute (const Handle(PrsMgr_PresentationManager)& thePrsMgr, + const Handle(Prs3d_Presentation)& thePrs, + const Standard_Integer theMode) +{ + TopoDS_Shape aShape = BRepPrimAPI_MakeCylinder (100.0, 100.0); + if (theMode == 0) + { + StdPrs_ShadedShape::Add (thePrs, aShape, myDrawer); + StdPrs_WFShape::Add (thePrs, aShape, myDrawer); // add wireframe + } + else if (theMode == 1) + { + Bnd_Box aBox; + BRepBndLib::Add (aShape, aBox); + Prs3d_BndBox::Add (thePrs, aBox, myDrawer); + } +} +~~~~ + +Now, displaying an object with **display mode 1** will show a box: + +~~~~{.cpp} +Handle(AIS_InteractiveContext) theCtx; +Handle(MyAisObject) aPrs = new MyAisObject(); +theCtx->Display (aPrs, 1, 0, true); +~~~~ + +@figure{ais_object_step1_bndbox.png,"@c Prs3d_BndBox presentation builder.",409} height=409px + +@c AIS disallows activating multiple display modes at the same time, so that these presentation modes should be alternatives to each other. +But @c AIS may use non-active display mode for highlighting purposes - like wireframe (@c AIS_Wireframe) presentation displayed on top of shaded (@c AIS_Shaded) presentation for selected @c AIS_Shape objects. + +Let's define a dedicated enumeration for display modes supported by our interactive object and setup the 1st (@c MyDispMode_Highlight) display mode for highlighting with help of @c PrsMgr_PresentableObject::SetHilightMode(): + +~~~~{.cpp} +class MyAisObject : public AIS_InteractiveObject +{ +public: + enum MyDispMode { MyDispMode_Main = 0, MyDispMode_Highlight = 1 }; + +... + +MyAisObject::MyAisObject() +{ + SetDisplayMode (MyDispMode_Main); // main (active) display mode + SetHilightMode (MyDispMode_Highlight); // auxiliary (highlighting) mode +} + +... + +Handle(AIS_InteractiveContext) theCtx; +Handle(MyAisObject) aPrs = new MyAisObject(); +theCtx->Display (aPrs, MyAisObject::MyDispMode_Main, 0, false); +theCtx->HilightWithColor (aPrs, aPrs->HilightAttributes(), false); +theCtx->CurrentViewer()->Redraw(); +~~~~ + +@figure{ais_object_step1_highlight.png,"Highlighting by color (left) and highlighting by another display mode (right).",818} height=409px + +In this particular use case we've used the method @c AIS_InteractiveContext::HilightWithColor() instead of @c @::SetSelected() - just because our object is not selectable yet and @c @::SetSelected() wouldn't work. +Highlighted presentation appears on the screen with modulated color (see left screenshot above). +Using a dedicated display mode for highlighting (right screenshot above) allows customizing presentation in selected / highlighted states. + +@section prim_arrays Primitive arrays + +@c Prs3d_Presentation might be filled in by the following **primitives**: +- **Triangles** + - @c Graphic3d_ArrayOfTriangles + - @c Graphic3d_ArrayOfTriangleFans + - @c Graphic3d_ArrayOfTriangleStrips +- **Lines** + - @c Graphic3d_ArrayOfSegments + - @c Graphic3d_ArrayOfPolylines +- **Points** or **Markers** + - @c Graphic3d_ArrayOfPoints + +This triplet of primitives is what graphics hardware is capable of rendering, so that it could be transferred directly to low-level graphics libraries in the form of *Vertex Buffer Objects* (VBO). +Each **primitive array** consists of an array of vertex attributes (_**position**, **normal**, **texture coordinates**, **vertex colors**_, etc.) and optional **array of indices**. +The latter one avoids duplicating vertices shared between connected elements (triangles, polylines) in attributes array. + +@c Graphic3d_ArrayOfPrimitives and it's subclasses provide a convenient interface for filling in primitive arrays: +- Constructor takes a number of vertices, number of edges (indices) and a bitmask of optional vertex attributes. +- @c Graphic3d_ArrayOfPrimitives::AddVertex() appends a vertex with specified attributes to the end of the array (within the range specified at construction time). +- @c Graphic3d_ArrayOfPrimitives::AddEdges() appends indices, starting with 1. + Each line segment is defined by two consequential edges, each triangle is defined by three consequential edges. + +Let's extend our sample and display a cylinder section contour defined by array of indexed segments (e.g. a polyline of four vertices): + +~~~~{.cpp} +void MyAisObject::Compute (const Handle(PrsMgr_PresentationManager)& thePrsMgr, + const Handle(Prs3d_Presentation)& thePrs, + const Standard_Integer theMode) +{ + const double aRadius = 100.0, aHeight = 100.0; + TopoDS_Shape aShape = BRepPrimAPI_MakeCylinder (aRadius, aHeight); + if (theMode == MyDispMode_Main) + { + StdPrs_ShadedShape::Add (thePrs, aShape, myDrawer); + //StdPrs_WFShape::Add (thePrs, aShape, myDrawer); + Handle(Graphic3d_ArrayOfSegments) aSegs = new Graphic3d_ArrayOfSegments (4, 4 * 2, Graphic3d_ArrayFlags_None); + aSegs->AddVertex (gp_Pnt (0.0, -aRadius, 0.0)); + aSegs->AddVertex (gp_Pnt (0.0, -aRadius, aHeight)); + aSegs->AddVertex (gp_Pnt (0.0, aRadius, aHeight)); + aSegs->AddVertex (gp_Pnt (0.0, aRadius, 0.0)); + aSegs->AddEdges (1, 2); + aSegs->AddEdges (2, 3); + aSegs->AddEdges (3, 4); + aSegs->AddEdges (4, 1); + Handle(Graphic3d_Group) aGroupSegs = thePrs->NewGroup(); + aGroupSegs->SetGroupPrimitivesAspect (myDrawer->WireAspect()->Aspect()); + aGroupSegs->AddPrimitiveArray (aSegs); + } + else if (theMode == MyDispMode_Highlight) { ... } +} +~~~~ + +@figure{ais_object_step2_segments.png,"Displaying @c Graphic3d_ArrayOfSegments.",409} height=409px + +The process is quite straightforward: +- Create a new @c Graphic3d_Group using @c Prs3d_Presentation::NewGroup(); +- Specify presentation aspects using @c Graphic3d_Group::SetGroupPrimitivesAspect(); +- Create and add an array of primitives using @c Graphic3d_Group::AddPrimitiveArray(). + +Standard presentation builders like @c StdPrs_ShadedShape / @c StdPrs_WFShape internally do exactly the same thing - a tessellated representation of a shape is added to presentation in form of triangles (shaded), +line segments (wireframe and free edges) and markers (free shape vertices). + +A single @c Graphic3d_Group normally defines just a single primitive array, but it is technically possible adding more arrays to the same group @c Graphic3d_Group::AddPrimitiveArray() +and with different aspects @c Graphic3d_Group::SetPrimitivesAspect(), which might be considered in advanced scenarios. + +Method @c Graphic3d_Group::AddText() allows adding text labels to a presentation. +Internally, text labels are rendered as an array of textured triangles using texture atlas created from a font, but this complex logic is hidden from the user. + +@section prim_aspects Primitive aspects + +@c Graphic3d_Aspects is a class defining **display properties** of a primitive array (@c Graphic3d_Group::SetGroupPrimitivesAspect()) - +_**material**, **shading model**, **color**, **texture maps**, **blending mode**, **line width**_ and others. + +There are also subclasses @c Graphic3d_AspectFillArea3d (triangles), @c Graphic3d_AspectLine3d (lines), @c Graphic3d_AspectMarker3d (markers) +and @c Graphic3d_AspectText3d (text labels) defined as specializations for a specific primitive array type. +These subclasses exist for historical reasons and are treated by renderers in exactly the same way. + +It is technically possible to create transient aspects directly within @c @::Compute() method like this: + +~~~~{.cpp} +void MyAisObject::Compute (const Handle(PrsMgr_PresentationManager)& thePrsMgr, + const Handle(Prs3d_Presentation)& thePrs, + const Standard_Integer theMode) +{ + Handle(Graphic3d_Aspects) anAspects = new Graphic3d_Aspects(); + anAspects->SetShadingModel (Graphic3d_TypeOfShadingModel_Unlit); + anAspects->SetColor (Quantity_NOC_RED); + Handle(Graphic3d_Group) aGroup = thePrs->NewGroup(); + aGroup->SetGroupPrimitivesAspect (anAspects); + ... +} +~~~~ + +While this code would work as expected, but prevents further dynamic updates of presentation aspects without recomputing entire presentation. +Instead, it is preferred taking attributes from @c PrsMgr_PresentableObject::myDrawer / @c @::Attributes() or storing custom attributes as class fields. +@c Prs3d_Drawer defines a set of attributes used by @c AIS presentation builders, but the same parameters might be used by a custom builder as well. + +It is also preferred preallocating attributes in the class constructor. +This would allow changing attributes without recomputing the entire presentation - just by calling @c PrsMgr_PresentableObject::SynchronizeAspects() after modifications. +Our custom object uses @c myDrawer->ShadingAspect() and @c myDrawer->WireAspect() aspects, so let's initialize them explicitly - assign silver material for shading and green color to line segments: + +~~~~{.cpp} +MyAisObject::MyAisObject() +{ + SetHilightMode (MyDispMode_Highlight); + myDrawer->SetupOwnShadingAspect(); + myDrawer->ShadingAspect()->SetMaterial (Graphic3d_NameOfMaterial_Silver); + myDrawer->SetWireAspect (new Prs3d_LineAspect (Quantity_NOC_GREEN, Aspect_TOL_SOLID, 2.0)); +} +~~~~ + +@section quadric_builders Quadric builders + +Previously, we've used @c StdPrs_ShadedShape for displaying cylinder geometry. +The @c Prs3d package provides a simpler way for displaying geometry like cylinders, spheres and toruses - based on the @c Prs3d_ToolQuadric interface. +This interface allows bypassing creation of a complex B-Rep (@c TopoDS_Shape) definition of a simple geometry, and to avoid using general-purpose tessellators like @c BRepMesh. + +> This difference could be negligible for a small number of such objects, but might become considerable for larger amounts. +> The B-Rep definition of a valid cylinder includes 2 unique @c TopoDS_Vertex, 3 @c TopoDS_Edge, 3 @c TopoDS_Wire, 3 @c TopoDS_Face, 1 @c TopoDS_Shell and 1 @c TopoDS_Solid. +> Internally each @c TopoDS_Edge also defines curves (@c Geom_Curve as well as 2D parametric @c Geom2d_Curve) and each @c TopoDS_Face defines analytical surface (@c Geom_Surface). +> Meshing such geometry with the help of @c BRepMesh is much more complicated than one may think. +> A plenty of data structures (memory!) and computations (time!) for displaying a geometry that could be triangulated by a simple for loop. + +@c Prs3d_ToolQuadric solves this problem by creating a triangulation for such kinds of shapes in a straight-forward way. +Let's try using @c Prs3d_ToolCylinder in our sample: + +~~~~{.cpp} +void MyAisObject::Compute (const Handle(PrsMgr_PresentationManager)& thePrsMgr, + const Handle(Prs3d_Presentation)& thePrs, + const Standard_Integer theMode) +{ + const double aRadius = 100.0, aHeight = 100.0; + TopoDS_Shape aShape = BRepPrimAPI_MakeCylinder (aRadius, aHeight); + if (theMode == MyDispMode_Main) + { + //StdPrs_ShadedShape::Add (thePrs, aShape, myDrawer); // add shading + //StdPrs_WFShape::Add (thePrs, aShape, myDrawer); // add wireframe + Handle(Graphic3d_ArrayOfTriangles) aTris = + Prs3d_ToolCylinder::Create (aRadius, aRadius, aHeight, 10, 10, gp_Trsf()); + Handle(Graphic3d_Group) aGroupTris = thePrs->NewGroup(); + aGroupTris->SetGroupPrimitivesAspect (myDrawer->ShadingAspect()->Aspect()); + aGroupTris->AddPrimitiveArray (aTris); + ... + } + ... +} +~~~~ + +@figure{ais_object_step3_quadrics_10.png,"@c Prs3d_ToolCylinder (10 slices).",409} height=409px + +Well... that looks a little bit edgy. +Quadric builder creates a triangulation taking the following parameters: +- Geometry parameters. + (in case of a cylinder - base radius, top radius and height). +- Number of subdivisions along U (slices) and V (stacks) parameters. + In some cases only one parametric scope matters. +- Transformation @c gp_Trsf to apply + (original geometry is defined within some reference coordinate system). + +Let's increase number of subdivisions from _10_ to _25_: +~~~~{.cpp} +Handle(Graphic3d_ArrayOfTriangles) aTris = + Prs3d_ToolCylinder::Create (aRadius, aRadius, aHeight, 25, 25, gp_Trsf()); +~~~~ + +@figure{ais_object_step3_quadrics_25.png,"@c Prs3d_ToolCylinder (25 slices).",409} height=409px + +It looks much better now! Note that @c Prs3d_ToolCylinder could be used for building both cones and cylinders depending on top/bottom radius definition. + +There is one issue though - our cylinder doesn't have top and bottom anymore! +To fix this problem we will use one more quadric builder @c Prs3d_ToolDisk: + +~~~~{.cpp} +void MyAisObject::Compute (const Handle(PrsMgr_PresentationManager)& thePrsMgr, + const Handle(Prs3d_Presentation)& thePrs, + const Standard_Integer theMode) +{ + const double aRadius = 100.0, aHeight = 100.0; + if (theMode == MyDispMode_Main) + { + Prs3d_ToolCylinder aCyl (aRadius, aRadius, aHeight, 25, 25); + Prs3d_ToolDisk aDisk (0.0, aRadius, 25, 1); + + Handle(Graphic3d_ArrayOfTriangles) aTris = + new Graphic3d_ArrayOfTriangles (aCyl.VerticesNb() + 2 * aDisk.VerticesNb(), + 3 * (aCyl.TrianglesNb() + 2 * aDisk.TrianglesNb()), + Graphic3d_ArrayFlags_VertexNormal); + aCyl .FillArray (aTris, gp_Trsf()); + aDisk.FillArray (aTris, gp_Trsf()); + + gp_Trsf aDisk2Trsf; + aDisk2Trsf.SetTransformation (gp_Ax3 (gp_Pnt (0.0, 0.0, aHeight), -gp::DZ(), gp::DX()), gp::XOY()); + aDisk.FillArray (aTris, aDisk2Trsf); + + Handle(Graphic3d_Group) aGroupTris = thePrs->NewGroup(); + aGroupTris->SetGroupPrimitivesAspect (myDrawer->ShadingAspect()->Aspect()); + aGroupTris->AddPrimitiveArray (aTris); + aGroupTris->SetClosed (true); + ... + } +} +~~~~ + +Now our cylinder looks solid! The sample above merges two triangulations into a single one instead of appending each primitive array individually. + +This looks like a minor difference, but it might have a _dramatic impact on performance_ in case of a large scene, +as each `Graphic3d_ArrayOfPrimitives` is mapped into a dedicated draw call at graphic driver (OpenGL) level. + +@figure{ais_object_step3_quadrics_fin.png,"@c Prs3d_ToolCylinder + @c Prs3d_ToolDisk.",409} height=409px + +As an exercise, let's try computing a triangulation for cylinder disk without help of @c Prs3d_ToolDisk builder: + +~~~~{.cpp} +void MyAisObject::Compute (const Handle(PrsMgr_PresentationManager)& thePrsMgr, + const Handle(Prs3d_Presentation)& thePrs, + const Standard_Integer theMode) +{ + const double aRadius = 100.0, aHeight = 100.0; + if (theMode == MyDispMode_Main) + { + const int aNbSlices = 25; + Prs3d_ToolCylinder aCyl (aRadius, aRadius, aHeight, aNbSlices, aNbSlices); + Handle(Graphic3d_ArrayOfTriangles) aTris = + new Graphic3d_ArrayOfTriangles (aCyl.VerticesNb(), + 3 * (aCyl.TrianglesNb()), + Graphic3d_ArrayFlags_VertexNormal); + aCyl.FillArray (aTris, gp_Trsf()); + + Handle(Graphic3d_ArrayOfTriangles) aTris2 = + new Graphic3d_ArrayOfTriangles (aNbSlices + 1, aNbSlices * 3, Graphic3d_ArrayFlags_VertexNormal); + aTris2->AddVertex (gp_Pnt (0.0, 0.0, aHeight), -gp::DZ()); + for (int aSliceIter = 0; aSliceIter < aNbSlices; ++aSliceIter) + { + double anAngle = M_PI * 2.0 * double(aSliceIter) / double(aNbSlices); + aTris2->AddVertex (gp_Pnt (Cos (anAngle) * aRadius, Sin (anAngle) * aRadius, aHeight), -gp::DZ()); + } + for (int aSliceIter = 0; aSliceIter < aNbSlices; ++aSliceIter) + { + aTris2->AddEdges (1, aSliceIter + 2, aSliceIter + 1 < aNbSlices ? (aSliceIter + 3) : 2); + } + + Handle(Graphic3d_Group) aGroupTris = thePrs->NewGroup(); + aGroupTris->SetGroupPrimitivesAspect (myDrawer->ShadingAspect()->Aspect()); + aGroupTris->AddPrimitiveArray (aTris); + aGroupTris->AddPrimitiveArray (aTris2); + ... + } +} +~~~~ + +@figure{ais_object_step3_quadrics_disk.png,"Manually triangulated disk.",409} height=409px + +The disk is here, but it has a strange color - like it is not affected by lighting. +This happens when vertex normals are defined incorrectly. +In our case we defined disk normal as @c -DZ (see the second argument of @c Graphic3d_ArrayOfTriangles::AddVertex()), +but normal direction should be also aligned to triangulation winding rule. +Graphic driver defines the front side of triangle using clockwise order of triangle nodes, and normal should be defined for a front side of triangle - e.g. it should be @c gp::DZ() in our case. +After reversing vertex normal direction, cylinder looks exactly like when @c Prs3d_ToolDisk was used. + +Front / back face orientation might be displayed using different material based on @c Graphic3d_Aspects::SetDistinguish() flag and @c @::FrontMaterial() / @c @::BackMaterial() setup. + +@section ais_selection Computing selection +In the first part of the tutorial we have created a custom @c AIS object @c MyAisObject computing presentation by implementing the @c PrsMgr_PresentableObject::Compute() interface. +In this part we will extend our object with interactive capabilities and make it selectable through implementing @c SelectMgr_SelectableObject interface. + +Let's do the first step and put into @c @::ComputeSelection() method some logic. +This method should fill in the @c SelectMgr_Selection argument with @c SelectMgr_SensitiveEntity entities defining selectable elements - triangulations, polylines, points and their composition. +@c Select3D_SensitiveBox is probably the simplest way to define selectable volume - by it's bounding box: + +~~~~{.cpp} +void MyAisObject::ComputeSelection (const Handle(SelectMgr_Selection)& theSel, + const Standard_Integer theMode) +{ + const double aRadius = 100.0, aHeight = 100.0; + TopoDS_Shape aShape = BRepPrimAPI_MakeCylinder (aRadius, aHeight); + Bnd_Box aBox; + BRepBndLib::Add (aShape, aBox); + Handle(SelectMgr_EntityOwner) anOwner = new SelectMgr_EntityOwner (this); + Handle(Select3D_SensitiveBox) aSensBox = new Select3D_SensitiveBox (anOwner, aBox); + theSel->Add (aSensBox); +} +~~~~ + +@c SelectMgr_EntityOwner is a key object in selection logic - it serves as an identifier of a pickable object or it's part. +You may see this object in methods like @c AIS_InteractiveContext::DetectedOwner(), **Owners** are stored within the list of selection objects @c AIS_Selection +and it received by methods like @c AIS_InteractiveContext::SetSelected() and @c AIS_InteractiveContext::AddOrRemoveSelected(). +From the Selector's point of view, @c AIS_InteractiveObject is just a drawer for @c SelectMgr_EntityOwner. + +The _**0th selection mode**_ normally defines a single Owner of the entire object. +To make a composite object selectable as whole, we add to Selection as many SensitiveEntity as necessary referring to the same Owner. +It might look confusing from first glance, that @c SelectMgr_SensitiveEntity stores @c SelectMgr_EntityOwner as a class field, and not in the opposite way +(@c SelectMgr_EntityOwner doesn't store the list of @c SelectMgr_SensitiveEntity defining it's picking volume). + +For local selection (selection of object parts) we create individual Owners for each part and add SensitiveEntity to Selection in the same way. +Owner may store an additional identifier as a class field, like @c StdSelect_BRepOwner stores @c TopoDS_Shape as an identifier of picked sub-shape with @c AIS_Shape object. + +In a similar way as @c StdPrs_ShadedShape is a **presentation builder** for @c TopoDS_Shape, the @c StdSelect_BRepSelectionTool can be seen as a standard **selection builder** for shapes: + +~~~~{.cpp} +void MyAisObject::ComputeSelection (const Handle(SelectMgr_Selection)& theSel, + const Standard_Integer theMode) +{ + const double aRadius = 100.0, aHeight = 100.0; + TopoDS_Shape aShape = BRepPrimAPI_MakeCylinder (aRadius, aHeight); + Standard_Real aDefl = StdPrs_ToolTriangulatedShape::GetDeflection (aShape, myDrawer); + StdSelect_BRepSelectionTool::Load (theSel, this, aShape, TopAbs_SHAPE, aDefl, + myDrawer->DeviationAngle(), + myDrawer->IsAutoTriangulation()); +} +~~~~ + +Internally, @c StdSelect_BRepSelectionTool iterates over sub-shapes and appends to the Selection (@c theSel) entities like @c Select3D_SensitiveTriangulation (for faces) and @c Select3D_SensitiveCurve (for edges). + +Previously, we have used @c Prs3d_ToolCylinder to triangulate a cylinder, so let's try to construct @c Select3D_SensitivePrimitiveArray from the same triangulation: + +~~~~{.cpp} +void MyAisObject::ComputeSelection (const Handle(SelectMgr_Selection)& theSel, + const Standard_Integer theMode) +{ + const double aRadius = 100.0, aHeight = 100.0; + Handle(SelectMgr_EntityOwner) anOwner = new SelectMgr_EntityOwner (this); + Handle(Graphic3d_ArrayOfTriangles) aTris = + Prs3d_ToolCylinder::Create (aRadius, aRadius, aHeight, 25, 25, gp_Trsf()); + Handle(Select3D_SensitivePrimitiveArray) aSensTri = + new Select3D_SensitivePrimitiveArray (anOwner); + aSensTri->InitTriangulation (aTris->Attributes(), aTris->Indices(), + TopLoc_Location()); + theSel->Add (aSensTri); +} +~~~~ + +Selection is computed independently from presentation, so that they don't have to match each other. +But inconsistency between presentation and selection might confuse a user, when he will not be able to pick an object clearly displayed under the mouse cursor. +These issues might happen, for example, when selection uses tessellated representation of the same geometry computed with different parameters (different number of subdivisions, or different deflection parameters). + +As in case of @c @::Compute(), it makes sense defining some enumeration of **selection modes** supported by specific object and reject unsupported ones to avoid unexpected behavior: + +~~~~{.cpp} +void MyAisObject::ComputeSelection (const Handle(SelectMgr_Selection)& theSel, + const Standard_Integer theMode) +{ + if (theMode != 0) { return; } + ... +} +~~~~ + +Unlike display modes, @c AIS_InteractiveContext allows activating an arbitrary combination of selection modes. +A user should be careful to activate only the modes that actually make sense and may work together. + +Selection mode to activate could be specified while displaying the object (passing _**-1**_ instead of _**0**_ would display an object with deactivated selection): + +~~~~{.cpp} +Handle(AIS_InteractiveContext) theCtx; +Handle(MyAisObject) aPrs = new MyAisObject(); +theCtx->Display (aPrs, MyAisObject::MyDispMode_Main, 0, false); +~~~~ + +Later on @c AIS_InteractiveContext::SetSelectionModeActive(), or it's wrappers @c AIS_InteractiveContext::Activate() and @c AIS_InteractiveContext::Deactivate(), +could be used to enable or disable desired selection modes one by one. + +@section sel_owner_highlight Highlighting selection owner + +As has been mentioned in the previous section, @c SelectMgr_EntityOwner is a key object which can be used as an identifier of selectable part(s). +Naturally, you might want to subclass it to put some application-specific ids for identification of selected parts. +But there are more things you may do with the Owner class like customized highlighting. + +Let's start from the beginning and define a custom Owner class: + +~~~~{.cpp} +class MyAisOwner : public SelectMgr_EntityOwner +{ + DEFINE_STANDARD_RTTI_INLINE(MyAisOwner, SelectMgr_EntityOwner) +public: + MyAisOwner (const Handle(MyAisObject)& theObj, int thePriority = 0) + : SelectMgr_EntityOwner (theObj, thePriority) {} + + virtual void HilightWithColor (const Handle(PrsMgr_PresentationManager)& thePrsMgr, + const Handle(Prs3d_Drawer)& theStyle, + const Standard_Integer theMode) override + { base_type::HilightWithColor (thePrsMgr, theStyle, theMode); } + + virtual void Unhilight (const Handle(PrsMgr_PresentationManager)& thePrsMgr, + const Standard_Integer theMode) override + { base_type::Unhilight (thePrsMgr, theMode); } +protected: + Handle(Prs3d_Presentation) myPrs; +}; +~~~~ + +@c SelectMgr_EntityOwner doesn't define any pure virtual methods, and can be instanced straight ahead, like it was done within @c MyAisObject::ComputeSelection() implementation above. +Let's revert usage of a dedicated display mode for highlighting (remove @c SetHilightMode() in @c MyAisObject constructor) and use our new class @c MyAisOwner within @c @::ComputeSelection(): + +~~~~{.cpp} +MyAisObject::MyAisObject() +{ + //SetHilightMode (MyDispMode_Highlight); + myDrawer->SetupOwnShadingAspect(); + ... +} + +void MyAisObject::ComputeSelection (const Handle(SelectMgr_Selection)& theSel, + const Standard_Integer theMode) +{ + const double aRadius = 100.0, aHeight = 100.0; + Handle(MyAisOwner) anOwner = new MyAisOwner (this); + ... +} +~~~~ + +The further logic creating sensitive entities and filling in Selection could be left as is. +Substitution of @c SelectMgr_EntityOwner with @c MyAisOwner currently doesn't change behavior and we see highlighting of the entire object through color modulation. +This is because default implementation of @c SelectMgr_EntityOwner for highlighting logic looks like this (simplified): + +~~~~{.cpp} +void SelectMgr_EntityOwner::HilightWithColor ( + const Handle(PrsMgr_PresentationManager)& thePrsMgr, + const Handle(Prs3d_Drawer)& theStyle, + const Standard_Integer theMode) +{ + const Graphic3d_ZLayerId aHiLayer = + theStyle->ZLayer() != Graphic3d_ZLayerId_UNKNOWN + ? theStyle->ZLayer() + : mySelectable->ZLayer(); + thePrsMgr->Color (mySelectable, theStyle, theMode, NULL, aHiLayer); +} +~~~~ + +@figure{ais_object_step4_highlight1.png,"Default behavior of @c SelectMgr_EntityOwner::HilightWithColor().",409} height=409px + +Now, let's override the @c SelectMgr_EntityOwner::HilightWithColor() method and display a bounding box presentation: + +~~~~{.cpp} +void MyAisOwner::HilightWithColor (const Handle(PrsMgr_PresentationManager)& thePrsMgr, + const Handle(Prs3d_Drawer)& theStyle, + const Standard_Integer theMode) +{ + if (myPrs.IsNull()) + { + myPrs = new Prs3d_Presentation (thePrsMgr->StructureManager()); + MyAisObject* anObj = dynamic_cast (mySelectable); + anObj->Compute (thePrsMgr, myPrs, MyAisObject::MyDispMode_Highlight); + } + if (!thePrsMgr->IsImmediateModeOn()) + { + myPrs->Display(); + } +} +~~~~ + +@c SelectMgr_EntityOwner::HilightWithColor() doesn't receive a presentation to fill in as an argument; highlight presentation should be manually created and even explicitly displayed on the screen. +To avoid code duplication, the code above reuses @c MyAisObject::Compute() already implementing computation of highlight presentation. + +@figure{ais_object_step4_highlight2.png,"Result of custom implementation @c MyAisOwner::HilightWithColor().",409} height=409px + +The visual result of the selected object looks exactly the same as when we've used a dedicated highlight mode. +One thing became broken, though - highlighting remains displayed even after clearing selection. +To fix this issue, we need implementing @c SelectMgr_EntityOwner::Unhilight() and hide our custom presentation explicitly: + +~~~~{.cpp} +void MyAisOwner::Unhilight (const Handle(PrsMgr_PresentationManager)& thePrsMgr, + const Standard_Integer theMode) +{ + if (!myPrs.IsNull()) { myPrs->Erase(); } +} +~~~~ + +Another problem is that the object is no longer dynamically highlighted. +To fix that we need to handle @c PrsMgr_PresentationManager::IsImmediateModeOn() specifically. +Within this mode turned ON, presentation should be displayed on the screen with help of @c PrsMgr_PresentationManager::AddToImmediateList() method +(it will be cleared from the screen automatically on the next mouse movement): + +~~~~{.cpp} +void MyAisOwner::HilightWithColor (const Handle(PrsMgr_PresentationManager)& thePrsMgr, + const Handle(Prs3d_Drawer)& theStyle, + const Standard_Integer theMode) +{ + if (myPrs.IsNull()) + { + myPrs = new Prs3d_Presentation (thePrsMgr->StructureManager()); + MyAisObject* anObj = dynamic_cast (mySelectable); + anObj->Compute (thePrsMgr, myPrs, MyAisObject::MyDispMode_Highlight); + } + if (thePrsMgr->IsImmediateModeOn()) + { + Handle(Prs3d_PresentationShadow) aShadow = + new Prs3d_PresentationShadow (thePrsMgr->StructureManager(), myPrs); + aShadow->SetZLayer (Graphic3d_ZLayerId_Top); + aShadow->Highlight (theStyle); + thePrsMgr->AddToImmediateList (aShadow); + } + else + { + myPrs->Display(); + } +} +~~~~ + +We may create two dedicated presentations for dynamic highlighting or reuse existing one for both cases with help of a transient object @c Prs3d_PresentationShadow. + +Let's go further and make dynamic highlighting a little bit more interesting - by drawing a surface normal at the point where mouse picked the object: + +~~~~{.cpp} +void MyAisOwner::HilightWithColor (const Handle(PrsMgr_PresentationManager)& thePrsMgr, + const Handle(Prs3d_Drawer)& theStyle, + const Standard_Integer theMode) +{ + MyAisObject* anObj = dynamic_cast (mySelectable); + if (thePrsMgr->IsImmediateModeOn()) + { + Handle(StdSelect_ViewerSelector) aSelector = + anObj->InteractiveContext()->MainSelector(); + SelectMgr_SortCriterion aPickPnt; + for (int aPickIter = 1; aPickIter <= aSelector->NbPicked(); ++aPickIter) + { + if (aSelector->Picked (aPickIter) == this) + { + aPickPnt = aSelector->PickedData (aPickIter); + break; + } + } + + Handle(Prs3d_Presentation) aPrs = mySelectable->GetHilightPresentation (thePrsMgr); + aPrs->SetZLayer (Graphic3d_ZLayerId_Top); + aPrs->Clear(); + Handle(Graphic3d_Group) aGroup = aPrs->NewGroup(); + aGroupPnt->SetGroupPrimitivesAspect (theStyle->ArrowAspect()->Aspect()); + gp_Trsf aTrsfInv = mySelectable->LocalTransformation().Inverted(); + gp_Dir aNorm (aPickPnt.Normal.x(), aPickPnt.Normal.y(), aPickPnt.Normal.z()); + Handle(Graphic3d_ArrayOfTriangles) aTris = + Prs3d_Arrow::DrawShaded (gp_Ax1(aPickPnt.Point, aNorm).Transformed (aTrsfInv), + 1.0, 15.0, + 3.0, 4.0, 10); + aGroupPnt->AddPrimitiveArray (aTris); + thePrsMgr->AddToImmediateList (aPrs); + } +} +~~~~ + +Code above does not store our new highlight presentation as a property of @c MyAisOwner, and instead uses @c SelectMgr_SelectableObject::GetHilightPresentation() method +to create a presentation stored directly inside of our interactive object. + +Next trick is passing through the last picking results in @c StdSelect_ViewerSelector. +Dynamic highlighting is expected to be called right after picking, so that highlighted Owner should be always found in picking results. +@c StdSelect_ViewerSelector::Picked() returns entities in the descending order of their distance from picking ray origin (mouse cursor); +normally our Owner should be the very first one in this list when no selection filters are assigned to @c AIS_InteractiveContext. + +@c SelectMgr_SortCriterion provides us useful information like 3D point on detected object lying on the picking ray, and surface normal direction at this point (actually, it would be a normal to a picked triangle), +which we display as an arrow with help of @c Prs3d_Arrow presentation builder. + +@figure{ais_object_step4_highlight3.png,"Surface normal on mouse over.",409} height=409px + +Result looks pretty nice on the screenshot, but has interaction problems - once displayed, an arrow is no longer updated with further mouse movements. +But this behavior is not a bug - @c AIS calls @c MyAisOwner::HilightWithColor() only when picking Owner changes to avoid unnecessary Viewer updates. +To override this behavior, we may override @c SelectMgr_EntityOwner::IsForcedHilight() option: + +~~~~{.cpp} +class MyAisOwner : public SelectMgr_EntityOwner +{ +... + virtual bool IsForcedHilight() const override { return true; } +}; +~~~~ + +This solves the problem within our specific use case. +Keep in mind that most objects don't need updating highlight presentation on every mouse move; +overriding this flag everywhere would be a waste of resources and may cause performance issues - use it sparingly. + +@section highlight_apporaches Highlighting approaches + +@c AIS provides one more alternative to handle presentation highlighting, which is managed by option @c SelectMgr_SelectableObject::IsAutoHilight(). +By default, this option is turned ON and redirects highlighting logic to @c SelectMgr_EntityOwner::HilightWithColor() demonstrated in the previous section. +Turning this option OFF redirects highlighting logic to the interactive object itself @c SelectMgr_SelectableObject::HilightSelected(). + +Apart from moving the logic from Owner to Interactive Object, this approach allows handling highlighting of all selected Owners within the same Object at once and sharing a common presentation +instead of per-Owner presentation - improving performance and reducing memory utilization in case of a large number of small selectable elements, like mesh nodes in @c MeshVS_Mesh object. + +The further optimization of such a scenario would be using a single Owner for the entire Object +storing the list of selected elements within the Owner itself - as utilized by @c AIS_PointCloud object for highlighting individual points. + +We wouldn't describe these advanced techniques here in detail - let's just summarize main highlighting approaches available in @c AIS: +- Highlighting of a main presentation of Interactive Object (active display mode) + filled in by @c PrsMgr_PresentableObject::Compute() + and displayed with color modulation by @c AIS logic. + - Example: @c AIS_TextLabel. +- Highlighting of a secondary presentation of Interactive Object + filled in by @c PrsMgr_PresentableObject::Compute() + and displayed with color modulation by @c AIS logic. + - Example: @c AIS_Shape, displayed in @c AIS_Shaded display mode and highlighted using @c AIS_Wireframe display mode (default behavior). + See also @c PrsMgr_PresentableObject::SetHilightMode(). +- Highlight presentation stored within a custom @c SelectMgr_EntityOwner + and managed by @c SelectMgr_EntityOwner::HilightWithColor(). + - Example: @c StdSelect_BRepOwner for selection of sub-shapes. +- Custom highlight presentation stored within Interactive Object itself + (see @c SelectMgr_SelectableObject::GetHilightPresentation() / @c @::GetSelectPresentation() methods). + - Filled in by @c SelectMgr_EntityOwner::HilightWithColor() + with @c SelectMgr_SelectableObject::IsAutoHilight() turned ON.
+ Example: @c AIS_PointCloud. + - Filled in by @c SelectMgr_SelectableObject::HilightSelected() + with @c SelectMgr_SelectableObject::IsAutoHilight() turned OFF.
+ Example: @c MeshVS_Mesh. +- Main presentation of Interactive Object (active display mode) + filled in by @c PrsMgr_PresentableObject::Compute() + and manually updated (recomputed or modified aspects) on highlight events. + - Example: @c AIS_Manipulator. + +The number of options looks overwhelming but in general, it is better to stick to the simplest approach working for you and consider alternatives only when you have to. + +@section mouse_click Mouse click + +Dynamic highlighting is only one of scenarios where @c SelectMgr_EntityOwner could be useful. +Another feature is an interface for handling a mouse click @c SelectMgr_EntityOwner @c @::HandleMouseClick(). + +This interface is useful for defining some user interface elements like buttons, and most likely your application will use a more comprehensive GUI framework for this purpose instead of @c AIS. +But let's have some fun and make our object to change a color on each mouse click: + +~~~~{.cpp} +class MyAisOwner : public SelectMgr_EntityOwner +{ +... + virtual bool HandleMouseClick (const Graphic3d_Vec2i& thePoint, + Aspect_VKeyMouse theButton, + Aspect_VKeyFlags theModifiers, + bool theIsDoubleClick) override; +}; + +bool MyAisOwner::HandleMouseClick (const Graphic3d_Vec2i& thePoint, + Aspect_VKeyMouse theButton, + Aspect_VKeyFlags theModifiers, + bool theIsDoubleClick) +{ + static math_BullardGenerator aRandGen; + Quantity_Color aRandColor (float(aRandGen.NextInt() % 256) / 255.0f, + float(aRandGen.NextInt() % 256) / 255.0f, + float(aRandGen.NextInt() % 256) / 255.0f, + Quantity_TOC_sRGB); + mySelectable->Attributes()->ShadingAspect()->SetColor(aRandColor); + mySelectable->SynchronizeAspects(); + return true; +} +~~~~ + +Looks pretty simple. Now let's make things more interesting and launch some simple object animation on each click. +We use a couple of global (@c static) variables in our sample for simplicity - don't do that in a real production code. + +~~~~{.cpp} +class MyAisOwner : public SelectMgr_EntityOwner +{ +... + void SetAnimation (const Handle(AIS_Animation)& theAnim) + { myAnim = theAnim; } +... + Handle(AIS_Animation) myAnim; +}; + +bool MyAisOwner::HandleMouseClick (const Graphic3d_Vec2i& thePoint, + Aspect_VKeyMouse theButton, + Aspect_VKeyFlags theModifiers, + bool theIsDoubleClick) +{ + static bool isFirst = true; + isFirst = !isFirst; + MyAisObject* anObj = dynamic_cast (mySelectable); + gp_Trsf aTrsfTo; + aTrsfTo.SetRotation (gp_Ax1 (gp::Origin(), gp::DX()), + isFirst ? M_PI * 0.5 : -M_PI * 0.5); + gp_Trsf aTrsfFrom = anObj->LocalTransformation(); + Handle(AIS_AnimationObject) anAnim = + new AIS_AnimationObject ("MyAnim", anObj->InteractiveContext(), + anObj, aTrsfFrom, aTrsfTo); + anAnim->SetOwnDuration (2.0); + + myAnim->Clear(); + myAnim->Add (anAnim); + myAnim->StartTimer (0.0, 1.0, true); + return true; +} +~~~~ + +Animation is a complex topic that is worth a dedicated article - let's not go too deep in detail here. +To perform animation in a non-interrupted way, it should be handled by some class like @c AIS_ViewController, which is responsible for managing user input events and for 3D viewer updates. +To utilize it, you need adding a custom object animation to @c AIS_ViewController::ObjectsAnimation() or adding custom view animation to @c AIS_ViewController::ViewAnimation(). +Somewhere in application this might look like this: + +~~~~{.cpp} +Handle(AIS_InteractiveContext) theCtx; +Handle(AIS_ViewController) theViewCtrl; +Handle(MyAisObject) aPrs = new MyAisObject(); +aPrs->SetAnimation (theViewCtrl->ObjectsAnimation()); +theCtx->Display (aPrs, MyAisObject::MyDispMode_Main, 0, false); +~~~~ + +@section final Final result + +The final sample could be seen by calling @c QATutorialAisObject command from Draw Harness plugin @c QAcommands (@c TKQADraw toolkit): + +~~~~ +pload VISUALIZATION QAcommands +vinit View1 +QATutorialAisObject p +vfit +~~~~ + +You may also take a look onto source code of this command at @c src/QADraw/QADraw_Tutorials.cxx if you have some problems following the tutorial. diff --git a/dox/samples/images/ais_object_step1_bndbox.png b/dox/samples/images/ais_object_step1_bndbox.png new file mode 100644 index 0000000000000000000000000000000000000000..48baaf76b5ce2448ce786d58cb8ce3163b59d776 GIT binary patch literal 3169 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�!zRldH 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 br7p2%rI-JPHG{W%0;d01{GDawc=>+;ucx7y literal 0 HcmV?d00001 diff --git a/dox/samples/images/ais_object_step1_shaded.png b/dox/samples/images/ais_object_step1_shaded.png new file mode 100644 index 0000000000000000000000000000000000000000..f39c0141d7a3a07b835621da7edd7c99192bf216 GIT binary patch literal 3308 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 WUQz0UcIGb9tc9t~-W+46h`#`lS9%`+ literal 0 HcmV?d00001 diff --git a/dox/samples/images/ais_object_step1_shaded_wf.png b/dox/samples/images/ais_object_step1_shaded_wf.png new file mode 100644 index 0000000000000000000000000000000000000000..54b1ad5816bf6980acb8de40ae1b43a53d1eb1d6 GIT binary patch literal 5330 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 M`{M=wzIgM00Qq>xssI20 literal 0 HcmV?d00001 diff --git a/dox/samples/images/ais_object_step2_segments.png b/dox/samples/images/ais_object_step2_segments.png new file mode 100644 index 0000000000000000000000000000000000000000..70a55443b7cf7688a58f56a8d5372ffe44c0cf40 GIT binary patch literal 3706 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 WKZX7g5cW?Au8pOmMX~vhSN;R(5iXkm literal 0 HcmV?d00001 diff --git a/dox/samples/images/ais_object_step3_quadrics_10.png b/dox/samples/images/ais_object_step3_quadrics_10.png new file mode 100644 index 0000000000000000000000000000000000000000..24b62a30f4f866fcbe1f7b9d5fd47b2528bc04a9 GIT binary patch literal 5872 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 D!(jGZ literal 0 HcmV?d00001 diff --git a/dox/samples/images/ais_object_step3_quadrics_25.png b/dox/samples/images/ais_object_step3_quadrics_25.png new file mode 100644 index 0000000000000000000000000000000000000000..59a38129b48d09a600a7dcfa2944031c244df76e GIT binary patch literal 5873 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 Hr(gdMlMxnq literal 0 HcmV?d00001 diff --git a/dox/samples/images/ais_object_step3_quadrics_disk.png b/dox/samples/images/ais_object_step3_quadrics_disk.png new file mode 100644 index 0000000000000000000000000000000000000000..4747f9fdb1cb6595fd08d9f50084809b2e3a6721 GIT binary patch literal 4652 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 literal 0 HcmV?d00001 diff --git a/dox/samples/images/ais_object_step3_quadrics_fin.png b/dox/samples/images/ais_object_step3_quadrics_fin.png new file mode 100644 index 0000000000000000000000000000000000000000..00f23de308454d48e03d6a91137d2954759b28fd GIT binary patch literal 4625 zcmcgw=U-Dv7lw6pcUe#oMPcnAiU}7J1VyPL(xNC5Ad-MfxF}6ZDAFwpihwLZ2%*^! z1Ows<IY}`lstMJf=1T+|trd-|O*RW@PzF^~RLVAInZu ze-+vmBZuQYIo!v+U#SR-(F_po0*L?LkA`r1@S|yjMn-Zhk=3cN3V83`<4Ig+sF%R%!FxRP|*H~oal`=ss-ws zNp_(+$7YWky1RFJp%ck66>_I_^u%HHoXr%gb5my7qe?IVsLO-luN2E;;9A{MJT(>Ba~3H_jX$S(+~p&>bAMceaOA zTK?+NRi8}^SHTJ{m?UPrdC5Lr>#C3{sPAuH?|J2!+bnFcO)3+~+yQ zVdK;KRILDq(2mG^w|ZL&<_+#_Ol~+N4Kh(?eF&KByFHxzt@?Lr%`u5LcG8ySI`8i~>2Sbo^56Yu44{Cextr^;#kf|g**ebSf~gyZnCOU| zw4X!U{v_sv<$tB?_aFBgd0wU*9T7~`H%()vgd}`;y?g)8Q;*uU+%PMIMi*w1=_ua>3+%>z>OskU1G+vhv zNNOa|Zxiwe_0F&CN6p2BDvD)|HjBe=B0|pIs8n3c#hq9eXQF?J+InR#zKWlT1jyQ= z>Dq5|?Kd-z4sQ;tsC|3M#r(r$e1ym0gVq+u>!>O+OyiKZ?;iRUd0#XUIr+Or*XTLY z3#Mc{zWoYf)z}`Y({r>~YTS_~&0Mc6$K4hwdSCO@ek$woW~OmMZ0Iw!%}mFmyL89- z7hFeF%y%ZI?iJr3ZxO}f|7L@!)^qHx-;s8+GQ%;eJoWJbd|XbAZHBik`-@~sH**i)_P&8W}7B2~Tz ze~5n?WOrUSK3+!YN!Il53}wDzMPJg3kMKQw)$GZHPn{3F^yPOHcb>kWq(XAW$Xue5 zGY`p_4@$QYM4`Rp*n}Znb*mDiVsANy$>a1dpB%{LFnzqy#v~MDk%`v|jv%rLuglqz zfav6Og}geKxY4Hzz9HAemeoWT5i8sE8R5 zpjA5V((H5#oeXgiTnq$J54f>5loY54jvxgQV2Y zlz>arFP@A+Mqx59X!UA8XPb$9(nyMBR*ump?a^W`~;!OxARr-RsQa&=2yi8b9=S*wsrDTrKKfT zyuG!&n4u&IUlMzyBs?rRyt>1ZD-T!pp-&^xj-YY>Y5FP3Oxbp$>XLpELDMR|)B>p> z6hKkp>_8%hJCTbMs<0(<^UyO!wamNcVocJqaBS={*X{E6e>gTxYIa&5xYKewwK)BF z-fI02;@L7k^(m8@CVTO|7n6$(VWjDdb`XwPN-+AuVyob`ds)+!3*X*pmcep4C}jWs zs)b98iIgVI=_(IRRW;*4Q4>d$Uuda2T~sC~wsWJ=98Hg|H=Zs3PE5R#f(j}K#n4~7 z{e)AQ74|1XJNjHIBch{IckNy|3Ce66r`V|(G-C##-MiJu{kz0+aoKobja2bxxQ6fa z1d8hVEKkJ+xR#egZX&vbdKjn0fMO0+u!t@DUQY>(U^M zXAi-Z;8K}u`;|&n7(&Qg)^($!ugf#~OTE7%OFvhd$TJPrR5i6Ct!GB2={CQU6UVvO z2+!apde*ctleRb$GeqeRCix8mMN3QJT%7Wi%*?!y<(~&2j4?$PdP@^#HYPndA|qq( zj>0oZqDYSNYwzDo@f4}$L(MXX)$d2)?9y(3(@k+Z6M0t|rD1JTV4;quU_NbuE zmo!S@z*UY9NiqymTL%iR<~=($3a0t+5b}8Yo3DW(f9>wESsNNb4 zK*I1G)PW1M>KOa$ml|8+0z+9E9dxcNqy4SFaAo=JB`Q691BqY^WMH+!vt!k6yiT{U z_WpO_M^!iN{uX1>j0gR^?G0o`mThS9Fn42hT8eVrQUGPzV<`Qaa_jTa5_qDv=k{-_ zjV-OJGOw~9I9WN)Iy4qG1N^dlbPJ=bIR2U;y`MpSu}zMVjb7@)n|E)A48|ya({~hN z_vl`q9=J(77-K4UUTBi=1f5}#-rXrE*rakQyVC&|=Wqi42xR>-ifbZUF~cQ} zX16%RhB6JwnUE_7omqMv#6#1y0xYYjQ5SQHYzRq78sL>#0W`cc@!>Cu#XH;$%8J4Guq9>3(R=2Bz> zR^43x!^(J+iue%bQ0Pa#1(&FoC>7RW$n80qbpbYipUWyZn# zVMl@ei}5zarx&Z#pmhvhJFIGVI^F1Ky#*oKS`x-@PQc1`zzd)n)!rn>0L>~3m?*eF zxG2-J<#{b5j+=@r{9L=l5Au`6XfOD~@vHX(oMZOx;&Kn5R&k?QpL&kl^- zzCN^}wCT#T(K*+BXAb7IEnuo((dP0VwR=ytGwZ8UD&#$0B~CO=ay{i3UZe=h%n;EL zR!JDQwJ0nLkGU|{XMtV4{Hgvqu&S@dmC4<2&;%I|868CEpv2n$%A zN@(IVZzdkFKYW2$W@THv1y}|VpckLuxUl8nn0ddb(kRw-m!HD3mW5*;oj)3KuFH`# z?K6SYZXEQDB-nGo4$Y26M{o@=IRC)&EDyaD;GxOz0vWA6gNznxE7hCHl?+@JnMAOf z;56Oc-|+zJxyY9-Dfb)*${~#J84^wU%Dn>zg~+tO!&^ih>^cimE1YO_8m$L~5xvQr ztrbc6hwpzV%&9edr`W*|0`OQCCDDU)b4GZxp+>4}xHCzWL{SR@@ALmLTSEcT0{sBf z>@_OU=y{fQ=32Bvr`F8-gXK~a`b4~zPFY(Y6q&B&vj&{d0oRCTJ}!z_)$mJp$AkOD z_`|hWg57GiOF#2NM0^`P8KcdwZrhpm;(1{ZCIekqjP>_lnS<=o6y_q6I!n_^rDM|%QQKLdL4WZ@fjFshW}H^aPNjlk&Apcf-;Ow zp4{}()TK?N0_1uZ&CJTbV46%gcR#BenQ3wdR1CeDQcercm_kRe<{HKk8=YV}eoa(g zF1GQm6KNs(emZdOK?2E6x0wmd zas~P1Zv1Q_jWkA)-Sc4wGeVSE#}G2NO8zJ+ePR&9Ilgb52%@b7Zgu4BTdD36kv+zB za~V2p*c+Liehv5o3{Pfg7!;{5Djo6@`l^ItaNM;#BhlAYlV>H@N?F#4=#NdLN7=}-IyckdjHz+E$XeKHkA`uLb^pSF^4_&$o`us%Rhx++u1zmi%&ya{r@joTnt`IvNcW^_^2 z>}^H^Q{;aHqCH@Z@YDeIySG>e6-L@7o$H-oF?o+7vAP{s_VujRrh&E5_ZgN|)tFpa zTos9$sK7A9+Pd8XE$H2anCr|pyOgy7HBba)op&?40@P*FT9|69RQD&cKu4qpSzcUi zqn{of>0FHjxL98XDe|f$uyf{&psK6a#Gl0NJpNJyd}#zp@=e^G{0pFslASn@t3YDv zE%54Gh1{^nW8RMx6qI5ZkvKV+CA#`b`7nR8RX7V^hJ0FHQH@v%j<;%chI{c4xZvYx z*gcMQS094f69Kd^G4kgdcRX<7j#jvK7HkW{Xl zuqMq(^<^CRB@3b9=()6o5})-sajp9xG#PZCc`{RP^h^)3l~Qi6cYFTFC*9pT->P91 zPg2;b@|4t7>8`@x8R~p^-MVF^ZU5?MYqx7_-{H8~LOrT`7hO&YU=JU;Fmc24wwe@; z4AhY*wlRPp_tObAB-rj@$HS5m)@%M`mQT+e=&xe>;j&N1%puT4g Thp#Is!ZtBHcRKIXe4$TmuLAfPt?lI%COothF%3Z|fnoBB&i4>E`v?5~+ za(hFfGcRdeb}B5n6jB?FJMVL}?|<;l=b7`IdCu?m`~JSa%k!K`wf@yie7nr{EnBvT zo1Znd-LmBe8uk_b5v=sScnf~)a5XnRZ6ERG(;z9!-sH!B2OInlnDm=v#N_Tp)0Wm0 zkuxc&?f(wylV05-t9H80Q23Wr#j}P=GIe65E+)Ww`uLDknd43^4_;{RHf(v~^pmW) z%}7u^rofwjIxcU zHExf%Tt*S)q|8qyt&E%8G%Xy?!>h=%?bAKcJ>vQ2is$D)@*F#6bSvc!&88*Bg=HqC z?V{Dy)xjzZ%5Cl>BsJCJq)gOq9?JUG)*}^aATxdQh;AiQF@avGM$0di+9j9KDXo>G z7@~RDTUa_3`*p@t*NogGd-aCRb*u3&D*bpZEwA;17sY&a`YD>GODlJt3rmj&s(5>Q zn{8~(L(d5PbaY`(G9>9n_F_+7!GVin;dah{sB^Wo6UCy+ovaYDB;|~d&Hju|+Kp%T z&B#jnX^H?Uvwh66j%Lun61(HgsBY3b?74))|BBGQ{Koe{!ZqTR@@9y`W> zE5H8!*N0FYt^nD=c%*@uq1p^#t29TX(GxB+EvQ*Fo|f3tP+}uw9dKD z5Fy0NCW($?bBo2y$RSB4(ue4tM6hI~1g@q86{MRKHO4JYzZ|j09JLKw`U=1A?vcVI zfRZH|q{)CO(t~mt%shlD9juV?pYHB@nGgnsiL%qDR3`Lthzz)`k!g>Ec=%$#gP4Ro z_m7Po3RkHXgRcL{<)&Me6coXk?XU0>cyNCD>M#gU5v-B~EQ6;kU0DSM z?HQlawK>Uh({#sjm!s-LMwu1nSCKS%{f!4A3q7x1$^&&(vdmVIEZe!?dScPUmp=OBjZDF zX3Fb%Wier`9OKWgij0VkR_G1|eZN(4(#j;{<>lqF6EZ>%>dV+w#tA($9Rq#;ZlYN{M%*Z(#M9~W0y*Pn6goP8a(^1PKh*8#j2(_*Ag<@ z?bG)?4H`M~5Ybtwus{Qb8mM;Ea#lK%$()<)4)>uEyj+QbrOA7f9Nv7SXcXb=0E(G|FHwJf-7QOpmtY4X`vxVJiav z{B!u=g;`g~f&&kfyH+t3xAAzq3l(mk=&=4VSu50M@zhnbY-Lxe$+c=OKh6?bnXUiq z6QMaVCwY4mKzUqS7{o_&JBW_S$=&i9Hc#`l1CuHr!;#ghtKY^c>1~b|2P)hLSaKvS z0^t)S{a^3u!_~_=&QWPq8_FcffQ`F);c^+fqB{ZxpE@|AWvHjWv(&#lLIv-$40L4< znR?6*`MkzMD^+k!K9I})vM)%A4O+>9tvaLqdAQjCv`v~#`El#kAbC^NvCQd!`cd}G z>|njY>KCI_6lZX};C12w8KJ2g#EBULCbHE46vNadGG%Fv(M>vTKN84Wv7%FHbF4O! z4&1d~MaoUYY4a{h;3J;zCq(UOx0NS)xgvpRi!$4gZs4(c zDt2|z<`!^gE0mv~?;FT}zxD7DfwCl9(NZcZlVPd=i53!~C1c~w3pqgxtr=>wwMe!fy8Y6nOT|cGP(7*U@tJT3_IX;v4h;YQL36Hkba zi`C4<^X6RPf4%ri4NZrC~j3u6TbZ&*Re?P$r27|!x3=SiETnat=}a9E{G2G`vsg|`tF-VdLP3> zw~AnW_ap)XQ!m1({Er>D-4sjujG-)*sct^#S_jC$l^M(fFb4@LFeM7G&Mze~pzqq? zjKtuONRA82F1wA0ScE}yO>uzi%gU&4KGBxo4zh^HL~{OQRr?M+O2h6=hVkZ1TL(z= z5DBvlx)O6Qwc2lCb}-5ohpDlikvIW^y0{(?9o7dDLlJ<3al5T*98ABn{X#-)O@S_3 zbs3?aZTcQZvNE;;1~(edRvktf*O#Y%v{lE96Z_532ZyGycKC;XD3vc8*l<8T^Z{mg zu*NSY#ziwE5X+HjJaW|ZF-FZ<8{%!pi~}Dy9<4%;Q?6nJfKT#zVWq}$Lost!7{}>e zd}>N(*IhjVRwsfOwC#XC<}s%f&e!s{O;X;(ttL)ntOo;e9;+Dk*-7}^i4{=1u~RP9 zxeI#R5LqSs0OVA)8?myq%v)yy^5_%zaBRX*)HDQ?%7|Y5M^F#}(kv<6M{CVf+RiCQ4FKOE<*ne&l)ZzSM{3A(>?WA`NK-=aAPibwJ5+5}_5%8-jc% zL3**PwRPqO0A`BEf=HaaoAc^ozjPzM&f}2A?xV>dfDVvek zC}Y17NQo8;p#_NLf{J=V3Y+RZx?3D@tR-<8f&^5W5P! z1;<9DG2}Pg)>rWQS*C0xKJUKsx&VWVO zGk-n%T}Wfq96jjIf8_8-x|`EX(NGw<( zZnbjchg}b$NuaDqdEcH9EtoN{g^OH@21l|!x*3@YG);P0hs-jssYj4+z1=onb^loZ zWd%)ns!F5h^Ek&xSEKm7^Uo3vI#e`#J-3*vw&VZxHx1ZvT~@y+p%8I7Mdbx%)!gJ) Kn1VtJMesUQ#{Aktf+ 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 literal 0 HcmV?d00001 diff --git a/dox/samples/images/ais_object_step4_highlight3.png b/dox/samples/images/ais_object_step4_highlight3.png new file mode 100644 index 0000000000000000000000000000000000000000..9be042ab50f02a625344867cf818b7fb00ed135d GIT binary patch literal 4024 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 set of code snippets performing typical actions with @ref occt_user_guides__ocaf "OCAF" services for newcomers. * @ref samples__ocaf_func
A simple example dedicated to the usage of "Function Mechanism" of @ref occt_user_guides__ocaf "OCCT Application Framework". + * @ref tutorials__ais_object +
A programming tutorial teaching how to compute presentation within AIS_InteractiveObject subclass for displaying in @ref occt_user_guides__visualization "OCCT 3D Viewer". - @subpage samples__projects * @ref samples_qt_iesample
A cross-platform multi-document 3D Viewer sample with CAD import / export functionality based on **Qt Widgets** framework. @@ -44,6 +46,7 @@ - @subpage occt__tutorial - @subpage samples__ocaf - @subpage samples__ocaf_func +- @subpage tutorials__ais_object @page samples__projects Sample Projects - @subpage samples_qt_iesample diff --git a/src/QADraw/FILES b/src/QADraw/FILES index e7e616ee07..4194c1f5c4 100755 --- a/src/QADraw/FILES +++ b/src/QADraw/FILES @@ -1,3 +1,3 @@ QADraw.cxx QADraw.hxx -QADraw_Additional.cxx +QADraw_Tutorials.cxx diff --git a/src/QADraw/QADraw.cxx b/src/QADraw/QADraw.cxx index fefa242a05..a0d0888136 100644 --- a/src/QADraw/QADraw.cxx +++ b/src/QADraw/QADraw.cxx @@ -14,23 +14,27 @@ // commercial license or contractual agreement. #include -#include -#include -#include -#include -#include + +#include +#include +#include +#include + #include -#include #include #include #include #include +#include +#include +#include +#include +#include +#include #include #include -#include -#include -#include +#include //======================================================================= //function : QATestExtremaSS @@ -172,7 +176,11 @@ void QADraw::Factory(Draw_Interpretor& theCommands) { // definition of QA Command QADraw::CommonCommands(theCommands); - QADraw::AdditionalCommands(theCommands); + QADraw::TutorialCommands(theCommands); + + QABugs::Commands(theCommands); + QADNaming::AllCommands(theCommands); + QANCollection::Commands(theCommands); } // Declare entry point PLUGINFACTORY diff --git a/src/QADraw/QADraw.hxx b/src/QADraw/QADraw.hxx index 9d8bf3dbc3..5e6832b902 100644 --- a/src/QADraw/QADraw.hxx +++ b/src/QADraw/QADraw.hxx @@ -22,44 +22,22 @@ #include - - +//! Draw Harness plugin defining non-general commands specific to test cases. class QADraw { public: DEFINE_STANDARD_ALLOC - - //! Define specicial commands for AIS. - Standard_EXPORT static void CommonCommands (Draw_Interpretor& DI); - - Standard_EXPORT static void AdditionalCommands (Draw_Interpretor& DI); - + Standard_EXPORT static void CommonCommands (Draw_Interpretor& theCommands); + + Standard_EXPORT static void AdditionalCommands (Draw_Interpretor& theCommands); + + Standard_EXPORT static void TutorialCommands (Draw_Interpretor& theCommands); + //! Loads all QA Draw commands. Used for plugin. - Standard_EXPORT static void Factory (Draw_Interpretor& DI); - - - - -protected: - - - - - -private: - - - - + Standard_EXPORT static void Factory (Draw_Interpretor& theCommands); }; - - - - - - #endif // _QADraw_HeaderFile diff --git a/src/QADraw/QADraw_Additional.cxx b/src/QADraw/QADraw_Additional.cxx deleted file mode 100644 index 7990d64ee6..0000000000 --- a/src/QADraw/QADraw_Additional.cxx +++ /dev/null @@ -1,29 +0,0 @@ -// Created on: 2002-03-12 -// Created by: QA Admin -// Copyright (c) 2002-2014 OPEN CASCADE SAS -// -// This file is part of Open CASCADE Technology software library. -// -// This library is free software; you can redistribute it and/or modify it under -// the terms of the GNU Lesser General Public License version 2.1 as published -// by the Free Software Foundation, with special exception defined in the file -// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT -// distribution for complete text of the license and disclaimer of any warranty. -// -// Alternatively, this file may be used under the terms of Open CASCADE -// commercial license or contractual agreement. - -#include -#include -#include -#include - -void QADraw::AdditionalCommands(Draw_Interpretor& theCommands) -{ - QABugs::Commands(theCommands); - - QADNaming::AllCommands(theCommands); - QANCollection::Commands(theCommands); - - return; -} diff --git a/src/QADraw/QADraw_Tutorials.cxx b/src/QADraw/QADraw_Tutorials.cxx new file mode 100644 index 0000000000..3fc6924bb5 --- /dev/null +++ b/src/QADraw/QADraw_Tutorials.cxx @@ -0,0 +1,349 @@ +// Copyright (c) 2022 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace +{ + +//! Custom AIS object from dox/samples/ais_object.md tutorial. +//! Make sure to update tutorial after modifications in this code! +class MyAisObject : public AIS_InteractiveObject +{ + DEFINE_STANDARD_RTTI_INLINE(MyAisObject, AIS_InteractiveObject) +public: + enum MyDispMode { MyDispMode_Main = 0, MyDispMode_Highlight = 1 }; +public: + MyAisObject(); + void SetAnimation (const Handle(AIS_Animation)& theAnim) { myAnim = theAnim; } +public: + virtual void Compute (const Handle(PrsMgr_PresentationManager)& thePrsMgr, + const Handle(Prs3d_Presentation)& thePrs, + const Standard_Integer theMode) override; + + virtual void ComputeSelection (const Handle(SelectMgr_Selection)& theSel, + const Standard_Integer theMode) override; + + virtual bool AcceptDisplayMode (const Standard_Integer theMode) const override + { + return theMode == MyDispMode_Main || theMode == MyDispMode_Highlight; + } +protected: + Handle(AIS_Animation) myAnim; + gp_Pnt myDragPntFrom; +}; + +MyAisObject::MyAisObject() +{ + // highlighting might use different display mode (see tutorial) + //SetHilightMode (MyDispMode_Highlight); + + myDrawer->SetupOwnShadingAspect(); + myDrawer->ShadingAspect()->SetMaterial (Graphic3d_NameOfMaterial_Silver); + myDrawer->SetWireAspect (new Prs3d_LineAspect (Quantity_NOC_GREEN, Aspect_TOL_SOLID, 2.0)); +} + +void MyAisObject::Compute (const Handle(PrsMgr_PresentationManager)& thePrsMgr, + const Handle(Prs3d_Presentation)& thePrs, + const Standard_Integer theMode) +{ + (void )thePrsMgr; + const double aRadius = 100.0, aHeight = 100.0; + TopoDS_Shape aShape = BRepPrimAPI_MakeCylinder (aRadius, aHeight); + if (theMode == MyDispMode_Main) + { + // use standard shape builders + //StdPrs_ShadedShape::Add (thePrs, aShape, myDrawer); + //StdPrs_WFShape::Add (thePrs, aShape, myDrawer); // add wireframe + + // use quadric builders for cylinder surface and disks + const int aNbSlices = 25; + Prs3d_ToolCylinder aCyl (aRadius, aRadius, aHeight, aNbSlices, aNbSlices); + Prs3d_ToolDisk aDisk (0.0, aRadius, 25, 1); + + Handle(Graphic3d_ArrayOfTriangles) aTris = + new Graphic3d_ArrayOfTriangles (aCyl.VerticesNb() + 2 * aDisk.VerticesNb(), + 3 * (aCyl.TrianglesNb() + 2 * aDisk.TrianglesNb()), + Graphic3d_ArrayFlags_VertexNormal); + aCyl .FillArray (aTris, gp_Trsf()); + aDisk.FillArray (aTris, gp_Trsf()); + + gp_Trsf aDisk2Trsf; + aDisk2Trsf.SetTransformation (gp_Ax3 (gp_Pnt (0.0, 0.0, aHeight), -gp::DZ(), gp::DX()), gp::XOY()); + aDisk.FillArray (aTris, aDisk2Trsf); + + Handle(Graphic3d_Group) aGroupTris = thePrs->NewGroup(); + aGroupTris->SetGroupPrimitivesAspect (myDrawer->ShadingAspect()->Aspect()); + aGroupTris->AddPrimitiveArray (aTris); + aGroupTris->SetClosed (true); // will allow backface culling / capping for our solid object + + // manually tessellated disk + /*Handle(Graphic3d_ArrayOfTriangles) aTris2 = + new Graphic3d_ArrayOfTriangles (aNbSlices + 1, aNbSlices * 3, Graphic3d_ArrayFlags_VertexNormal); + aTris2->AddVertex (gp_Pnt (0.0, 0.0, aHeight), gp::DZ()); + for (int aSliceIter = 0; aSliceIter < aNbSlices; ++aSliceIter) + { + double anAngle = M_PI * 2.0 * double(aSliceIter) / double(aNbSlices); + aTris2->AddVertex (gp_Pnt (Cos (anAngle) * aRadius, Sin (anAngle) * aRadius, aHeight), gp::DZ()); + } + for (int aSliceIter = 0; aSliceIter < aNbSlices; ++aSliceIter) + { + aTris2->AddEdges (1, aSliceIter + 2, aSliceIter + 1 < aNbSlices ? (aSliceIter + 3) : 2); + } + aGroupTris->AddPrimitiveArray (aTris2);*/ + + // manually tessellate cylinder section as a polyline + Handle(Graphic3d_ArrayOfSegments) aSegs = new Graphic3d_ArrayOfSegments (4, 4 * 2, Graphic3d_ArrayFlags_None); + aSegs->AddVertex (gp_Pnt (0.0, -aRadius, 0.0)); + aSegs->AddVertex (gp_Pnt (0.0, -aRadius, aHeight)); + aSegs->AddVertex (gp_Pnt (0.0, aRadius, aHeight)); + aSegs->AddVertex (gp_Pnt (0.0, aRadius, 0.0)); + aSegs->AddEdges (1, 2); + aSegs->AddEdges (2, 3); + aSegs->AddEdges (3, 4); + aSegs->AddEdges (4, 1); + + Handle(Graphic3d_Group) aGroupSegs = thePrs->NewGroup(); + aGroupSegs->SetGroupPrimitivesAspect (myDrawer->WireAspect()->Aspect()); + aGroupSegs->AddPrimitiveArray (aSegs); + } + else if (theMode == MyDispMode_Highlight) + { + Bnd_Box aBox; + BRepBndLib::Add (aShape, aBox); + Prs3d_BndBox::Add (thePrs, aBox, myDrawer); + } +} + +//! Custom AIS owner. +class MyAisOwner : public SelectMgr_EntityOwner +{ + DEFINE_STANDARD_RTTI_INLINE(MyAisOwner, SelectMgr_EntityOwner) +public: + MyAisOwner (const Handle(MyAisObject)& theObj, int thePriority = 0) + : SelectMgr_EntityOwner (theObj, thePriority) {} + + void SetAnimation (const Handle(AIS_Animation)& theAnim) { myAnim = theAnim; } + + virtual void HilightWithColor (const Handle(PrsMgr_PresentationManager)& thePrsMgr, + const Handle(Prs3d_Drawer)& theStyle, + const Standard_Integer theMode) override; + virtual void Unhilight (const Handle(PrsMgr_PresentationManager)& thePrsMgr, + const Standard_Integer theMode) override; + + virtual bool IsForcedHilight() const override { return true; } + virtual bool HandleMouseClick (const Graphic3d_Vec2i& thePoint, + Aspect_VKeyMouse theButton, + Aspect_VKeyFlags theModifiers, + bool theIsDoubleClick) override; + virtual void SetLocation (const TopLoc_Location& theLocation) override + { + if (!myPrs.IsNull()) { myPrs->SetTransformation (new TopLoc_Datum3D (theLocation.Transformation())); } + } +protected: + Handle(Prs3d_Presentation) myPrs; + Handle(AIS_Animation) myAnim; +}; + +void MyAisOwner::HilightWithColor (const Handle(PrsMgr_PresentationManager)& thePrsMgr, + const Handle(Prs3d_Drawer)& theStyle, + const Standard_Integer theMode) +{ + (void )theMode; + MyAisObject* anObj = dynamic_cast (mySelectable); + if (myPrs.IsNull()) + { + myPrs = new Prs3d_Presentation (thePrsMgr->StructureManager()); + anObj->Compute (thePrsMgr, myPrs, MyAisObject::MyDispMode_Highlight); + } + if (thePrsMgr->IsImmediateModeOn()) + { + Handle(StdSelect_ViewerSelector3d) aSelector = anObj->InteractiveContext()->MainSelector(); + SelectMgr_SortCriterion aPickPnt; + for (int aPickIter = 1; aPickIter <= aSelector->NbPicked(); ++aPickIter) + { + if (aSelector->Picked (aPickIter) == this) + { + aPickPnt = aSelector->PickedData (aPickIter); + break; + } + } + + Handle(Prs3d_Presentation) aPrs = mySelectable->GetHilightPresentation (thePrsMgr); + aPrs->Clear(); + Handle(Graphic3d_Group) aGroupPnt = aPrs->NewGroup(); + aGroupPnt->SetGroupPrimitivesAspect (theStyle->ArrowAspect()->Aspect()); + + gp_Trsf aTrsfInv (mySelectable->InversedTransformation().Trsf()); + gp_Dir aNorm (aPickPnt.Normal.x(), aPickPnt.Normal.y(), aPickPnt.Normal.z()); + Handle(Graphic3d_ArrayOfTriangles) aTris = + Prs3d_Arrow::DrawShaded (gp_Ax1(aPickPnt.Point, aNorm).Transformed (aTrsfInv), + 1.0, 15.0, + 3.0, 4.0, 10); + aGroupPnt->AddPrimitiveArray (aTris); + + aPrs->SetZLayer (Graphic3d_ZLayerId_Top); + thePrsMgr->AddToImmediateList (aPrs); + + //Handle(Prs3d_PresentationShadow) aShadow = new Prs3d_PresentationShadow (thePrsMgr->StructureManager(), myPrs); + //aShadow->SetZLayer (Graphic3d_ZLayerId_Top); + //aShadow->Highlight (theStyle); + //thePrsMgr->AddToImmediateList (aShadow); + } + else + { + myPrs->SetTransformation (mySelectable->TransformationGeom()); + myPrs->Display(); + } +} + +void MyAisOwner::Unhilight (const Handle(PrsMgr_PresentationManager)& thePrsMgr, + const Standard_Integer theMode) +{ + (void )thePrsMgr; + (void )theMode; + if (!myPrs.IsNull()) + { + myPrs->Erase(); + } +} + +bool MyAisOwner::HandleMouseClick (const Graphic3d_Vec2i& thePoint, + Aspect_VKeyMouse theButton, + Aspect_VKeyFlags theModifiers, + bool theIsDoubleClick) +{ + (void )thePoint; + (void )theButton; + (void )theModifiers; + (void )theIsDoubleClick; + { + static math_BullardGenerator aRandGen; + Quantity_Color aRandColor (float(aRandGen.NextInt() % 256) / 255.0f, + float(aRandGen.NextInt() % 256) / 255.0f, + float(aRandGen.NextInt() % 256) / 255.0f, + Quantity_TOC_sRGB); + mySelectable->Attributes()->ShadingAspect()->SetColor(aRandColor); + mySelectable->SynchronizeAspects(); + } + + if (!myAnim.IsNull()) + { + static bool isFirst = true; + isFirst = !isFirst; + MyAisObject* anObj = dynamic_cast (mySelectable); + + gp_Trsf aTrsfTo; + aTrsfTo.SetRotation (gp_Ax1 (gp::Origin(), gp::DX()), isFirst ? M_PI * 0.5 : -M_PI * 0.5); + gp_Trsf aTrsfFrom = anObj->LocalTransformation(); + Handle(AIS_AnimationObject) anAnim = new AIS_AnimationObject ("MyAnim", anObj->InteractiveContext(), anObj, aTrsfFrom, aTrsfTo); + anAnim->SetOwnDuration (2.0); + + myAnim->Clear(); + myAnim->Add (anAnim); + myAnim->StartTimer (0.0, 1.0, true); + } + + return true; +} + +void MyAisObject::ComputeSelection (const Handle(SelectMgr_Selection)& theSel, + const Standard_Integer theMode) +{ + if (theMode != 0) + { + return; + } + + const double aRadius = 100.0, aHeight = 100.0; + TopoDS_Shape aShape = BRepPrimAPI_MakeCylinder (aRadius, aHeight); + Bnd_Box aBox; + BRepBndLib::Add (aShape, aBox); + Handle(MyAisOwner) anOwner = new MyAisOwner (this); + anOwner->SetAnimation (myAnim); + + Handle(Graphic3d_ArrayOfTriangles) aTris = Prs3d_ToolCylinder::Create (aRadius, aRadius, aHeight, 25, 25, gp_Trsf()); + Handle(Select3D_SensitivePrimitiveArray) aSensTri = new Select3D_SensitivePrimitiveArray (anOwner); + aSensTri->InitTriangulation (aTris->Attributes(), aTris->Indices(), TopLoc_Location()); + theSel->Add (aSensTri); + + //Handle(SelectMgr_EntityOwner) anOwner = new SelectMgr_EntityOwner (this); + //Handle(Select3D_SensitiveBox) aSensBox = new Select3D_SensitiveBox (anOwner, aBox); + //theSel->Add (aSensBox); +} + +} + +//======================================================================= +//function : QATutorialAisObject +//purpose : +//======================================================================= +static Standard_Integer QATutorialAisObject (Draw_Interpretor& theDi, + Standard_Integer theNbArgs, + const char** theArgVec) +{ + if (theNbArgs != 2) + { + theDi << "Syntax error: wrong number of arguments"; + return 1; + } + if (ViewerTest::GetAISContext().IsNull()) + { + theDi << "Syntax error: no active viewer"; + return 1; + } + + const TCollection_AsciiString aPrsName (theArgVec[1]); + + Handle(MyAisObject) aPrs = new MyAisObject(); + aPrs->SetAnimation (ViewerTest::CurrentEventManager()->ObjectsAnimation()); + ViewerTest::Display (aPrsName, aPrs); + return 0; +} + +//======================================================================= +//function : TutorialCommands +//purpose : +//======================================================================= +void QADraw::TutorialCommands (Draw_Interpretor& theCommands) +{ + const char* aGroup = "QA_Commands"; + + theCommands.Add ("QATutorialAisObject", + "QATutorialAisObject name", + __FILE__, QATutorialAisObject, aGroup); +} diff --git a/tests/demo/samples/aisobject b/tests/demo/samples/aisobject new file mode 100644 index 0000000000..420a4a1b6a --- /dev/null +++ b/tests/demo/samples/aisobject @@ -0,0 +1,14 @@ +puts "==============================" +puts "0032668: Documentation - add tutorial for creating a custom AIS Interactive Object" +puts "==============================" + +pload MODELING VISUALIZATION QAcommands +vclear +vinit View1 + +QATutorialAisObject p +vfit +vmoveto 300 300 +vdump $imagedir/${casename}.png + +puts "TEST COMPLETED"