qt-自制基本图形绘制器

一个QT新手,记录学习历程
在qt官方文档没有找到搜索功能,每次都是一个个找函数位置,如果你知道方便的查找某个函数的文档,欢迎评论告诉我

之前学计算机图形学做了一个图形绘制器,这次弄相当于一个更新。用了部分助教提供的架构设计以及复用了上次自己的代码,所以也许你会感觉似曾相识(原项目也在github就是CG那个)。

边写的时候有修修补补,前后可能不一致

UI界面设计

  • 如何添加图片资源
  • 程序图标
  • 文档-QtoolBar
    右键MainWindow添加一个toolBar工具栏,在工具栏中添加action对象
    QT添加工具栏
  • 画布,用一个Qlabel划定范围,注意大小和位置
    文档- qimage
    fille函数

其他

  • 拖拽的时候要看好位置放到工具栏里面
  • 右键toolBar添加分隔符
  • 命名:推荐保留action前缀,表示这是一个Qaction动作
  • 加图片资源的时候突然发现原来可以鼠标选中一片图形,我原来都是乖巧地一个一个加,可浪费时间

qt-自制基本图形绘制器_第1张图片
这个弄完之后长这样
qt-自制基本图形绘制器_第2张图片

特别说明:界面参考@GeeeekExplorer(应该是我上一届的学长),图片来源也是从这位同学项目资源(https://github.com/GeeeekExplorer/NJU-Computer-Graphics/tree/master/rc,并不是自己制作!非常感谢学长,如果您不希望我使用这些图片,可以联系我剔除。我的邮箱是[email protected]
其他图片,我也没想到啥好方法,就用procreate自己画(又是procreate大材小用的一天)

UI界面不更新

设计完界面之后做槽函数绑定,加入事件

一个无情的文档搬运工

槽机制
文档-鼠标事件
文档-Qwidget-鼠标移动,按下,释放函数都在同页面
文档-绘图事件
文档-Qwidget-绘图函数
文档-Qwidget-窗口大小
文档-画笔
文档-笔刷
文档-qlist
文档-qvector

现在的大体结构
qt-自制基本图形绘制器_第3张图片

qt-自制基本图形绘制器_第4张图片

绘制

绘图事件

  • 先写paint事件的坏处是忘记了resize事件,画布直接没有初始化(dogie)
  • 要转成像素图,不然image显示不出来霍
void MainWindow::paintEvent(QPaintEvent *event){
    Q_UNUSED(event);
    m_image.fill(Qt::white);
    m_painter.begin(&m_image);
    for(Shape* p:m_shapes){
        m_painter.setPen(p->pen());
        m_painter.drawPoints(p->points());
    }
    m_painter.end();
    ui->label->setPixmap(QPixmap::fromImage(m_image));
}

void MainWindow::resizeEvent(QResizeEvent *event){
    Q_UNUSED(event);
    ui->label->resize(ui->centralwidget->size());
    m_image = QImage(ui->label->size(), QImage::Format_RGB32);
}

直线:DDA/Bresenham/中点画线法

嗯,只能水平竖直线,而且位置也不对
qt-自制基本图形绘制器_第5张图片
原因是坐标没有转换:QT获取鼠标位置
现在位置对了,但是只有水平竖直线
qt-自制基本图形绘制器_第6张图片
改着改着就疯狂未响应,我整个电脑都卡了
qt-自制基本图形绘制器_第7张图片
在这里插入图片描述
原因是我合并情况写错了,所以死循环了,嗯。。。内心复杂
至于为什么只有水平竖直,因为这里我把初始点写了int,所以算增量的时候截断成0了
在这里插入图片描述
现在可以了,检查的时候可以一个点按下然后转一圈看各种形态的直线
qt-自制基本图形绘制器_第8张图片

//用的是DDA
if (m_args.size()!=2)
        return {};
    double x0 = m_args[0].x(),y0 = m_args[0].y();
    double x1 = m_args[1].x(),y1 = m_args[1].y();
    QVector<QPoint>points_tmp;
    points_tmp.append(QPoint(x0,y0));
    if (x0 == x1 || y0==y1){//水平、竖直
        int dx= x0==x1?0:(x0<x1?1:-1);
        int dy= y0==y1?0:(y0<y1?1:-1);
        while(x0!=x1 || y0!=y1){
            x0+=dx;
            y0+=dy;
            points_tmp.append(QPoint(x0,y0));
        }
        return  points_tmp;
    }
    double dx = 0,dy = 0;
    double px = qAbs(x0-x1),py = qAbs(y0-y1);
    if ( px > py ){
        dx = x0<x1?1:-1;
        dy = (y1-y0)/px;
        while(x0 != x1){
            x0+=dx;
            y0+=dy;
            points_tmp.append(QPoint(x0,y0));
        }
    }
    else{
        dy = y0<y1?1:-1;
        dx = (x1-x0)/py;
        while(y0 != y1){
            x0+=dx;
            y0+=dy;
            points_tmp.append(QPoint(x0,y0));
        }
    }

    return  points_tmp;

合并了下其他形态的直线

 //其他直线
    double dx = 0,dy = 0;
    double px = qAbs(x0-x1),py = qAbs(y0-y1);
    dx = px>py?(x0<x1?1:-1):(x1-x0)/py;
    dy = px>py?(y1-y0)/px:(y0<y1?1:-1);
    while( (px>=py &&x0 != x1) || (px<py && y0 != y1)){
        x0+=dx;
        y0+=dy;
        points_tmp.append(QPoint(x0,y0));
    }

然后合并全形态直线。
总结起来就是先看增长方向,增长方向是增量0,1,-1,另一个方向是斜率/斜率倒数
循环条件是增长方向

	assert(m_args.size() == 2);
    double x0 = m_args[0].x(),y0 = m_args[0].y();
    double x1 = m_args[1].x(),y1 = m_args[1].y();
    QVector<QPoint>points_tmp;
    points_tmp.append(QPoint(x0,y0));
    double dx = 0,dy = 0;
    double px = qAbs(x0-x1),py = qAbs(y0-y1);
    dx = px>=py?(x0==x1?0:(x0<x1?1:-1)):(x1-x0)/py;
    dy = px>py?(y1-y0)/px:(y0==y1?0:(y0<y1?1:-1));
    while( (px>=py &&x0 != x1) || (px<py && y0 != y1)){
        x0+=dx;
        y0+=dy;
        points_tmp.append(QPoint(x0,y0));
    }

多边形-多个点按序用直线连接

文档-append-追加一个vector
我一开始是想这样有三个点之后加入集合,后来想到如果大小是1,2的时候创建了对象又换了状态,这个对象没放到shapes里面,不好删除。所以就还是改成创建的时候就加入
qt-自制基本图形绘制器_第9张图片
qt-自制基本图形绘制器_第10张图片
别忘记首尾相连啊

int sz = m_args.size();
    if (sz<2)
        return {};
    Line* tmp_line = new Line({m_args[0],m_args[1]},m_pen);
    QVector<QPoint>points_tmp;
    for(int i=1;i<sz;++i){
        tmp_line->setArgs({m_args[i-1],m_args[i]});
        points_tmp.append(tmp_line->points());
    }
    tmp_line->setArgs({m_args[sz-1],m_args[0]});
    points_tmp.append(tmp_line->points());
    delete tmp_line;
    return points_tmp;

qt-自制基本图形绘制器_第11张图片

三角形+矩形+连接线:在多边形的基础上

等腰三角形,按下点为顶点,移动鼠标另一点与顶点构成腰,另一边对称计算点坐标
矩形,按下点和移动点作为左上和右下点,其他点对称计算
按下一个点然后转一圈看各种形态的三角形,矩形
连接线:首尾不闭合的多边形
qt-自制基本图形绘制器_第12张图片

圆形+椭圆

中点圆生成算法
按下一个点作为圆形,拖动鼠标确定半径
按下以及拖动形成边框,在边框内绘制椭圆
按下一个点然后转一圈看各种形态的圆,椭圆

def draw_ellipse(p_list):
    """绘制椭圆(采用中点圆生成算法)

    :param p_list: (list of list of int: [[x0, y0], [x1, y1]]) 椭圆的矩形包围框左上角和右下角顶点坐标;
    :return: (list of list of int: [[x_0, y_0], [x_1, y_1], [x_2, y_2], ...]) 绘制结果的像素点坐标列表
    """
    #计算椭圆中心和rx,ry
    x0, y0 = p_list[0]
    x1, y1 = p_list[1]
    result = []
    xc = (x0 + x1) / 2
    yc = (y0 + y1) / 2
    rx = abs(x1 - xc)
    ry = abs(y1 - yc)
    rx2 = pow(rx, 2)
    ry2 = pow(ry, 2)
    x = 0
    y = ry
    pk = ry2 - rx2 * ry + rx2 / 4
    #####区域1,+1和-0.5是两个区域连接部分有些缺口,自调的
    while ry2 * (x + 1) < rx2 * (y - 0.5):
        if pk < 0:
            pk += ry2 * (2 * x + 3)
            x += 1
            result.append([x, y])
        else:
            pk += ry2 * (2 * x + 3) + rx2 * (2 - 2 * y)
            x += 1
            y -= 1
            result.append([x, y])
    pk = ry * (x + 0.5) * 2 + rx * 2 * (y - 1) - rx * 2 * ry
    ###区域2
    while y > 0:
        if pk < 0:
            x += 1
            y -= 1
            pk += 2 * ry2 * x - 2 * rx2 * y + rx2
            result.append([x, y])
        else:
            y -= 1
            pk += rx2 - 2 * rx2 * y
            result.append([x, y])
    ###加入对称点
    length = len(result)
    for i in range(length):
        basex, basey = result[i]
        result.append([-basex + xc, basey + yc])
        result.append([-basex + xc, -basey + yc])
        result.append([basex + xc, -basey + yc])
        result[i] = [basex + xc, basey + yc]
    return result

圆是八分图,取一份然后做对称
中点圆生成算法
qt-自制基本图形绘制器_第13张图片
椭圆是四分图,每一分里按xy分为两个部分绘制(圆是xy对换,椭圆得分别算)
qt-自制基本图形绘制器_第14张图片

变幻

首先确定怎么选中图形
确定鼠标落点
图形是空心的,所以用区域contain不太好,我写的是判断点集
因为很难精确选中,所以给了容错空间

平移

把所有参数平移,再绘制
RE: 重新绘制增加计算量,还是把所有point都平移了

旋转

组合图形

复制

撤回

删除

。。。。。。。。。。。。。。。。。。
项目写完了,博客不想写了【lazy】

你可能感兴趣的:(项目)