310 lines
7.2 KiB
C++
310 lines
7.2 KiB
C++
#include "MapPresent.h"
|
||
#include "UnitDelegate.h"
|
||
#include <QVector2D>
|
||
#include <cmath>
|
||
#include <QVector3D>
|
||
#include <QEvent>
|
||
#include <QPainter>
|
||
#include <QMouseEvent>
|
||
#include <QElapsedTimer>
|
||
#include <QtDebug>
|
||
|
||
|
||
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<PresentIndex>& 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<PresentOption> 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);
|
||
|
||
for (auto unit_idx : _updated_index_list) {
|
||
pic_painter.save();
|
||
|
||
auto opt = _visible_units[unit_idx];
|
||
auto rect = opt.outline;
|
||
pic_painter.translate(rect.topLeft());
|
||
rect.moveTo(0, 0);
|
||
|
||
auto clip_path = BasicUnitDelegate::clipPathGet(rect);
|
||
pic_painter.setClipPath(clip_path);
|
||
|
||
opt.outline = rect;
|
||
|
||
int unit_type = 0;
|
||
if (_map_data_model) {
|
||
unit_type = _map_data_model->mapData(MapDataModel::DataType::UnitType, unit_idx).toInt();
|
||
}
|
||
|
||
UnitPresentDelegate& t = *_type_present_delegate[unit_type];
|
||
t.paint(&pic_painter, opt);
|
||
|
||
pic_painter.restore();
|
||
}
|
||
|
||
_updated_index_list.clear();
|
||
}
|
||
|
||
QPainter widget_painter(this);
|
||
// ת<><D7AA><EFBFBD>ۻ<EFBFBD>
|
||
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<PresentIndex> MapPresent::siblingsGet(const PresentIndex& center, uint16_t dist) const
|
||
{
|
||
QList<PresentIndex> 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<PresentIndex> 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<PresentIndex> MapPresent::item_supply(const PresentIndex& a, const PresentIndex& b) const
|
||
{
|
||
QList<PresentIndex> 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;
|
||
}
|