#include "blockviewwavetrace.h" BlockViewWavetrace::BlockViewWavetrace(int width, int height) { max_steps = 512; resize(width, height); setPreferredDirection(Horizontal); } void BlockViewWavetrace::resize(int width, int height) { wid = width; hei = height; if (field.size() != wid) field.resize(wid); for (int i = 0; i < wid; ++i) { if (field[i].size() != hei) { field[i].resize(hei); field[i].fill(Cell()); } } } void BlockViewWavetrace::fill(short val) { for (int i = 0; i < wid; ++i) { if (i == 0) field[i].fill(val); else memcpy(field[i].data(), field[0].constData(), hei * sizeof(field[0][0])); } } void BlockViewWavetrace::fill(const QRect & rect, short val) { for (int i = rect.left(); i <= rect.right(); ++i) for (int j = rect.top(); j <= rect.bottom(); ++j) field[i][j].value = field[i][j].direction = val; } void BlockViewWavetrace::fill(int px, int py, short val) { short p = field[px][py].value; if ((val == HorizontalBus && p == VerticalBus) || (val == VerticalBus && p == HorizontalBus)) field[px][py].value = Blocked; else field[px][py].value = val; field[px][py].direction = field[px][py].value; } bool BlockViewWavetrace::trace(const QPoint & start, const QPoint & finish) { st = start; fn = finish; if (dir_ == NoTrace) return true; // qDebug() << "trace" << start << finish; short cl = 0; QRect frect(0, 0, wid - 1, hei - 1); QVector cpnts, npnts; fill(st, cl); cpnts.push_back(st); if (field[fn.x()][fn.y()].value == (short)Blocked) return false; auto checkAndFill = [this, &npnts, &frect](int x, int y, short acc_dir, short c) { if (!frect.contains(x, y)) return; short p = field[x][y].value; if (p == (short)Empty || p == acc_dir) { npnts.push_back(QPoint(x, y)); field[x][y].value = c; } }; while (cpnts.size() > 0) { npnts.clear(); cl++; if (cl >= max_steps) return false; for (int i = 0; i < cpnts.size(); ++i) { if (cpnts[i] == fn) return true; checkAndFill(cpnts[i].x() - 1, cpnts[i].y(), (short)VerticalBus, cl); checkAndFill(cpnts[i].x() + 1, cpnts[i].y(), (short)VerticalBus, cl); checkAndFill(cpnts[i].x(), cpnts[i].y() - 1, (short)HorizontalBus, cl); checkAndFill(cpnts[i].x(), cpnts[i].y() + 1, (short)HorizontalBus, cl); } cpnts = npnts; // qDebug() << cl << ": " << cpnts.size(); } return false; } void BlockViewWavetrace::gatherPath() { path_.clear(); path_.push_back(fn); if (dir_ == NoTrace) { path_.push_front(st); return; } int pa = -1, ca = -1; bool first = true; short cl = field[fn.x()][fn.y()].value; QRect frect(0, 0, wid, hei); QPoint cpnt = fn; auto checkAndStep = [this, &cpnt, &first, &frect](int dir, short c, int & ca_, int pa_) -> bool { int cx = cpnt.x() + dps[dir].x(); int cy = cpnt.y() + dps[dir].y(); if (frect.contains(cx, cy)) { const Cell & cell(field[cx][cy]); if (cell.value == c) { if (cell.direction == HorizontalBus || cell.direction == VerticalBus) { if (dps[dir].x() == 0 && cell.direction == VerticalBus) return false; if (dps[dir].y() == 0 && cell.direction == HorizontalBus) return false; } ca_ = QLineF(QPointF(cx, cy), cpnt).angle(); if (ca_ != pa_ && !first) path_.push_front(cpnt); cpnt = QPoint(cx, cy); first = false; return true; } } return false; }; while (cl > 0) { cl--; pa = ca; if (checkAndStep(0, cl, ca, pa)) continue; if (checkAndStep(1, cl, ca, pa)) continue; if (checkAndStep(2, cl, ca, pa)) continue; if (checkAndStep(3, cl, ca, pa)) continue; } path_.push_front(st); } void BlockViewWavetrace::setPreferredDirection(BlockViewWavetrace::Direction dir) { dir_ = dir; if (dir == BlockViewWavetrace::Horizontal) { dps[0] = QPoint(0, -1); dps[1] = QPoint(0, 1); dps[2] = QPoint(-1, 0); dps[3] = QPoint(1, 0); } if (dir == BlockViewWavetrace::Vertical) { dps[2] = QPoint(0, -1); dps[3] = QPoint(0, 1); dps[0] = QPoint(-1, 0); dps[1] = QPoint(1, 0); } } const QVector & BlockViewWavetrace::path() const { return path_; }