计算机图形学 实现鼠标拖拽图元

计算机图形学 实现鼠标拖拽图元

问题描述

当计算机图形学中需要实现拖拽图元时,关键在于如何判断鼠标是否在拖拽,鼠标被拖拽时的移动

  1. 确定鼠标是在图元内部按下还是按下后移动到图元内
  2. 确定鼠标的移动,点击图元,图元的原点(锚点)不会瞬移至光标的位置

已知如下变量

bool isContained反映鼠标当下位置是否在图元内,true为在图元内,false为在图元外

bool isDraging反映鼠标此时是否被按下,true表示鼠标此时被按下,false表示鼠标此时未被按下

primitive图元结构体,有color和position成员

解决方案

问题一

对于第一个问题,我们可以发现鼠标一共有以下三种状态

  1. 鼠标松开

  2. 鼠标按下,且鼠标在图元外

  3. 鼠标按下,且鼠标在图元内

我们需要做的是区分这三种状态,我们可以用一个int型变量p,用0,1,2分别表示这三种状态,关键在于这三种状态如何转换

0→1:当鼠标按下(isDraging),鼠标在图元内(isContained),鼠标上一时刻处于松开状态(p == 0

0→2:当鼠标按下(isDraging),鼠标不在图元内(!isContained),鼠标上一时刻处于松开状态(p == 0

1→02→0:当鼠标松开(!isDraging

这部分代码如下:

//用static变量可以让p在在循环中只初始化一次
static int p = 0;
if (isDraging)
{
    if (isContained)
    {
        if (p == 0) p = 1;
    }
    else
    {
        if (p == 0) p = 2;
    }
}
else
{
    p = 0;
}
问题二

对于第二个问题,我们需要找到鼠标移动过程中的位移,我们不能直接将鼠标的NDC坐标赋值给图元坐标(这样会导致当鼠标在图元内按下时图元立刻跳到鼠标位置),而是应该将鼠标从开始拖拽那一时刻的坐标到此时鼠标的坐标的位移向量坐标加上图元上一时刻坐标赋值给图元此时坐标

具体如下:

如下图所示,鼠标由B移动到D,图元由A移动到C

image-20211026113415304

p状态0→1时记录此时的鼠标坐标B,图元位置A,计算 A B → \overrightarrow{AB} AB

p处于状态1时,表示正在拖拽图元,鼠标坐标D,图元坐标为C,此时更新图元坐标,即 C 点 坐 标 = D 点 坐 标 − A B → C点坐标=D点坐标-\overrightarrow{AB} C=DAB

代码实现如下

注:其中的csugl命名空间为自己建立的,参考逻辑实现即可,理解问题的解决方案之后就可以自己写了

void MouseDragger(const glm::ivec2 &winSize, csugl::Ref<Primitive> primitive)
{
    // 鼠标在图元内
    static bool isContained = false;
    // 鼠标正被按下
    static bool isDraging = false;
    // 拖拽过程是否开始
    static bool flag = false;
    // 鼠标位移差
    static glm::vec2 changeCurNDCpos = {0.0f, 0.0f};

    // 1.获取鼠标窗口坐标:
    // 2.转换为NDC坐标: (使用WindowPosToNDCPos转换)
    // 3.是否在拖拽:
    // 4.是否在图元内: (使用primitive的is_contained检测)
    // 5.设置图元颜色:
    // 6.设置图元新坐标:

    float xPos, yPos;
    xPos = csugl::Input::GetMousePosX();
    yPos = csugl::Input::GetMousePosY();
    glm::vec2 curpos = {xPos, yPos};
    glm::vec2 curNDCpos = WindowPosToNDCPos(curpos, winSize);

    isDraging = csugl::Input::IsMouseButtonPressed(csugl::Mouse::Button0);
    isContained = primitive->is_contained(curNDCpos);

    //变量p用于判断是否在图元内点击鼠标,0表示鼠标松开,1表示鼠标按下且在图元内,2表示鼠标按下且在图元外
    static int p = 0;
    if (isDraging) {
        if (isContained) {
            if (p == 0) {
                changeCurNDCpos = curNDCpos - primitive->position;
                p = 1;
            }
        } else {
            if (p == 0) {
                p = 2;
            }
        }
    } else {
        p = 0;
    }
    if(p==1) {
        primitive->color = {0.1f, 0.5f, 0.8f};
        primitive->position = curNDCpos - changeCurNDCpos;
    } else {
        primitive->color = {0.8f, 0.5f, 0.1f};
    }
}

// 将窗口坐标转换为标准设备坐标(NDC)
glm::vec2 WindowPosToNDCPos(const glm::vec2 &winPosition, const glm::vec2 &winSize)
{
    glm::vec2 tmpPos = winPosition / winSize * 2.0f - 1.0f; //转化为float求解
    tmpPos.y = -tmpPos.y;//坐标是上下相反的
    return tmpPos;
}
  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值