#include #include "ZxSerializer.h" #include "IxPtyPano.h" #include "ZxSubAxisX.h" #include "ZxSubAxisY.h" #include "nmObjPolygonTool.h" #include "nmObjPolygon.h" #include "nmDataAnalyzeManager.h" #include ZX_DEFINE_DYNAMIC(nObjPolygon, nmObjPolygon) nmObjPolygon::nmObjPolygon() { m_sObjTag = "nObjPolygon"; nmObjPolygon::init("", nullptr, nullptr); } nmObjPolygon::nmObjPolygon(const QString& sName, \ ZxSubAxisX* pAxisX, \ ZxSubAxisY* pAxisY) { m_sObjTag = "nObjPolygon"; nmObjPolygon::init(sName, pAxisX, pAxisY); } nmObjPolygon::~nmObjPolygon() { } void nmObjPolygon::init(const QString& sName, \ ZxSubAxisX* pAxisX, \ ZxSubAxisY* pAxisY) { nmObjBase::init(sName, pAxisX, pAxisY); nmObjPolygon::initFlags(); m_oPen = QPen(QBrush(QColor(128, 128, 128)), \ 0.3, Qt::SolidLine); m_clrBackgrd = QColor(255, 170, 255, 100); loadTempl(); } void nmObjPolygon::initTools() { m_pTool = new nmObjPolygonTool(); nmObjBase::initTools(); } void nmObjPolygon::initFlags() { setLockPos(false); setLockSize(false); setReadOnly(false); } bool nmObjPolygon::hitTest(const QPointF& pt) { return nmObjBase::hitTest(pt); } bool nmObjPolygon::_runHitTest(const QPointF& pt, int& nOption, int& nSubIndex) { if (!nmObjBase::_runHitTest(pt, nOption, nSubIndex)) { return false; } nOption = -1; nSubIndex = 0; if (nullptr == m_pAxisX || nullptr == m_pAxisY) { return false; } QVector pts = getPosOf(m_vecPoints); // 点 double r = 1.0; for (int i = 0; i < pts.count(); i++) { QPointF ptTopLeft = QPointF(pts[i].x() - r, pts[i].y() - r); QSizeF sz = QSizeF(r * 2.0, r * 2.0); QRectF rt = QRectF(ptTopLeft, sz); bool b = rt.contains(pt); if (b) { nOption = (int)OHO_Point; nSubIndex = i; return true; } } // 线 for (int i = 0; i < pts.count()/* - 1*/; i++) { int n1 = i; int n2 = (i == pts.count() - 1 ? 0 : i + 1); if (_isNearLine(pt, pts[n1], pts[n2], 1.0 * 2)) { nOption = (int)OHO_Bound; nSubIndex = i; return true; } } // 内部 QPainterPath oPath; oPath.moveTo(pts[0]); for (int i = 1; i < pts.count(); i++) { oPath.lineTo(pts[i]); } oPath.closeSubpath(); if (oPath.contains(pt) &&isSelected()) { nOption = (int)OHO_Inner; nSubIndex = 0; return true; } return false; } bool nmObjPolygon::runMove(const QPointF& pt1, const QPointF& pt2) { if (isLockPos()) { return false; } if (nullptr == m_pAxisX || nullptr == m_pAxisY) { return false; } //emit sigRunMoveTo(pt1, pt2); int nCount = m_vecPoints.count(); QVector vecPts = getPosOf(m_vecPoints); //屏幕值 if (m_oHitOption == OHO_Point && m_nHitIndex >= 0) { // 如果是点,不用计算偏移量,直接将该点移动到鼠标抬起的位置即可 vecPts[m_nHitIndex] = pt2; nmDataAnalyzeManager::getCurrentInstance()->notifyDataChanged(); return moveToPos(vecPts); } else if (m_oHitOption == OHO_Bound && m_nHitIndex >= 0) { int nCount = vecPts.count(); // 如果是最后一个点,则下一点是索引0;否则是索引+1 int nNext = (m_nHitIndex == nCount - 1) ? 0 : (m_nHitIndex + 1); vecPts[m_nHitIndex] = offsetPoint(vecPts[m_nHitIndex], pt1, pt2); vecPts[nNext] = offsetPoint(vecPts[nNext], pt1, pt2); nmDataAnalyzeManager::getCurrentInstance()->notifyDataChanged(); return moveToPos(vecPts); } else if (m_oHitOption == OHO_Inner) { for (int i = 0; i < nCount; i++) { vecPts[i] = offsetPoint(vecPts[i], pt1, pt2); } m_dOffsetX = pt2.x() - pt1.x(); m_dOffsetY = pt2.y() - pt1.y(); nmDataAnalyzeManager::getCurrentInstance()->notifyDataChanged(); return moveToPos(vecPts); } else { return false; } return true; } void nmObjPolygon::fillPtyPano(IxPtyPano* sheet) { nmObjBase::fillPtyPano(sheet); ZX_PROP("ObjPolygon.Pen", getPen, setPen); ZX_PROP("ObjPolygon.BackgrdColor", getBackgrdColor, setBackgrdColor); ZX_PROP("ObjPolygon.BackgrdColorAlpha", getBackgrdAlpha, setBackgrdAlpha); } void nmObjPolygon::onSerialize(ZxSerializer* ser) { nmObjBase::onSerialize(ser); ser->write("BackgrdColor", m_clrBackgrd); // for () // { // ser->write("BackgrdColor", m_clrBackgrd); // } } void nmObjPolygon::onDeserialize(ZxSerializer* ser) { nmObjBase::onDeserialize(ser); ser->read("BackgrdColor", m_clrBackgrd); } void nmObjPolygon::onSaveTempl(ZxSerializer* ser) { nmObjBase::onSaveTempl(ser); ser->write("BackgrdColor", m_clrBackgrd); } void nmObjPolygon::onLoadTempl(ZxSerializer* ser) { nmObjBase::onLoadTempl(ser); ser->read("BackgrdColor", m_clrBackgrd); } QColor nmObjPolygon::getBackgrdColor() const { return m_clrBackgrd; } void nmObjPolygon::setBackgrdColor(QColor clr) { m_clrBackgrd.setRed(clr.red()); m_clrBackgrd.setGreen(clr.green()); m_clrBackgrd.setBlue(clr.blue()); update(); } int nmObjPolygon::getBackgrdAlpha() const { return m_clrBackgrd.alpha(); } void nmObjPolygon::setBackgrdAlpha(int nAlpha) { m_clrBackgrd.setAlpha(nAlpha); update(); } void nmObjPolygon::paintBack(QPainter* painter, const ZxPaintParam& param) { if (nullptr == m_pAxisX || nullptr == m_pAxisY || \ m_pAxisX->getRangeMin() == m_pAxisX->getRangeMax() || \ m_pAxisY->getRangeMin() == m_pAxisY->getRangeMax()) { return; } painter->save(); QVector pts = getPosOf(m_vecPoints); QRectF bound = getBounds(); if (!pts.empty()) { // 填充 QPainterPath oPath; oPath.moveTo(pts[0]); for (int i = 1; i < pts.count(); i++) { oPath.lineTo(pts[i]); } oPath.closeSubpath(); // m_clrBackgrd.setAlpha(100); painter->fillPath(oPath, QBrush(m_clrBackgrd)); // 边界 QPen pen = m_oPen; painter->setPen(pen); painter->drawPolygon(&pts[0], pts.count()); // 绘制名称文字 QFont ft = qApp->font(); ft.setPointSize(8); painter->setFont(ft); painter->setPen(Qt::black); ZxDrawHelper::drawText(painter, bound, Qt::AlignCenter|Qt::AlignVCenter, m_sName); } if (isSelected()) //选中状态下 { QPen pen(QBrush(Qt::black), 0.0f, Qt::DotLine); painter->setPen(pen); painter->drawPolygon(&pts[0], pts.count()); for (int i = 0; i < pts.count(); i++) { double w = 2.2; QPointF pt = pts[i]; QRectF rect(pt.x() - w * 0.5, pt.y() - w * 0.5, w, w); if (m_nHitIndex == i && m_oHitOption == OHO_Point) { QBrush br(Qt::red); painter->fillRect(rect, br); } // else if (m_nHitIndex == i && m_oHitOption == OHO_Bound) // { // //QBrush br(Qt::red); // painter->setPen(QColor(0, 0, 255)); // painter->drawLine(pts[i], pts[i + 1]); // } else { painter->setPen(QColor(0, 0, 128)); painter->drawRect(rect); } } } reorderPointsToAntiClockwise(); painter->restore(); } //鞋带公式计算多边形有向面积。 static double calcSignedArea(const QVector& pts) { double area = 0; int n = pts.size(); for (int i = 0; i < n; ++i) { const QPointF &p1 = pts[i]; const QPointF &p2 = pts[(i + 1) % n]; // 保证闭合 area += (p1.x() * p2.y() - p2.x() * p1.y()); } return area / 2.0; } // 重新排序点,保证 m_vecPoints 中的点按照顺方向排列 void nmObjPolygon::reorderPointsToAntiClockwise() { // 至少需要三个点才能形成封闭区域 if (m_vecPoints.size() < 3) return; // 使用 getPosOf 获取实际点的位置 double area = calcSignedArea(getPosOf(m_vecPoints)); // 如果面积为负,则说明点为逆时针排列,此时翻转数组 if (area < 0) { std::reverse(m_vecPoints.begin(), m_vecPoints.end()); } }