#include "MapPresent.h" #include "UnitDelegate.h" #include #include #include #include #include #include #include #include MapPresent::MapPresent(QWidget* parent /*= nullptr*/) :QWidget(parent) { _center_index.row = 0; _center_index.col = 0; auto delegate = new BasicUnitDelegate(this); _type_present_delegate[delegate->unitType()] = delegate; connect(this, &MapPresent::mouseOutNotify, delegate, &BasicUnitDelegate::hotClear); connect(this, &MapPresent::mouseHoverNotify, delegate, &BasicUnitDelegate::hotIndexSet); connect(delegate, &BasicUnitDelegate::updateRequest, [=](const QList& list) { for (auto idx : list) if (idx.isValid() && !_updated_index_list.contains(idx)) { _updated_index_list.append(idx); } this->update(); }); this->setMouseTracking(true); } void MapPresent::zoomTo(double percent) { this->_scale_times = std::max(1.0 / 5, percent); this->visible_units_tidy(); this->update(); } double MapPresent::zoomTimes() const { return _scale_times; } bool MapPresent::setDelegate(UnitPresentDelegate* ins) { if (_type_present_delegate.contains(ins->unitType())) return false; _type_present_delegate[ins->unitType()] = ins; this->visible_units_tidy(); this->update(); return true; } void MapPresent::setDataModel(MapDataModel* model) { this->_map_data_model = model; this->visible_units_tidy(); this->update(); } MapDataModel* MapPresent::dataModel() const { return _map_data_model; } PresentIndex MapPresent::indexGet(const QPointF& pos) const { QList templist; for (auto& unit : _visible_units) { if (unit.outline.contains(pos)) { templist << unit; } } while (templist.size() > 1) { auto opta = templist[0]; auto optb = templist[1]; auto dista = QVector3D(opta.outline.center()).distanceToPoint(QVector3D(pos)); auto distb = QVector3D(optb.outline.center()).distanceToPoint(QVector3D(pos)); if (dista < distb) { templist.removeAt(1); } else { templist.removeAt(0); } } if (templist.size()) return templist[0].index; return PresentIndex(); } QRectF MapPresent::outlineGet(const PresentIndex& idx) const { auto widget_rect = this->rect(); auto widget_center = widget_rect.center(); const float ap_len = _primitive_region_len * _scale_times / 2; auto a2_vec = ap_len * sqrt(3) / 2; auto n_yinc = QVector3D(a2_vec, ap_len * 3 / 2, 0); auto n_xinc = QVector3D(a2_vec, -ap_len * 3 / 2, 0); auto xinc = idx.row - _center_index.row; auto yinc = idx.col - _center_index.col; auto target_center = QVector3D(widget_center) + n_yinc * yinc + n_xinc * xinc; target_center -= QVector3D(ap_len, ap_len, 0); return QRectF(target_center.toPointF(), QSizeF(ap_len * 2, ap_len * 2)); } void MapPresent::paintEvent(QPaintEvent* ev) { QWidget::paintEvent(ev); if (_updated_index_list.size()) { QPainter pic_painter(&_paint_buffer); const int update_batch_count = 1200; for (auto unit_idx : _updated_index_list.mid(0, update_batch_count)) { pic_painter.save(); // 原点偏移,绘制缩放 auto opt = _visible_units[unit_idx]; pic_painter.translate(opt.outline.topLeft()); pic_painter.scale(_scale_times, _scale_times); // PresentOption重组,ClipPath设置 auto rect = QRectF(0, 0, _primitive_region_len, _primitive_region_len); opt.outline = rect; auto clip_path = BasicUnitDelegate::clipPathGet(rect); pic_painter.setClipPath(clip_path); int unit_type = 0; if (_map_data_model) { unit_type = _map_data_model->mapData(MapDataModel::DataType::UnitType, unit_idx).toInt(); } if (!_type_present_delegate.contains(unit_type)) { unit_type = 0; } UnitPresentDelegate& t = *_type_present_delegate[unit_type]; t.paint(&pic_painter, opt); pic_painter.restore(); } _updated_index_list = _updated_index_list.mid(update_batch_count); if (_updated_index_list.size()) { this->update(); } } QPainter widget_painter(this); // 转发累积 widget_painter.drawPixmap(rect(), _paint_buffer); } void MapPresent::resizeEvent(QResizeEvent* event) { QWidget::resizeEvent(event); visible_units_tidy(); } void MapPresent::mouseMoveEvent(QMouseEvent* event) { QWidget::mouseMoveEvent(event); event->accept(); auto index = indexGet(event->pos()); emit mouseHoverNotify(index); } void MapPresent::enterEvent(QEvent* event) { QWidget::enterEvent(event); event->accept(); emit mouseInNotify(); } void MapPresent::leaveEvent(QEvent* event) { QWidget::leaveEvent(event); event->accept(); emit mouseOutNotify(); } void MapPresent::mousePressEvent(QMouseEvent* ev) { QWidget::mousePressEvent(ev); emit this->mousePressNotify(ev); } void MapPresent::mouseReleaseEvent(QMouseEvent* ev) { QWidget::mouseReleaseEvent(ev); emit this->mouseReleaseNotify(ev); } PresentOption AssumeOpt(PresentIndex index, QRectF rf, MapDataModel *m) { PresentOption opt; opt.dataModel = m; opt.index = index; opt.outline = rf; return opt; } void MapPresent::visible_units_tidy() { _visible_units.clear(); QRectF curr_outline = this->rect(); _paint_buffer = QPixmap(curr_outline.toRect().size()); _visible_units[_center_index] = AssumeOpt(_center_index, outlineGet(_center_index), _map_data_model); int visible_count = 1, deduce_x = 1; while (visible_count) { visible_count = 0; auto siblings = siblingsGet(_center_index, deduce_x++); for (auto unit : siblings) { auto visible_rect = outlineGet(unit); if (curr_outline.intersects(visible_rect)) { _visible_units[unit] = AssumeOpt(unit, visible_rect, _map_data_model); visible_count++; } } } for (auto &optx : _visible_units) _updated_index_list.append(optx.index); } QList MapPresent::siblingsGet(const PresentIndex& center, uint16_t dist) const { QList e6_points; e6_points << PresentIndex{ center.row, center.col - dist }; e6_points << PresentIndex{ center.row + dist, center.col }; e6_points << PresentIndex{ center.row + dist, center.col + dist }; e6_points << PresentIndex{ center.row, center.col + dist }; e6_points << PresentIndex{ center.row - dist, center.col }; e6_points << PresentIndex{ center.row - dist, center.col - dist }; e6_points << PresentIndex{ center.row, center.col - dist }; QList values; for (auto idx = 0; idx < 6; ++idx) { auto ap = e6_points[idx]; auto bp = e6_points[idx + 1]; values << item_supply(ap, bp); } return values; } QList MapPresent::item_supply(const PresentIndex& a, const PresentIndex& b) const { QList items; auto sign_row = (a.row == b.row) ? 0 : ((b.row - a.row) / std::abs(a.row - b.row)); auto sign_col = (a.col == b.col) ? 0 : ((b.col - a.col) / std::abs(a.col - b.col)); auto temp_pt = a; while (temp_pt != b) { items << temp_pt; temp_pt += PresentIndex{ sign_row, sign_col }; } return items; } PresentIndex& PresentIndex::operator+=(const PresentIndex& other) { row += other.row; col += other.col; return *this; } bool PresentIndex::operator!=(const PresentIndex& other) const { return row != other.row || col != other.col; } bool PresentIndex::isValid() const { return row != INT_MAX && col != INT_MAX; } bool PresentIndex::operator<(const PresentIndex& other) const { if (row == other.row) return col < other.col; return row < other.row; } bool PresentIndex::operator==(const PresentIndex& other) const { return row == other.row && col == other.col; } UnitPresentDelegate::UnitPresentDelegate(QObject* parent /*= nullptr*/) :QObject(parent) { } PresentOption& PresentOption::operator=(const PresentOption& other) { index = other.index; outline = other.outline; dataModel = other.dataModel; return *this; }