Open CASCADE学习|显示文本

本文介绍了如何在OpenCASCADE的3DViewer中扩展代码,以支持文本显示功能,包括创建`Viewer`类,添加`TopoDS_Shape`和`AIS_TextLabel`对象,以及实现消息循环和窗口事件处理。
摘要由CSDN通过智能技术生成

目录

1、修改代码

Viewer.h:

Viewer.cpp:

2、显示文本

OpenCasCade 你好啊

霜吹花落


1、修改代码

在文章《Open CASCADE学习|显示模型》基础上,增加部分代码,实现对文本显示的支持,具体如下:

Viewer.h:

//-----------------------------------------------------------------------------// Created on: 24 August 2017//-----------------------------------------------------------------------------// Copyright (c) 2017, Sergey Slyadnev (sergey.slyadnev@gmail.com)// All rights reserved.//// Redistribution and use in source and binary forms, with or without// modification, are permitted provided that the following conditions are met:////    * Redistributions of source code must retain the above copyright//      notice, this list of conditions and the following disclaimer.//    * Redistributions in binary form must reproduce the above copyright//      notice, this list of conditions and the following disclaimer in the//      documentation and/or other materials provided with the distribution.//    * Neither the name of the copyright holder(s) nor the//      names of all contributors may be used to endorse or promote products//      derived from this software without specific prior written permission.//// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE// DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.//-----------------------------------------------------------------------------#pragma once#ifdef _WIN32#include <Windows.h>#endif// Local includes#include "ViewerInteractor.h"// OpenCascade includes#include <TopoDS_Shape.hxx>#include <AIS_TextLabel.hxx>#include <WNT_Window.hxx>// Standard includes#include <vector>class V3d_Viewer;class V3d_View;class AIS_InteractiveContext;class AIS_ViewController;//-----------------------------------------------------------------------------//! Simple 3D viewer.class Viewer{public:    Viewer(const int left,        const int top,        const int width,        const int height);public:    Viewer& operator<<(const TopoDS_Shape& shape)    {        this->AddShape(shape);        return *this;    }    Viewer& operator<<(const Handle(AIS_TextLabel)& label)    {        this->AddLabel(label);        return *this;    }    void AddShape(const TopoDS_Shape& shape);    void AddLabel(const Handle(AIS_TextLabel)& label);    void StartMessageLoop();    void StartMessageLoop2();private:    static LRESULT WINAPI        wndProcProxy(HWND hwnd,            UINT message,            WPARAM wparam,            LPARAM lparam);    LRESULT CALLBACK        wndProc(HWND hwnd,            UINT message,            WPARAM wparam,            LPARAM lparam);    void init(const HANDLE& windowHandle);    /* API-related things */private:    std::vector<TopoDS_Shape> m_shapes; //!< Shapes to visualize.    std::vector<Handle(AIS_TextLabel)> m_labels; //!< Shapes to visualize.    /* OpenCascade's things */private:    Handle(V3d_Viewer)             m_viewer;    Handle(V3d_View)               m_view;    Handle(AIS_InteractiveContext) m_context;    Handle(WNT_Window)             m_wntWindow;    Handle(ViewerInteractor)       m_evtMgr;    /* Lower-level things */private:    HINSTANCE m_hInstance; //!< Handle to the instance of the module.    HWND      m_hWnd;      //!< Handle to the instance of the window.    bool      m_bQuit;     //!< Indicates whether user want to quit from window.};

Viewer.cpp:

//-----------------------------------------------------------------------------
// Created on: 24 August 2017
//-----------------------------------------------------------------------------
// Copyright (c) 2017, Sergey Slyadnev (sergey.slyadnev@gmail.com)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//    * Redistributions of source code must retain the above copyright
//      notice, this list of conditions and the following disclaimer.
//    * Redistributions in binary form must reproduce the above copyright
//      notice, this list of conditions and the following disclaimer in the
//      documentation and/or other materials provided with the distribution.
//    * Neither the name of the copyright holder(s) nor the
//      names of all contributors may be used to endorse or promote products
//      derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

// Own include
#include "Viewer.h"

// OpenCascade includes
#include <AIS_InteractiveContext.hxx>
#include <AIS_Shape.hxx>
#include <Aspect_DisplayConnection.hxx>
#include <Aspect_Handle.hxx>
#include <OpenGl_GraphicDriver.hxx>
#include <V3d_AmbientLight.hxx>
#include <V3d_DirectionalLight.hxx>
#include <V3d_View.hxx>
#include <V3d_Viewer.hxx>

namespace {
    //! Adjust the style of local selection.
    //! \param[in] context the AIS context.
    void AdjustSelectionStyle(const Handle(AIS_InteractiveContext)& context)
    {
        // Initialize style for sub-shape selection.
        Handle(Prs3d_Drawer) selDrawer = new Prs3d_Drawer;
        //
        selDrawer->SetLink(context->DefaultDrawer());
        selDrawer->SetFaceBoundaryDraw(true);
        selDrawer->SetDisplayMode(1); // Shaded
        selDrawer->SetTransparency(0.5f);
        selDrawer->SetZLayer(Graphic3d_ZLayerId_Topmost);
        selDrawer->SetColor(Quantity_NOC_GOLD);
        selDrawer->SetBasicFillAreaAspect(new Graphic3d_AspectFillArea3d());

        // Adjust fill area aspect.
        const Handle(Graphic3d_AspectFillArea3d)&
            fillArea = selDrawer->BasicFillAreaAspect();
        //
        fillArea->SetInteriorColor(Quantity_NOC_GOLD);
        fillArea->SetBackInteriorColor(Quantity_NOC_GOLD);
        //
        fillArea->ChangeFrontMaterial().SetMaterialName(Graphic3d_NOM_NEON_GNC);
        fillArea->ChangeFrontMaterial().SetTransparency(0.4f);
        fillArea->ChangeBackMaterial().SetMaterialName(Graphic3d_NOM_NEON_GNC);
        fillArea->ChangeBackMaterial().SetTransparency(0.4f);

        selDrawer->UnFreeBoundaryAspect()->SetWidth(1.0);

        // Update AIS context.
        context->SetHighlightStyle(Prs3d_TypeOfHighlight_LocalSelected, selDrawer);
    }
}

//-----------------------------------------------------------------------------

Viewer::Viewer(const int left,
    const int top,
    const int width,
    const int height)
    : m_hWnd(NULL),
    m_bQuit(false)
{
    // Register the window class once
    static HINSTANCE APP_INSTANCE = NULL;
    if (APP_INSTANCE == NULL)
    {
        APP_INSTANCE = GetModuleHandleW(NULL);
        m_hInstance = APP_INSTANCE;

        WNDCLASSW WC;
        WC.cbClsExtra = 0;
        WC.cbWndExtra = 0;
        WC.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
        WC.hCursor = LoadCursor(NULL, IDC_ARROW);
        WC.hIcon = LoadIcon(NULL, IDI_APPLICATION);
        WC.hInstance = APP_INSTANCE;
        WC.lpfnWndProc = (WNDPROC)wndProcProxy;
        WC.lpszClassName = L"OpenGLClass";
        WC.lpszMenuName = 0;
        WC.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;

        if (!RegisterClassW(&WC))
        {
            return;
        }
    }

    // Set coordinates for window's area rectangle.
    RECT Rect;
    SetRect(&Rect,
        left, top,
        left + width, top + height);

    // Adjust window rectangle.
    AdjustWindowRect(&Rect, WS_OVERLAPPEDWINDOW, false);

    // Create window.
    m_hWnd = CreateWindow(L"OpenGLClass",
        L"Quaoar >>> 3D",
        WS_OVERLAPPEDWINDOW,
        Rect.left, Rect.top, // Adjusted x, y positions
        Rect.right - Rect.left, Rect.bottom - Rect.top, // Adjusted width and height
        NULL, NULL,
        m_hInstance,
        this);

    // Check if window has been created successfully.
    if (m_hWnd == NULL)
    {
        return;
    }

    // Show window finally.
    ShowWindow(m_hWnd, TRUE);

    HANDLE windowHandle = (HANDLE)m_hWnd;

    this->init(windowHandle);
}

//-----------------------------------------------------------------------------

void Viewer::AddShape(const TopoDS_Shape& shape)
{
    m_shapes.push_back(shape);
}


void Viewer::AddLabel(const Handle(AIS_TextLabel)& label)
{
    m_labels.push_back(label);
}
//-----------------------------------------------------------------------------

//! Starts message loop.
void Viewer::StartMessageLoop()
{
    for (auto sh : m_shapes)
    {
        Handle(AIS_Shape) shape = new AIS_Shape(sh);
        m_context->Display(shape, true);
        m_context->SetDisplayMode(shape, AIS_Shaded, true);

        // Adjust selection style.
        ::AdjustSelectionStyle(m_context);

        // Activate selection modes.
        m_context->Activate(4, true); // faces
        m_context->Activate(2, true); // edges
    }

    MSG Msg;
    while (!m_bQuit)
    {
        switch (::MsgWaitForMultipleObjectsEx(0, NULL, 12, QS_ALLINPUT, 0))
        {
        case WAIT_OBJECT_0:
        {
            while (::PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE))
            {
                if (Msg.message == WM_QUIT)
                    m_bQuit = true;// return;

                ::TranslateMessage(&Msg);
                ::DispatchMessage(&Msg);
            }
        }
        }
    }
}

//! Starts message loop.
void Viewer::StartMessageLoop2()
{
    for (auto sh : m_labels)
    {
        //Handle(AIS_Shape) shape = new AIS_Shape(sh);
        m_context->Display(sh,0);
        //m_context->SetDisplayMode(shape, AIS_Shaded, true);

        // Adjust selection style.
        ::AdjustSelectionStyle(m_context);

        // Activate selection modes.
        m_context->Activate(4, true); // faces
        m_context->Activate(2, true); // edges
    }

    MSG Msg;
    while (!m_bQuit)
    {
        switch (::MsgWaitForMultipleObjectsEx(0, NULL, 12, QS_ALLINPUT, 0))
        {
        case WAIT_OBJECT_0:
        {
            while (::PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE))
            {
                if (Msg.message == WM_QUIT)
                    m_bQuit = true;// return;

                ::TranslateMessage(&Msg);
                ::DispatchMessage(&Msg);
            }
        }
        }
    }
}

//-----------------------------------------------------------------------------

void Viewer::init(const HANDLE& windowHandle)
{
    static Handle(Aspect_DisplayConnection) displayConnection;
    //
    if (displayConnection.IsNull())
        displayConnection = new Aspect_DisplayConnection();

    HWND winHandle = (HWND)windowHandle;
    //
    if (winHandle == NULL)
        return;

    // Create OCCT viewer.
    Handle(OpenGl_GraphicDriver)
        graphicDriver = new OpenGl_GraphicDriver(displayConnection, false);

    m_viewer = new V3d_Viewer(graphicDriver);

    // Lightning.
    Handle(V3d_DirectionalLight) LightDir = new V3d_DirectionalLight(V3d_Zneg, Quantity_Color(Quantity_NOC_GRAY97), 1);
    Handle(V3d_AmbientLight)     LightAmb = new V3d_AmbientLight();
    //
    LightDir->SetDirection(1.0, -2.0, -10.0);
    //
    m_viewer->AddLight(LightDir);
    m_viewer->AddLight(LightAmb);
    m_viewer->SetLightOn(LightDir);
    m_viewer->SetLightOn(LightAmb);

    // AIS context.
    m_context = new AIS_InteractiveContext(m_viewer);

    // Configure some global props.
    const Handle(Prs3d_Drawer)& contextDrawer = m_context->DefaultDrawer();
    //
    if (!contextDrawer.IsNull())
    {
        const Handle(Prs3d_ShadingAspect)& SA = contextDrawer->ShadingAspect();
        const Handle(Graphic3d_AspectFillArea3d)& FA = SA->Aspect();
        contextDrawer->SetFaceBoundaryDraw(true); // Draw edges.
        FA->SetEdgeOff();

        // Fix for inifinite lines has been reduced to 1000 from its default value 500000.
        contextDrawer->SetMaximalParameterValue(1000);
    }

    // Main view creation.
    m_view = m_viewer->CreateView();
    m_view->SetImmediateUpdate(false);

    // Event manager is constructed when both contex and view become available.
    m_evtMgr = new ViewerInteractor(m_view, m_context);

    // Aspect window creation
    m_wntWindow = new WNT_Window(winHandle);
    m_view->SetWindow(m_wntWindow, nullptr);
    //
    if (!m_wntWindow->IsMapped())
    {
        m_wntWindow->Map();
    }
    m_view->MustBeResized();

    // View settings.
    m_view->SetShadingModel(V3d_PHONG);

    // Configure rendering parameters
    Graphic3d_RenderingParams& RenderParams = m_view->ChangeRenderingParams();
    RenderParams.IsAntialiasingEnabled = true;
    RenderParams.NbMsaaSamples = 8; // Anti-aliasing by multi-sampling
    RenderParams.IsShadowEnabled = false;
    RenderParams.CollectedStats = Graphic3d_RenderingParams::PerfCounters_NONE;
}

//-----------------------------------------------------------------------------

LRESULT WINAPI Viewer::wndProcProxy(HWND   hwnd,
    UINT   message,
    WPARAM wparam,
    LPARAM lparam)
{
    if (message == WM_CREATE)
    {
        // Save pointer to our class instance (sent on window create) to window storage.
        CREATESTRUCTW* pCreateStruct = (CREATESTRUCTW*)lparam;
        SetWindowLongPtr(hwnd, int(GWLP_USERDATA), (LONG_PTR)pCreateStruct->lpCreateParams);
    }

    // Get pointer to our class instance.
    Viewer* pThis = (Viewer*)GetWindowLongPtr(hwnd, int(GWLP_USERDATA));
    return (pThis != NULL) ? pThis->wndProc(hwnd, message, wparam, lparam)
        : DefWindowProcW(hwnd, message, wparam, lparam);
}

//-----------------------------------------------------------------------------

//! Window procedure.
LRESULT Viewer::wndProc(HWND   hwnd,
    UINT   message,
    WPARAM wparam,
    LPARAM lparam)
{
    if (m_view.IsNull())
        return DefWindowProc(hwnd, message, wparam, lparam);

    switch (message)
    {
    case WM_PAINT:
    {
        PAINTSTRUCT aPaint;
        BeginPaint(m_hWnd, &aPaint);
        EndPaint(m_hWnd, &aPaint);
        m_evtMgr->ProcessExpose();
        break;
    }
    case WM_SIZE:
    {
        m_evtMgr->ProcessConfigure();
        break;
    }
    case WM_MOVE:
    case WM_MOVING:
    case WM_SIZING:
    {
        switch (m_view->RenderingParams().StereoMode)
        {
        case Graphic3d_StereoMode_RowInterlaced:
        case Graphic3d_StereoMode_ColumnInterlaced:
        case Graphic3d_StereoMode_ChessBoard:
        {
            // track window moves to reverse stereo pair
            m_view->MustBeResized();
            m_view->Update();
            break;
        }
        default:
            break;
        }
        break;
    }
    case WM_KEYUP:
    case WM_KEYDOWN:
    {
        const Aspect_VKey vkey = WNT_Window::VirtualKeyFromNative((int)wparam);
        //
        if (vkey != Aspect_VKey_UNKNOWN)
        {
            const double timeStamp = m_evtMgr->EventTime();
            if (message == WM_KEYDOWN)
            {
                m_evtMgr->KeyDown(vkey, timeStamp);
            }
            else
            {
                m_evtMgr->KeyUp(vkey, timeStamp);
            }
        }
        break;
    }
    case WM_LBUTTONUP:
    case WM_MBUTTONUP:
    case WM_RBUTTONUP:
    case WM_LBUTTONDOWN:
    case WM_MBUTTONDOWN:
    case WM_RBUTTONDOWN:
    {
        const Graphic3d_Vec2i pos(LOWORD(lparam), HIWORD(lparam));
        const Aspect_VKeyFlags flags = WNT_Window::MouseKeyFlagsFromEvent(wparam);
        Aspect_VKeyMouse button = Aspect_VKeyMouse_NONE;
        //
        switch (message)
        {
        case WM_LBUTTONUP:
        case WM_LBUTTONDOWN:
            button = Aspect_VKeyMouse_LeftButton;
            break;
        case WM_MBUTTONUP:
        case WM_MBUTTONDOWN:
            button = Aspect_VKeyMouse_MiddleButton;
            break;
        case WM_RBUTTONUP:
        case WM_RBUTTONDOWN:
            button = Aspect_VKeyMouse_RightButton;
            break;
        }
        if (message == WM_LBUTTONDOWN
            || message == WM_MBUTTONDOWN
            || message == WM_RBUTTONDOWN)
        {
            SetFocus(hwnd);
            SetCapture(hwnd);

            if (!m_evtMgr.IsNull())
                m_evtMgr->PressMouseButton(pos, button, flags, false);
        }
        else
        {
            ReleaseCapture();

            if (!m_evtMgr.IsNull())
                m_evtMgr->ReleaseMouseButton(pos, button, flags, false);
        }

        m_evtMgr->FlushViewEvents(m_context, m_view, true);
        break;
    }
    case WM_MOUSEWHEEL:
    {
        const int    delta = GET_WHEEL_DELTA_WPARAM(wparam);
        const double deltaF = double(delta) / double(WHEEL_DELTA);
        //
        const Aspect_VKeyFlags flags = WNT_Window::MouseKeyFlagsFromEvent(wparam);
        //
        Graphic3d_Vec2i pos(int(short(LOWORD(lparam))), int(short(HIWORD(lparam))));
        POINT cursorPnt = { pos.x(), pos.y() };
        if (ScreenToClient(hwnd, &cursorPnt))
        {
            pos.SetValues(cursorPnt.x, cursorPnt.y);
        }

        if (!m_evtMgr.IsNull())
        {
            m_evtMgr->UpdateMouseScroll(Aspect_ScrollDelta(pos, deltaF, flags));
            m_evtMgr->FlushViewEvents(m_context, m_view, true);
        }
        break;
    }
    case WM_MOUSEMOVE:
    {
        Graphic3d_Vec2i pos(LOWORD(lparam), HIWORD(lparam));
        Aspect_VKeyMouse buttons = WNT_Window::MouseButtonsFromEvent(wparam);
        Aspect_VKeyFlags flags = WNT_Window::MouseKeyFlagsFromEvent(wparam);

        // don't make a slide-show from input events - fetch the actual mouse cursor position
        CURSORINFO cursor;
        cursor.cbSize = sizeof(cursor);
        if (::GetCursorInfo(&cursor) != FALSE)
        {
            POINT cursorPnt = { cursor.ptScreenPos.x, cursor.ptScreenPos.y };
            if (ScreenToClient(hwnd, &cursorPnt))
            {
                // as we override mouse position, we need overriding also mouse state
                pos.SetValues(cursorPnt.x, cursorPnt.y);
                buttons = WNT_Window::MouseButtonsAsync();
                flags = WNT_Window::MouseKeyFlagsAsync();
            }
        }

        if (m_wntWindow.IsNull() || (HWND)m_wntWindow->HWindow() != hwnd)
        {
            // mouse move events come also for inactive windows
            break;
        }

        if (!m_evtMgr.IsNull())
        {
            m_evtMgr->UpdateMousePosition(pos, buttons, flags, false);
            m_evtMgr->FlushViewEvents(m_context, m_view, true);
        }
        break;
    }
    default:
    {
        break;
    }

    case WM_DESTROY:
        m_bQuit = true;
    }
    return DefWindowProc(hwnd, message, wparam, lparam);
}

2、显示文本

OpenCasCade 你好啊

#include "TCollection_ExtendedString.hxx"#include "Resource_Unicode.hxx"#include "AIS_TextLabel.hxx"#include "AIS_InteractiveContext.hxx"#include"Viewer.h"int main() {    TCollection_ExtendedString tostr;    Standard_CString str = "OpenCasCade 你好啊";    Resource_Unicode::ConvertGBToUnicode(str, tostr);    Handle(AIS_TextLabel) aLabel = new AIS_TextLabel();    aLabel->SetText(tostr);    aLabel->SetColor(Quantity_NOC_RED);    aLabel->SetFont("SimHei");    Viewer vout(50, 50, 500, 500);    vout << aLabel;    vout.StartMessageLoop2();    return 0;    }

霜吹花落

#include "TCollection_ExtendedString.hxx"#include "Resource_Unicode.hxx"#include "AIS_TextLabel.hxx"#include "AIS_InteractiveContext.hxx"#include"Viewer.h"int main() {    TCollection_ExtendedString tostr;    Standard_CString str = "霜吹花落";    Resource_Unicode::ConvertGBToUnicode(str, tostr);    Handle(AIS_TextLabel) aLabel = new AIS_TextLabel();    aLabel->SetPosition(gp_Pnt(0, 0, 0));    aLabel->SetText(tostr);    aLabel->SetHJustification(Graphic3d_HTA_CENTER);    aLabel->SetVJustification(Graphic3d_VTA_CENTER);    aLabel->SetHeight(50);    aLabel->SetZoomable(false);//如果允许缩放,你会发现放大后字太难看了    aLabel->SetAngle(0);    aLabel->SetColor(Quantity_NOC_YELLOW);    aLabel->SetFont("SimHei");//一定要设置合适的字体,不然不能实现功能    //如果有一天你需要鼠标选中文本时,下面代码有用,高亮文本    Handle(Prs3d_Drawer) aStyle = new Prs3d_Drawer(); // 获取高亮风格    aLabel->SetHilightAttributes(aStyle);    aStyle->SetMethod(Aspect_TOHM_COLOR);    aStyle->SetColor(Quantity_NOC_RED);    // 设置高亮颜色    aStyle->SetDisplayMode(1); // 整体高亮    aLabel->SetDynamicHilightAttributes(aStyle);    Viewer vout(50, 50, 500, 500);    vout << aLabel;    vout.StartMessageLoop2();    return 0;    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值