Open CASCADE学习|ApplicationFramework 框架使用指南
在现代 CAD(计算机辅助设计)应用开发中,构建一个高效、可扩展且用户友好的应用程序框架是至关重要的。Open CASCADE(简称 OCC)提供了一个功能强大的 ApplicationFramework(应用程序框架),它为开发者提供了一套完整的工具集,用于构建基于文档 - 视图架构的 CAD 应用程序。本文将深入探讨 Open CASCADE 的 ApplicationFramework 框架,并通过一个完整的代码示例来展示其使用方法。
一、引言
随着 CAD 技术的发展,应用程序的复杂性不断增加。开发者需要处理大量的图形数据、用户交互以及复杂的几何运算。Open CASCADE 的 ApplicationFramework 框架为这些问题提供了标准化的解决方案。它基于文档 - 视图模式,允许开发者将数据管理与用户界面分离,从而提高了代码的可维护性和可扩展性。
二、ApplicationFramework 框架概述
ApplicationFramework 是 Open CASCADE 中一个高级的应用程序开发框架,它集成了文档管理、视图渲染、命令处理和资源管理等多个方面。以下是其核心组件的简要介绍:
-
文档管理(Document Management)
- 文档是数据的容器,用于存储 CAD 模型及相关属性。TDocStd_Document 类提供了文档的基本功能,可以扩展来自定义文档类型。
-
视图渲染(View Rendering)
- 视图负责显示文档中的数据。V3d_Viewer 和 V3d_View 类提供了 3D 视图的创建和管理功能,AIS_InteractiveContext 类用于处理交互式图形显示。
-
命令处理(Command Processing)
- 命令系统允许用户通过界面或脚本执行操作。TFunction_Framework 提供了命令的定义和执行机制,支持撤销 / 重做功能。
-
资源管理(Resource Management)
- 资源管理涉及用户界面元素(如菜单、工具栏)的定义和本地化。ApplicationFramework 提供了资源文件的加载和管理功能。
三、基于 ApplicationFramework 的 CAD 应用程序开发
接下来,我们将通过一个示例来展示如何使用 Open CASCADE 的 ApplicationFramework 构建一个简单的 CAD 应用程序。该示例将实现基本的图形创建、显示和交互功能。
3.1 示例代码分析
以下是完整的示例代码,展示了如何实现一个具有简单图形创建和交互功能的 CAD 应用程序:
#include <windows.h>
#include <vector>
#include <commctrl.h>
#include <TDocStd_Application.hxx>
#include <TDocStd_Document.hxx>
#include <TDataStd_Name.hxx>
#include <TNaming_Builder.hxx>
#include <TNaming_NamedShape.hxx>
#include <TPrsStd_AISViewer.hxx>
#include <XCAFDoc_DocumentTool.hxx>
#include <AIS_InteractiveContext.hxx>
#include <V3d_Viewer.hxx>
#include <V3d_View.hxx>
#include <OpenGl_GraphicDriver.hxx>
#include <WNT_Window.hxx>
#include <BRepPrimAPI_MakeBox.hxx>
#include <BRepPrimAPI_MakeSphere.hxx>
#include <BRepPrimAPI_MakeCone.hxx>
#include <AIS_Shape.hxx>
#include <Standard_DefineHandle.hxx>
#include <Standard_Transient.hxx>
#include <Standard_Type.hxx>
#include <TDF_Label.hxx>
#include <TDF_ChildIterator.hxx>
#include <Quantity_Color.hxx>
#include <Aspect_TypeOfTriedronPosition.hxx>
#include <TopTools_HSequenceOfShape.hxx>
#include <TopoDS_Shape.hxx>#pragma comment(lib, "comctl32.lib")enum class AppCommand {CMD_NONE,CMD_CREATE_BOX = 1001, // 使用更大的ID值避免冲突CMD_CREATE_SPHERE,CMD_CREATE_CONE,CMD_DELETE_OBJ,CMD_ZOOM_ALL
};class CADDocument : public TDocStd_Document {
public:CADDocument(const TCollection_ExtendedString& format): TDocStd_Document(format) {}void AddShape(const TopoDS_Shape& shape, const TCollection_AsciiString& name) {TDF_Label rootLabel = Main();TDF_Label shapeLabel = rootLabel.FindChild(1, Standard_True);TNaming_Builder builder(shapeLabel);builder.Generated(shape);TDataStd_Name::Set(shapeLabel, name);}
};class CADApplicationFramework {
public:CADApplicationFramework(HINSTANCE hInst): hInstance(hInst) {InitApplication();}void InitApplication() {app = new TDocStd_Application();TCollection_AsciiString format("CADDocument");app->NewDocument(format, myDocument);myGraphicDriver = new OpenGl_GraphicDriver(nullptr);myViewer = new V3d_Viewer(myGraphicDriver);myContext = new AIS_InteractiveContext(myViewer);InitMainWindow();InitToolbar();}void InitMainWindow() {WNDCLASSW wc = { 0 };wc.lpfnWndProc = MainWndProc;wc.hInstance = hInstance;wc.lpszClassName = L"CADAppWindow";wc.hCursor = LoadCursor(NULL, IDC_ARROW);RegisterClassW(&wc);hMainWnd = CreateWindowW(L"CADAppWindow", L"CAD Application",WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,800, 600, NULL, NULL, hInstance, this);Create3DView();ShowWindow(hMainWnd, SW_SHOW);}void Create3DView() {myView = myViewer->CreateView();Handle(WNT_Window) wntWindow = new WNT_Window(hMainWnd);myView->SetWindow(wntWindow);myView->SetBackgroundColor(Quantity_NOC_GRAY40);myView->MustBeResized();myView->TriedronDisplay(Aspect_TOTP_LEFT_LOWER, Quantity_NOC_WHITE, 0.1);}void InitToolbar() {hToolbar = CreateWindowExW(0, TOOLBARCLASSNAMEW, NULL,WS_CHILD | WS_VISIBLE | TBSTYLE_FLAT | CCS_TOP | CCS_NODIVIDER,0, 0, 0, 0, hMainWnd, (HMENU)1, hInstance, NULL);SendMessageW(hToolbar, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0);SendMessageW(hToolbar, TB_SETBUTTONSIZE, 0, MAKELPARAM(60, 24));TBBUTTON tbButtons[] = {{0, (int)AppCommand::CMD_CREATE_BOX, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, (INT_PTR)L"Box"},{1, (int)AppCommand::CMD_CREATE_SPHERE, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, (INT_PTR)L"Sphere"},{2, (int)AppCommand::CMD_CREATE_CONE, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, (INT_PTR)L"Cone"},{0, 0, TBSTATE_ENABLED, BTNS_SEP, {0}, 0, 0},{3, (int)AppCommand::CMD_DELETE_OBJ, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, (INT_PTR)L"Delete"},{0, 0, TBSTATE_ENABLED, BTNS_SEP, {0}, 0, 0},{4, (int)AppCommand::CMD_ZOOM_ALL, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, (INT_PTR)L"Zoom"}};SendMessageW(hToolbar, TB_ADDBUTTONS, sizeof(tbButtons) / sizeof(TBBUTTON), (LPARAM)&tbButtons);SendMessageW(hToolbar, TB_AUTOSIZE, 0, 0);}void CreateBox() {BRepPrimAPI_MakeBox boxMaker(30, 30, 30);Handle(AIS_Shape) aisShape = new AIS_Shape(boxMaker.Shape());myContext->Display(aisShape, Standard_True);myView->FitAll();}void CreateSphere() {BRepPrimAPI_MakeSphere sphereMaker(20);Handle(AIS_Shape) aisShape = new AIS_Shape(sphereMaker.Shape());myContext->Display(aisShape, Standard_True);myView->FitAll();}void CreateCone() {BRepPrimAPI_MakeCone coneMaker(15, 5, 25);Handle(AIS_Shape) aisShape = new AIS_Shape(coneMaker.Shape());myContext->Display(aisShape, Standard_True);myView->FitAll();}void DeleteSelected() {myContext->InitSelected();if (myContext->MoreSelected()) {Handle(AIS_Shape) shape = Handle(AIS_Shape)::DownCast(myContext->SelectedInteractive());myContext->Remove(shape, Standard_True);myView->Redraw();}}static LRESULT CALLBACK MainWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {CADApplicationFramework* pThis = nullptr;if (msg == WM_CREATE) {pThis = static_cast<CADApplicationFramework*>(reinterpret_cast<CREATESTRUCT*>(lParam)->lpCreateParams);SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pThis));}else {pThis = reinterpret_cast<CADApplicationFramework*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));}if (pThis) {switch (msg) {case WM_COMMAND: {if (HIWORD(wParam) == BN_CLICKED) {switch (LOWORD(wParam)) {case (int)AppCommand::CMD_CREATE_BOX:pThis->CreateBox();break;case (int)AppCommand::CMD_CREATE_SPHERE:pThis->CreateSphere();break;case (int)AppCommand::CMD_CREATE_CONE:pThis->CreateCone();break;case (int)AppCommand::CMD_DELETE_OBJ:pThis->DeleteSelected();break;case (int)AppCommand::CMD_ZOOM_ALL:pThis->myView->FitAll();break;}}break;}case WM_SIZE: {SendMessageW(pThis->hToolbar, TB_AUTOSIZE, 0, 0);RECT rcClient;GetClientRect(hWnd, &rcClient);RECT rcToolbar;GetWindowRect(pThis->hToolbar, &rcToolbar);int toolbarHeight = rcToolbar.bottom - rcToolbar.top;rcClient.top += toolbarHeight;pThis->myView->MustBeResized();Handle(WNT_Window) wntWindow = Handle(WNT_Window)::DownCast(pThis->myView->Window());if (!wntWindow.IsNull()) {wntWindow->SetPos(rcClient.left, rcClient.top,rcClient.right - rcClient.left,rcClient.bottom - rcClient.top);}break;}case WM_DESTROY:PostQuitMessage(0);break;default:return DefWindowProc(hWnd, msg, wParam, lParam);}return 0;}return DefWindowProc(hWnd, msg, wParam, lParam);}private:HINSTANCE hInstance;HWND hMainWnd;HWND hToolbar;Handle(TDocStd_Application) app;Handle(TDocStd_Document) myDocument;Handle(OpenGl_GraphicDriver) myGraphicDriver;Handle(V3d_Viewer) myViewer;Handle(AIS_InteractiveContext) myContext;Handle(V3d_View) myView;
};int APIENTRY wWinMain(_In_ HINSTANCE hInstance,_In_opt_ HINSTANCE hPrevInstance,_In_ LPWSTR lpCmdLine,_In_ int nCmdShow) {INITCOMMONCONTROLSEX icex;icex.dwSize = sizeof(INITCOMMONCONTROLSEX);icex.dwICC = ICC_BAR_CLASSES;InitCommonControlsEx(&icex);CADApplicationFramework app(hInstance);MSG msg;while (GetMessage(&msg, NULL, 0, 0)) {TranslateMessage(&msg);DispatchMessage(&msg);}return (int)msg.wParam;
}
3.2 代码解析
-
文档类(CADDocument)
- 继承自 TDocStd_Document,用于存储 CAD 模型数据。
AddShape
方法将形状添加到文档的数据框架中,并为其设置名称。
-
应用程序框架类(CADApplicationFramework)
- 构造函数中初始化应用程序,包括创建文档、图形驱动、视图和交互上下文。
InitMainWindow
和InitToolbar
方法分别初始化主窗口和工具栏,设置窗口过程函数以处理用户交互。Create3DView
方法创建 3D 视图并设置其显示参数。CreateBox
、CreateSphere
和CreateCone
方法用于创建基本几何体并将其显示在视图中。DeleteSelected
方法用于删除选中的图形对象。
-
窗口过程函数(MainWndProc)
- 处理窗口消息,如命令点击、窗口大小调整和销毁消息。
- 根据不同的命令 ID 调用相应的图形创建或操作方法。
-
主函数(wWinMain)
- 初始化公共控件,创建 CADApplicationFramework 实例,并进入消息循环。
四、应用程序架构建议
-
分层架构
- 数据层:负责数据的存储和管理,主要基于 TDF(Toolkit for Data Framework)。
- 业务逻辑层:包含应用程序的核心功能,如几何计算、数据处理等。
- 用户界面层:处理用户交互,展示图形和数据。
-
模块化设计
- 将不同的功能模块化,如图形创建、编辑、显示等,便于维护和扩展。
-
资源管理
- 使用资源文件管理用户界面元素,支持多语言和本地化。
-
命令模式
- 将用户操作封装为命令对象,支持撤销 / 重做功能。
五、常见问题及解决方案
-
图形显示问题
- 问题:图形显示不正确或不更新。
- 解决方案:确保在窗口大小调整后调用
MustBeResized
和Redraw
方法;检查视图的背景色和显示模式设置。
-
文档保存和加载
- 问题:保存的文档无法正确加载。
- 解决方案:确保所有数据都正确提交到文档的数据框架;检查文件路径和权限。
-
命令执行失败
- 问题:执行命令时出现异常或未按预期工作。
- 解决方案:检查命令是否正确注册;确保所有依赖的对象(如文档、视图)已正确初始化。
六、进阶功能
-
多文档界面(MDI)
- 支持同时打开和编辑多个文档,实现文档间的切换和比较。
-
高级图形交互
- 实现复杂的交互操作,如拖拽、旋转、缩放等,提升用户体验。
-
插件系统
- 开发插件系统,允许第三方扩展应用程序的功能。
-
网络协作
- 实现多用户协同编辑功能,支持实时数据同步。
七、总结
Open CASCADE 的 ApplicationFramework 提供了一个强大的基础,用于构建功能丰富的 CAD 应用程序。通过合理利用其文档 - 视图架构、命令处理机制和资源管理功能,开发者可以高效地构建出满足不同需求的 CAD 软件。本文提供的示例代码展示了 ApplicationFramework 的基本使用方法,开发者可以在此基础上进一步扩展和优化,以实现更复杂的应用场景。
在实际开发中,建议深入研究 Open CASCADE 的官方文档和示例代码,结合具体需求进行定制化开发。同时,关注性能优化和用户体验,确保应用程序在各种硬件配置和使用场景下都能稳定运行。