Dust3D项目实训十一 | 基于silhouetteimaggenerator的图像轮廓绘制分析

2021SC@SDUSC

目录

分析概括

模块功能

QPainter类

void QPainter::drawEllipse(int x, int y, int width, int height)

QPainterPath类

关键代码分析

silhouetteimagegenerator.h分析

Snapshot类分析

SilhouetteImageGenerator类分析

silhouetteimagegenerator.cpp分析

generate()分析


分析概括

模块功能

silhouetteimaggenerator模块的主要功能在于通过输入的二维图像绘制关键点轮廓圆和点与点之间的连接关系,如下图中的昆虫绿色部分轮廓:

QPainter类

QPainter提供了高度优化的功能,可以完成GUI程序所需的大多数绘图。它可以绘制从简单的线条到复杂的形状(如馅饼和和弦)的所有内容。它还可以绘制对齐的文本和像素映射。

(QPainter类函数介绍:QPainter class |Qt GUI 5.15.7

1.开启抗锯齿(反走样)功能。

painter.setRenderHint(QPainter::Antialiasing, true);

2.绘制由以(x, y)开头的矩形定义的椭圆

void QPainter::drawEllipse(int x, int y, int width, int height)

3.其他绘图接口

drawPoint(), drawPoints() 画点,参数是QPoint对象。

drawLine(), drawLines() 绘制直线,参数是QLine对象。

drawRect(), drawRoundedRect(),drawRects()绘制矩形,后者为圆角矩形。参数为QRect对象。

drawArc() 绘制弧线,参数是QRect对象和起点终点角度。

drawPie() 绘制扇形,参数是QRect对象和起点终点角度。

drawChord() 绘制弦,参数是QRect对象和起点终点角度。

drawPolyline()绘制多点连接的线,参数是QPoints对象。

drawPolygon()绘制多边形,参数是QPoints对象。

drawConvexPolygon() 绘制凸多边形,参数是QPoints对象。

fillRect() 填充一个矩形,无边框线。参数是QRect对象和QBrush对象。

eraseRect() 擦除某个背景区域,显示窗体背景色。参数是QRect对象。

QPainterPath类

QPainterPath类提供了一个容器,用于绘图操作,可以创建和重用图形形状。QPainterPath是一个图形构建块的对象,如矩形、椭圆、直线和曲线。构建块可以加入在封闭的子路径中,例如:矩形或椭圆形。一个封闭的路径同时存在开始点和结束点。或者作为未封闭的子路径独立存在,如:直线和曲线。QPainterPath可以进行填充、显示轮廓和裁剪。要生成可填充的轮廓的绘图路径,可以使用QPainterPathStroker类。QPainterPath比正常绘制的主要优点在于:复杂的图形只需创建一次,然后可以仅仅通过调用QPainter::drawPath()函数来进行多次绘制。

关键代码分析

silhouetteimagegenerator.h分析

Snapshot类分析

class Snapshot
{
public:
    std::map<QString, QString> canvas;画布
    std::map<QString, std::map<QString, QString>> nodes;节点图
    std::map<QString, std::map<QString, QString>> edges;边图
    std::map<QString, std::map<QString, QString>> parts;构件图
    std::map<QString, std::map<QString, QString>> components;组成图
    std::map<QString, QString> rootComponent;骨架根
    std::map<QString, std::map<QString, QString>> motions;动作
    std::vector<std::pair<std::map<QString, QString>, std::vector<std::pair<std::map<QString, QString>, std::vector<std::map<QString, QString>>>>>> materials; // std::pair<Material attributes, layers>  layer: std::pair<Layer attributes, maps>

    void resolveBoundingBox(QRectF *mainProfile, QRectF *sideProfile, const QString &partId=QString()) const;
};

SilhouetteImageGenerator类分析

SilhouetteImageGenerator类定义了生成轮廓的一些列函数与变量,其中的Snapshot 类是在Snapshot .h中定义的

class SilhouetteImageGenerator : public QObject
{
    Q_OBJECT
public:
    SilhouetteImageGenerator(int width, int height, Snapshot *snapshot);//初始化构造函数
    ~SilhouetteImageGenerator();//析构函数
    QImage *takeResultImage();//产生结果图像
    void generate();//轮廓产生函数
signals:
    void finished();
public slots:
    void process();
private:
    int m_width = 0;//宽度
    int m_height = 0;//高度
    QImage *m_resultImage = nullptr;//结果图像
    Snapshot *m_snapshot = nullptr;
};

silhouetteimagegenerator.cpp分析

generate()分析

generate()函数描述了具体绘制轮廓的过程使用了QT中的QPainter类进行绘制,分别进行了关键点所在圆绘制、关键点间的路径轮廓绘制等

void SilhouetteImageGenerator::generate()
{
    if (m_width <= 0 || m_height <= 0 || nullptr == m_snapshot)
    若高度、宽度或snapshot不符合要求,直接返回
        return;
    
    delete m_resultImage;
    //QImage::Format_ARGB32:每个像素有四个分量(alpha、red、green、blue)
    m_resultImage = new QImage(m_width, m_height, QImage::Format_ARGB32);
    //为m_resultImage填充颜色
    m_resultImage->fill(QColor(0xE1, 0xD2, 0xBD));
    
    struct NodeInfo
    {//设置结构体存储节点信息
        QVector3D position;
        float radius;
    };
    
    std::map<QString, NodeInfo> nodePositionMap;
    for (const auto &node: m_snapshot->nodes) {
        //对于每一个节点node初始化节点信息
        NodeInfo nodeInfo;
        //初始化node节点的坐标
        nodeInfo.position = QVector3D(valueOfKeyInMapOrEmpty(node.second, "x").toFloat(),
            valueOfKeyInMapOrEmpty(node.second, "y").toFloat(),
            valueOfKeyInMapOrEmpty(node.second, "z").toFloat());
        //初始化node节点的半径
        nodeInfo.radius = valueOfKeyInMapOrEmpty(node.second, "radius").toFloat();
        在nodePositionMap中插入该节点以及节点信息
        nodePositionMap.insert({node.first, nodeInfo});
    }
    
    QPainter painter;//创建一个画布
    painter.begin(m_resultImage);//当前画笔的要画到m_resultImage中
    painter.setRenderHint(QPainter::Antialiasing);//开启反走样功能
    painter.setRenderHint(QPainter::HighQualityAntialiasing);

    painter.setPen(Qt::NoPen);
    //笔刷的设置
    QBrush brush;
    brush.setColor(QColor(0x88, 0x80, 0x73));
    brush.setStyle(Qt::SolidPattern);
    
    painter.setBrush(brush);
    
    for (const auto &it: nodePositionMap) {
        const auto &nodeInfo = it.second;
        //绘制(nodeInfo.position.x() - nodeInfo.radius) * m_height和(nodeInfo.position.y() - nodeInfo.radius) * m_height定义矩形区域内的椭圆
        painter.drawEllipse((nodeInfo.position.x() - nodeInfo.radius) * m_height,
            (nodeInfo.position.y() - nodeInfo.radius) * m_height,
            nodeInfo.radius * m_height * 2.0,
            nodeInfo.radius * m_height * 2.0);
            
        painter.drawEllipse((nodeInfo.position.z() - nodeInfo.radius) * m_height,
            (nodeInfo.position.y() - nodeInfo.radius) * m_height,
            nodeInfo.radius * m_height * 2.0,
            nodeInfo.radius * m_height * 2.0);
    }
    
    for (int round = 0; round < 2; ++round) {
        if (1 == round)
            painter.setCompositionMode(QPainter::CompositionMode_Multiply);//将图像合成模式设置为合并
        for (const auto &edge: m_snapshot->edges) {

            //valueOfKeyInMapOrEmpty():映射中键的值或为空,若非空,赋予此非空值,否则返回
            QString partId = valueOfKeyInMapOrEmpty(edge.second, "partId");
            QString fromNodeId = valueOfKeyInMapOrEmpty(edge.second, "from");
            QString toNodeId = valueOfKeyInMapOrEmpty(edge.second, "to");
            const auto &fromNodeInfo = nodePositionMap[fromNodeId];
            const auto &toNodeInfo = nodePositionMap[toNodeId];
      
            {
                QVector3D pointerOut = QVector3D(0.0, 0.0, 1.0);
                //圆心的箭头指向
                QVector3D direction = (QVector3D(toNodeInfo.position.x(), toNodeInfo.position.y(), 0.0)-QVector3D(fromNodeInfo.position.x(), fromNodeInfo.position.y(), 0.0)).normalized();
                //crossProduct(第一个变量与第二个标量形成的向量)
                QVector3D fromBaseDirection = QVector3D::crossProduct(pointerOut, direction);
                QVector3D fromBaseRadius = fromBaseDirection * fromNodeInfo.radius;
                //第一个基点的坐标
                QVector3D fromBaseFirstPoint = QVector3D(fromNodeInfo.position.x(), fromNodeInfo.position.y(), 0.0) - fromBaseRadius;
                //第二个基点的坐标
                QVector3D fromBaseSecondPoint = QVector3D(fromNodeInfo.position.x(), fromNodeInfo.position.y(), 0.0) + fromBaseRadius;
                //到基点的方向
                QVector3D tobaseDirection = -fromBaseDirection;
                //到基点的半径
                QVector3D toBaseRadius = tobaseDirection * toNodeInfo.radius;
                QVector3D toBaseFirstPoint = QVector3D(toNodeInfo.position.x(), toNodeInfo.position.y(), 0.0) - toBaseRadius;
                QVector3D toBaseSecondPoint = QVector3D(toNodeInfo.position.x(), toNodeInfo.position.y(), 0.0) + toBaseRadius;
                QPolygon polygon;//定义一个多边形
                polygon.append(QPoint(fromBaseFirstPoint.x() * m_height, fromBaseFirstPoint.y() * m_height));
                polygon.append(QPoint(fromBaseSecondPoint.x() * m_height, fromBaseSecondPoint.y() * m_height));
                polygon.append(QPoint(toBaseFirstPoint.x() * m_height, toBaseFirstPoint.y() * m_height));
                polygon.append(QPoint(toBaseSecondPoint.x() * m_height, toBaseSecondPoint.y() * m_height));
                //生成轮廓
                QPainterPath path;
                path.addPolygon(polygon);
                painter.fillPath(path, brush);
            }
        }
    }

    painter.end();//结束绘制
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值