#include "dag_present.h" #include using namespace dags; ActivePresentNode::ActivePresentNode(const QString name, PrsnType type, QFont font) : __GraphNodeBase(GraphNodeType::ActivePresentNode), node_type(type), node_name(name), measure_base(font) {} QString ActivePresentNode::nodeName() const { return this->node_name; } QRectF ActivePresentNode::boundingRect() const { auto rect = this->measure_base.boundingRect(this->node_name); rect.moveTo(0, 0); return rect += QMarginsF(0, 0, 10, 10); } void ActivePresentNode::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) { auto outline = this->boundingRect(); painter->save(); if (this->node_type == PrsnType::NormalNode) painter->fillRect(outline, Qt::gray); else painter->fillRect(outline, Qt::green); if (this->isHighlighted()) { painter->setPen(Qt::red); } painter->drawRect(outline); painter->drawText(outline - QMarginsF(5, 5, 0, 0), this->node_name); painter->restore(); } PenetrateNode::PenetrateNode(double width, const QString& towards, const QString& to) : __GraphNodeBase(GraphNodeType::PenetrateNode), outline(QSizeF(20, 8)), from_name(towards), to_name(to) { } void PenetrateNode::resizeOutline(double w, double h) { this->outline = QSizeF(w, h); this->update(); } QString PenetrateNode::nodeFrom() const { return from_name; } QString PenetrateNode::nodeTo() const { return to_name; } QRectF PenetrateNode::boundingRect() const { return QRectF(QPointF(), this->outline); } void PenetrateNode::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) { auto outline = this->boundingRect(); painter->save(); painter->setRenderHint(QPainter::Antialiasing); auto ypos = outline.height() / 2 - 3; if (this->isHighlighted()) painter->fillRect(QRectF(0, ypos, outline.width(), 4), Qt::red); else painter->fillRect(QRectF(0, ypos, outline.width(), 4), Qt::black); painter->restore(); } TransitionCurve::TransitionCurve(IGraphNode* start, IGraphNode* end, double prev_layer_width) : __GraphNodeBase(GraphNodeType::TransitionCurve), start_node(start), end_node(end), prev_layer_w(prev_layer_width) {} void TransitionCurve::layoutRefresh() { auto orect = this->start_node->boundingRect(); auto erect = this->end_node->boundingRect(); auto xpos = this->start_node->pos().x() + orect.width(); auto ypos = std::min(this->start_node->pos().y(), this->end_node->pos().y()); auto width_value = this->end_node->pos().x() - xpos; auto bottom_y = std::max(this->start_node->pos().y() + orect.height(), this->end_node->pos().y() + erect.height()); this->outline = QRectF(0, 0, width_value, bottom_y - ypos); this->setPos(xpos, ypos); } QString TransitionCurve::nodeFrom() const { if (start_node->nodeType() == GraphNodeType::ActivePresentNode) return static_cast(start_node)->nodeName(); else return static_cast(start_node)->nodeFrom(); } QString TransitionCurve::nodeTo() const { if (end_node->nodeType() == GraphNodeType::ActivePresentNode) return static_cast(end_node)->nodeName(); else return static_cast(end_node)->nodeTo(); } QRectF TransitionCurve::boundingRect() const { return this->outline; } void TransitionCurve::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) { auto outline = this->boundingRect(); auto start_rect = this->start_node->boundingRect(); auto end_rect = this->end_node->boundingRect(); start_rect.moveTo(this->start_node->pos()); end_rect.moveTo(this->end_node->pos()); auto aj_start_pos = start_rect.topRight(); auto aj_end_pos = end_rect.topLeft(); auto line_span = this->prev_layer_w - start_rect.width(); painter->save(); painter->setRenderHint(QPainter::Antialiasing); auto start_pos = aj_start_pos - QGraphicsItem::pos() + QPointF(0, (int) start_rect.height() / 2); auto end_pos = aj_end_pos - QGraphicsItem::pos() + QPointF(0, (int) end_rect.height() / 2); auto line_epos = start_pos + QPointF(line_span, 0); auto control_pos0 = line_epos + QPointF((outline.width() - line_span) / 3, 0); auto control_pos1 = end_pos - QPointF((outline.width() - line_span) / 3, 0); auto npen = QPen(Qt::black); if (this->isHighlighted()) npen = QPen(Qt::red); npen.setWidthF(4); painter->setPen(npen); painter->drawLine(start_pos, line_epos); auto path0 = QPainterPath(); path0.moveTo(line_epos); path0.cubicTo(control_pos0, control_pos1, end_pos); painter->drawPath(path0); painter->restore(); } DAGActiveView::DAGActiveView(QWidget* parent) : QGraphicsView(parent), _layout_engine(new DAGGraph){ this->setViewportUpdateMode(QGraphicsView::FullViewportUpdate); this->setScene(&this->scene_bind); auto f = this->font(); f.setPixelSize(25); this->setFont(f); } QList DAGActiveView::layer_nodes_construction(const QHash>& prev_layer_helper, const QList>& total_datas, int layer_idx, double prev_layer_end) { // 挑选当前层次节点 QList> current_layer_datas; std::copy_if(total_datas.begin(), total_datas.end(), std::back_inserter(current_layer_datas), [&](std::shared_ptr ins) { return ins->layerNumber() == layer_idx; }); std::sort(current_layer_datas.begin(), current_layer_datas.end(), [](std::shared_ptra, std::shared_ptr b) { return a->sortNumber() < b->sortNumber(); }); QHash> current_nodes_helper; // 当前层次无节点 if (!current_layer_datas.size()) return current_nodes_helper.keys(); // 构建当前层次图形节点 for (auto& data_node : current_layer_datas) { if (data_node->isFakeNode()) { auto from = data_node->tailsNode()->bindPoint().name(); auto to = data_node->headsNode()->bindPoint().name(); auto curr_gnode = new PenetrateNode(20, from, to); this->scene_bind.addItem(curr_gnode); curr_gnode->setPos(prev_layer_end, data_node->sortNumber().toDouble() * (this->node_span + this->font().pointSizeF()) * 7); current_nodes_helper[curr_gnode] = data_node; } else { auto node_type_vx = PrsnType::NormalNode; if (!data_node->layerNode()->inputCount()) { node_type_vx = PrsnType::StartNode; } auto curr_gnode = new ActivePresentNode(data_node->layerNode()->bindPoint().name(), node_type_vx, this->font()); this->scene_bind.addItem(curr_gnode); curr_gnode->setPos(prev_layer_end, data_node->sortNumber().toDouble() * (this->node_span + this->font().pointSizeF()) * 7); current_nodes_helper[curr_gnode] = data_node; } } // 对其当前层次节点宽度 double curr_layer_width = 0, prev_node_height = 20; for (auto n : current_nodes_helper.keys()) { curr_layer_width = std::max(curr_layer_width, n->boundingRect().width()); } if (prev_layer_helper.size()) { prev_node_height = prev_layer_helper.keys().first()->boundingRect().height(); for (auto n : current_nodes_helper.keys()) { if (n->nodeType() == GraphNodeType::PenetrateNode) { static_cast(n)->resizeOutline(curr_layer_width, prev_node_height); } } } auto next_nodes = this->layer_nodes_construction(current_nodes_helper, total_datas, layer_idx + 1, prev_layer_end + curr_layer_width + this->layer_span); // 构建层次连线 if (prev_layer_helper.size()) { double prev_layer_width = 0; for (auto n : prev_layer_helper.keys()) { prev_layer_width = std::max(prev_layer_width, n->boundingRect().width()); } for (auto curr_gnode : current_nodes_helper.keys()) { auto curr_data = current_nodes_helper[curr_gnode]; auto upstream_nodes = curr_data->getUpstreamNodes(); for (auto prev_gnode : prev_layer_helper.keys()) { auto prev_data = prev_layer_helper[prev_gnode]; if (upstream_nodes.contains(prev_data)) { auto line_cmbn = new TransitionCurve(prev_gnode, curr_gnode, prev_layer_width); this->scene_bind.addItem(line_cmbn); line_cmbn->layoutRefresh(); next_nodes << line_cmbn; } } } } next_nodes.append(current_nodes_helper.keys()); return next_nodes; } #include void DAGActiveView::updateWithEdges(QList arrows) { _layout_engine->rebuildFromEdges(arrows); _layout_engine->primitiveGraphLayout(); this->refreshGraph(); } void DAGActiveView::highlightGraphLink(const QList arrows) { for (auto& n : this->highlight_nodelist) n->highlight(false); this->highlight_nodelist.clear(); QList _color_path; for (auto& a : arrows) { _color_path << a.endPoint().name(); _color_path << a.startPoint().name(); } auto set = _color_path.toSet(); //高亮关键路径点 for (auto& node : this->total_graph_nodes) { if (node->nodeType() == GraphNodeType::ActivePresentNode) { auto x_node = static_cast(node); if (set.contains(x_node->nodeName())) { x_node->highlight(true); this->highlight_nodelist << x_node; } } } for (auto idx = 0; idx < arrows.size(); ++idx) { auto start_name = arrows.at(idx).startPoint().name(); auto end_name = arrows.at(idx).endPoint().name(); for (auto gnode : this->total_graph_nodes) { switch (gnode->nodeType()) { // 高亮占位节点 case GraphNodeType::PenetrateNode: { auto pinst = dynamic_cast(gnode); if (start_name == pinst->nodeFrom() && end_name == pinst->nodeTo()) { gnode->highlight(true); this->highlight_nodelist << gnode; } } break; case GraphNodeType::TransitionCurve: { auto pinst = dynamic_cast(gnode); if (start_name == pinst->nodeFrom() && end_name == pinst->nodeTo()) { gnode->highlight(true); this->highlight_nodelist << gnode; } } break; default: break; } } } this->scene_bind.update(); this->update(); } DAGGraph* dags::DAGActiveView::layoutEngine() const { return this->_layout_engine; } void dags::DAGActiveView::refreshGraph() { // 清除当前视图 this->scene_bind.clear(); this->total_graph_nodes.clear(); this->highlight_nodelist.clear(); auto total_nodes = _layout_engine->nodeWithLayout(); total_graph_nodes = this->layer_nodes_construction(QHash>(), total_nodes); auto rect = this->scene()->itemsBoundingRect(); this->scene()->setSceneRect(rect); } void DAGActiveView::mousePressEvent(QMouseEvent* ev) { QGraphicsView::mousePressEvent(ev); if (ev->buttons().testFlag(Qt::LeftButton)) { auto gitems = this->items(ev->pos()); if (!gitems.size()) return; for (auto& gitem : gitems) { auto type_item = dynamic_cast(gitem); if (type_item->nodeType() == GraphNodeType::ActivePresentNode) { auto node = static_cast(type_item); emit this->nodeClicked(ev->pos(), node->nodeName()); break; } } } } __GraphNodeBase::__GraphNodeBase(GraphNodeType t) :_node_type(t), _highlight_mark(false) {} GraphNodeType __GraphNodeBase::nodeType() const { return _node_type; } void __GraphNodeBase::highlight(bool mark) { _highlight_mark = mark; this->update(); } bool __GraphNodeBase::isHighlighted() const { return _highlight_mark; } QPointF __GraphNodeBase::pos() const { return QGraphicsItem::pos(); } void __GraphNodeBase::setPos(qreal x, qreal y) { QGraphicsItem::setPos(x, y); }