Open CASCADE学习|小球沿样条曲线运动
1. 样条曲线的创建
在OpenCASCADE中,样条曲线(B样条曲线)是通过控制点来定义的。我们使用GeomAPI_PointsToBSpline类来根据给定的控制点生成B样条曲线。

TColgp_Array1OfPnt points(1, 4);
points.SetValue(1, gp_Pnt(-20, -20, 0));
points.SetValue(2, gp_Pnt(0, 30, 20));
points.SetValue(3, gp_Pnt(40, -20, 0));
points.SetValue(4, gp_Pnt(60, 30, 20));
GeomAPI_PointsToBSpline bsplineBuilder(points);
Handle(Geom_BSplineCurve) curve = bsplineBuilder.Curve();
- TColgp_Array1OfPnt用于存储控制点。
- GeomAPI_PointsToBSpline根据控制点生成B样条曲线。
- curve是生成的B样条曲线对象。
2. 球体的创建
使用BRepPrimAPI_MakeSphere类创建一个球体。
BRepPrimAPI_MakeSphere makeSphere(3.0); // 半径为3
TopoDS_Shape sphere = makeSphere.Shape();
Handle(AIS_Shape) aisSphere = new AIS_Shape(sphere);
- BRepPrimAPI_MakeSphere用于创建球体。
- sphere是球体的形状对象。
- aisSphere是用于显示球体的AIS_Shape对象。
3. 显示样条曲线和球体
将样条曲线和球体添加到视图中进行显示。
TopoDS_Edge edge = BRepBuilderAPI_MakeEdge(curve);
Handle(AIS_Shape) aisCurve = new AIS_Shape(edge);
context->Display(aisCurve, Standard_True);
context->Display(aisSphere, Standard_True);
- BRepBuilderAPI_MakeEdge将曲线转换为边。
- aisCurve是用于显示曲线的AIS_Shape对象。
- context->Display将对象添加到视图中显示。
4. 动画的实现
动画的核心是通过不断更新球体的位置来实现的。我们通过一个循环逐步改变球体在样条曲线上的位置。
void StartAnimation() {
    double uStart = curve->FirstParameter();
    double uEnd = curve->LastParameter();
    double t = 0.0;
    const double delta = 0.01;
    const int delayMs = 30;
    while (t <= 1.0) {
        MSG msg;
        while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
            if (msg.message == WM_QUIT) {
                return;
            }
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        double u = uStart + t * (uEnd - uStart);
        gp_Pnt point;
        curve->D0(u, point);
        gp_Trsf trsf;
        trsf.SetTranslation(gp_Vec(point.X(), point.Y(), point.Z()));
        aisSphere->SetLocalTransformation(trsf);
        context->UpdateCurrentViewer();
        view->Redraw();
        std::this_thread::sleep_for(std::chrono::milliseconds(delayMs));
        t += delta;
    }
}
- uStart和- uEnd是样条曲线的参数范围。
- t是动画的参数,从0到1逐步增加。
- u是当前的曲线参数,根据- t计算得到。
- curve->D0(u, point)计算曲线在参数- u处的点- point。
- gp_Trsf用于创建平移变换,将球体移动到- point位置。
- aisSphere->SetLocalTransformation(trsf)更新球体的位置。
- context->UpdateCurrentViewer()和- view->Redraw()更新视图并重绘。
- std::this_thread::sleep_for用于控制动画的速度。
5. 按钮点击事件
按钮点击事件触发动画函数StartAnimation。
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    switch (msg) {
    case WM_COMMAND:
        if (LOWORD(wParam) == 1) { // 按钮点击事件
            StartAnimation();
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProcW(hwnd, msg, wParam, lParam);
    }
    return 0;
}
- WM_COMMAND消息处理按钮点击事件。
- LOWORD(wParam) == 1判断点击的是哪个按钮。
- 调用StartAnimation函数开始动画。
6. 完整代码
#include <Windows.h>
#include <AIS_Shape.hxx>
#include <AIS_InteractiveContext.hxx>
#include <V3d_Viewer.hxx>
#include <V3d_View.hxx>
#include <OpenGl_GraphicDriver.hxx>
#include <WNT_Window.hxx>
#include <GeomAPI_PointsToBSpline.hxx>
#include <TColgp_Array1OfPnt.hxx>
#include <BRepPrimAPI_MakeSphere.hxx>
#include <BRepBuilderAPI_MakeEdge.hxx>
#include <gp_Pnt.hxx>
#include <gp_Trsf.hxx>
#include <TopoDS_Edge.hxx>
#include <Geom_BSplineCurve.hxx>
#include <chrono>
#include <thread>
// 全局变量,用于存储OpenCASCADE对象
Handle(AIS_Shape) aisSphere;
Handle(Geom_BSplineCurve) curve;
Handle(V3d_View) view;
Handle(AIS_InteractiveContext) context;
// 创建OpenCASCADE窗口
HWND CreateOCCWindow() {
    HINSTANCE hInstance = GetModuleHandle(NULL);
    WNDCLASSW wc = { 0 };
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = DefWindowProcW;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wc.lpszClassName = L"OCCWindowClass";
    RegisterClassW(&wc);
    HWND hwnd = CreateWindowW(
        L"OCCWindowClass",
        L"OpenCASCADE Animation",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, 1024, 768,
        NULL, NULL, hInstance, NULL
    );
    ShowWindow(hwnd, SW_SHOW);
    UpdateWindow(hwnd);
    return hwnd;
}
// 动画函数
void StartAnimation() {
    double uStart = curve->FirstParameter();
    double uEnd = curve->LastParameter();
    double t = 0.0;
    const double delta = 0.01;
    const int delayMs = 30;
    while (t <= 1.0) {
        MSG msg;
        while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
            if (msg.message == WM_QUIT) {
                return;
            }
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        double u = uStart + t * (uEnd - uStart);
        gp_Pnt point;
        curve->D0(u, point);
        gp_Trsf trsf;
        trsf.SetTranslation(gp_Vec(point.X(), point.Y(), point.Z()));
        aisSphere->SetLocalTransformation(trsf);
        context->UpdateCurrentViewer();
        view->Redraw();
        std::this_thread::sleep_for(std::chrono::milliseconds(delayMs));
        t += delta;
    }
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    switch (msg) {
    case WM_COMMAND:
        if (LOWORD(wParam) == 1) { // 按钮点击事件
            // 调用动画函数
            StartAnimation();
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProcW(hwnd, msg, wParam, lParam);
    }
    return 0;
}
int main() {
    // 创建窗口
    HWND hwnd = CreateOCCWindow();
    // 创建按钮
    CreateWindowW(
        L"BUTTON",
        L"Start Animation",
        WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
        10, 10, 100, 30,
        hwnd,
        (HMENU)1,
        NULL,
        NULL
    );
    // 设置窗口过程
    SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (LONG_PTR)WndProc);
    Handle(WNT_Window) window = new WNT_Window(hwnd);
    // 初始化图形驱动
    Handle(OpenGl_GraphicDriver) graphicDriver = new OpenGl_GraphicDriver(new Aspect_DisplayConnection());
    // 创建Viewer
    Handle(V3d_Viewer) viewer = new V3d_Viewer(graphicDriver);
    viewer->SetDefaultLights();
    viewer->SetLightOn();
    // 创建View
    Handle(V3d_View) view_temp = viewer->CreateView();
    view_temp->SetWindow(window);
    view_temp->TriedronDisplay(Aspect_TOTP_LEFT_LOWER, Quantity_NOC_GOLD, 0.08, V3d_WIREFRAME);
    view_temp->SetBackgroundColor(Quantity_NOC_BLACK);
    view_temp->MustBeResized();
    view_temp->Redraw();
    // 创建InteractiveContext
    Handle(AIS_InteractiveContext) context_temp = new AIS_InteractiveContext(viewer);
    context_temp->SetDisplayMode(AIS_Shaded, Standard_True);
    // 创建样条曲线
    TColgp_Array1OfPnt points(1, 4);
    points.SetValue(1, gp_Pnt(-20, -20, 0));
    points.SetValue(2, gp_Pnt(0, 30, 20));
    points.SetValue(3, gp_Pnt(40, -20, 0));
    points.SetValue(4, gp_Pnt(60, 30, 20));
    GeomAPI_PointsToBSpline bsplineBuilder(points);
    Handle(Geom_BSplineCurve) curve_temp = bsplineBuilder.Curve();
    // 创建曲线Edge并显示
    TopoDS_Edge edge = BRepBuilderAPI_MakeEdge(curve_temp);
    Handle(AIS_Shape) aisCurve = new AIS_Shape(edge);
    context_temp->Display(aisCurve, Standard_True);
    // 创建球体
    BRepPrimAPI_MakeSphere makeSphere(3.0);
    TopoDS_Shape sphere = makeSphere.Shape();
    Handle(AIS_Shape) aisSphere_temp = new AIS_Shape(sphere);
    context_temp->Display(aisSphere_temp, Standard_True);
    // 调整视图以适应内容
    view_temp->FitAll();
    // 将全局变量赋值
    aisSphere = aisSphere_temp;
    curve = curve_temp;
    view = view_temp;
    context = context_temp;
    // 保持窗口打开,直到用户关闭
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return 0;
}

