You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

OpenGL中如何单独拖拽移动多个2D图形(正方形、圆角矩形)

解决OpenGL图形独立拖拽问题

第一步:给图形添加独立属性

不能用全局变量控制所有图形的位置,要给每个图形单独存储位置、尺寸等属性。比如定义结构体:

typedef struct {
    float x, y;          // 图形的基准坐标(比如左上角或中心,根据你的绘图逻辑定)
    float width, height; // 图形尺寸
    int type;            // 图形类型标记:0=正方形,1=圆角矩形
} Shape;

// 示例图形数组,可根据需求添加更多
Shape shapes[] = {
    {100, 100, 50, 50, 0},   // 正方形:位置(100,100),边长50
    {200, 200, 60, 40, 1}    // 圆角矩形:位置(200,200),宽60高40
};
int shapeCount = sizeof(shapes)/sizeof(shapes[0]);
int selectedShape = -1; // 选中图形的索引,-1表示未选中任何图形
float offsetX, offsetY; // 鼠标点击点相对于选中图形的偏移量

第二步:实现点击位置的坐标转换与图形命中判断

GLUT鼠标坐标以窗口左上角为原点,而OpenGL常用左下角为原点,需要先做坐标转换;再判断点击点是否落在图形范围内:

// 把GLUT鼠标坐标转换为OpenGL坐标
void convertMouseCoord(int mx, int my, float *ox, float *oy) {
    *ox = mx;
    *oy = glutGet(GLUT_WINDOW_HEIGHT) - my;
}

// 判断点(px, py)是否在目标图形内
int isPointInShape(float px, float py, Shape *shape) {
    switch(shape->type) {
        case 0: // 正方形判断(假设x,y是左上角坐标)
            return (px >= shape->x && px <= shape->x + shape->width &&
                    py >= shape->y && py <= shape->y + shape->height);
        case 1: // 圆角矩形先按普通矩形判断,精确圆角判断可后续优化
            return (px >= shape->x && px <= shape->x + shape->width &&
                    py >= shape->y && py <= shape->y + shape->height);
        default:
            return 0;
    }
}

第三步:处理鼠标事件回调

鼠标按下事件(解决Q2:点击非图形区域不触发移动)

void mouseDown(int button, int state, int mx, int my) {
    if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {
        float ox, oy;
        convertMouseCoord(mx, my, &ox, &oy);
        selectedShape = -1;
        // 遍历所有图形,判断是否点击到某一个
        for (int i=0; i<shapeCount; i++) {
            if (isPointInShape(ox, oy, &shapes[i])) {
                selectedShape = i;
                // 记录鼠标相对于图形的偏移,避免拖拽时图形瞬移
                offsetX = ox - shapes[i].x;
                offsetY = oy - shapes[i].y;
                break;
            }
        }
    }
}

鼠标拖拽事件(解决Q1:仅移动选中的图形)

void mouseDrag(int mx, int my) {
    if (selectedShape != -1) { // 仅当有选中图形时处理拖拽
        float ox, oy;
        convertMouseCoord(mx, my, &ox, &oy);
        // 更新选中图形的位置,保持鼠标点击点在图形上的相对位置不变
        shapes[selectedShape].x = ox - offsetX;
        shapes[selectedShape].y = oy - offsetY;
        glutPostRedisplay(); // 触发画面重绘
    }
}

鼠标释放事件

void mouseUp(int button, int state, int mx, int my) {
    if (button == GLUT_LEFT_BUTTON && state == GLUT_UP) {
        selectedShape = -1; // 释放鼠标后取消选中状态
    }
}

第四步:修改Display函数,基于图形数组绘制

把原来的固定绘图逻辑改成遍历图形数组,用每个图形自身的坐标做平移:

void Display() {
    glClear(GL_COLOR_BUFFER_BIT);
    glLoadIdentity();

    for (int i=0; i<shapeCount; i++) {
        glutPushMatrix();
        glTranslatef(shapes[i].x, shapes[i].y, 0);
        // 根据图形类型调用对应绘图函数
        if (shapes[i].type == 0) {
            drawsquare(); // 注意:该函数要基于原点绘制(比如从(0,0)画到(width,height))
        } else if (shapes[i].type == 1) {
            drawRoundRect(); // 同理,基于原点绘制
        }
        glutPopMatrix();
    }

    glutSwapBuffers();
}

第五步:注册鼠标回调函数

在main函数中添加鼠标事件的回调注册:

int main(int argc, char** argv) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
    glutInitWindowSize(800, 600);
    glutCreateWindow("Draggable Shapes");

    // 初始化正交投影,让坐标和窗口像素对应
    glClearColor(1.0, 1.0, 1.0, 1.0);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(0, 800, 0, 600);

    // 注册回调
    glutDisplayFunc(Display);
    glutMouseFunc(mouseDown);
    glutMotionFunc(mouseDrag);
    glutMouseUpFunc(mouseUp);

    glutMainLoop();
    return 0;
}

关键注意事项

  • 绘图函数要基于原点绘制:如果你的drawsquare()或drawRoundRect()是用固定坐标绘制的,必须改成以(0,0)为基准,这样glTranslatef才能正确控制图形位置。
  • 精确碰撞优化(可选):如果需要精确判断圆角矩形的圆角区域,可通过计算点到圆角圆心的距离是否小于圆角半径来实现,新手可先从矩形判断入手。

内容的提问来源于stack exchange,提问作者Wiki

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 11:35:13