#include #include "ZxSerializer.h" #include "IxPtyPano.h" #include "ZxSubAxisX.h" #include "ZxSubAxisY.h" #include "nmObjRegion.h" #include "nmObjRegionTool.h" #include "ZxBaseUtil.h" #include "ZxPlot.h" #include "nmDataAnalyzeManager.h" #include ZX_DEFINE_DYNAMIC(nObjRegion, nmObjRegion) nmObjRegion::nmObjRegion() { m_sObjTag = "nObjRegion"; nmObjRegion::init("", NULL, NULL); } nmObjRegion::nmObjRegion(const QString& sName, \ ZxSubAxisX* pAxisX, \ ZxSubAxisY* pAxisY) { m_sObjTag = "nObjRegion"; nmObjRegion::init(sName, pAxisX, pAxisY); } nmObjRegion::~nmObjRegion() { // 复合区数据由DataManager统一释放,图元只保存引用。 m_regionData = nullptr; if(m_regionMark != nullptr) { m_regionMark = nullptr; } } void nmObjRegion::init(const QString& sName, \ ZxSubAxisX* pAxisX, \ ZxSubAxisY* pAxisY) { nmObjBase::init(sName, pAxisX, pAxisY); nmObjRegion::initFlags(); m_oPen = QPen(QBrush(QColor(128, 128, 128)), \ 0.3f, Qt::SolidLine); m_clrBackgrd = QColor(0, 0, 0, 0); m_regionMark = nullptr; m_regionData = nullptr; loadTempl(); // 连接图元可见性信号和槽 connect(this, SIGNAL(sigObjVisibleChanged(bool)), this, SLOT(onObjVisibleChanged(bool))); } void nmObjRegion::initTools() { m_pTool = new nmObjRegionTool(); nmObjBase::initTools(); } void nmObjRegion::initFlags() { setLockPos(false); setLockSize(false); setReadOnly(false); } bool nmObjRegion::hitTest(const QPointF& pt) { return nmObjBase::hitTest(pt); } bool nmObjRegion::_runHitTest(const QPointF& pt, int& nOption, int& nSubIndex) { if(!nmObjBase::_runHitTest(pt, nOption, nSubIndex)) { return false; } nOption = -1; nSubIndex = 0; if(NULL == m_pAxisX || NULL == m_pAxisY) { return false; } QVector pts = getPosOf(m_vecPoints); // 点 double r = 1.f; for(int i = 0; i < pts.count(); i++) { QPointF ptTopLeft = QPointF(pts[i].x() - r, pts[i].y() - r); QSizeF sz = QSizeF(r * 2.f, r * 2.f); 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(); i++) { int n1 = i; int n2 = (i == pts.count() - 1 ? 0 : i + 1); if(_isNearLine(pt, pts[n1], pts[n2], 1.f * 2)) { // 判断当前线是否已经存在 if(m_oHitOption == OHO_Bound && m_pTool->isLeftDown()) { // 添加新节点 pts.insert(i + 1, pt); m_vecPoints.clear(); m_vecPoints = getValueOf(pts); nOption = (int)OHO_Point; nSubIndex = i + 1; return true; } nOption = (int)OHO_Bound; nSubIndex = i; return true; } } nOption = (int)OHO_None; nSubIndex = -1; return false; } bool nmObjRegion::runMove(const QPointF& pt1, const QPointF& pt2) { if(isLockPos()) { return false; } if(NULL == m_pAxisX || NULL == m_pAxisY) { return false; } int nCount = m_vecPoints.count(); QVector vecPts = getPosOf(m_vecPoints); // 屏幕值 if(m_oHitOption == OHO_Point && m_nHitIndex >= 0) { // 如果是点,不用计算偏移量,直接将该点移动到鼠标抬起的位置即可 vecPts[m_nHitIndex] = pt2; return moveToPos(vecPts); } else if(m_oHitOption == OHO_Bound && m_nHitIndex >= 0) { 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(); return moveToPos(vecPts); } else { return false; } return true; } void nmObjRegion::fillPtyPano(IxPtyPano* sheet) { nmObjBase::fillPtyPano(sheet); ZX_PROP("ObjRect.Pen", getPen, setPen); ZX_PROP("ObjRect.BackgrdColor", getBackgrdColor, setBackgrdColor); IxPtyItem* pProp = ZX_PROP("ObjRegion.FlowModel", getRegionFlowModel, setRegionFlowModel); if(NULL != pProp) { QStringList listTags; listTags << tr("Leaky") << tr("Composite Limit"); QList listIndexes; listIndexes << 0 << 1 << 2; pProp->getPtyPano(); pProp->setOptions(listTags, listIndexes); } ZX_PROP("ObjRegion.Leakage", getRegionLeakage, setRegionLeakage); } void nmObjRegion::onSerialize(ZxSerializer* ser) { nmObjBase::onSerialize(ser); int flowModel; if(m_regionData->getRegionFlowModel().getValue() == tr("Leaky")) flowModel = 0; else flowModel = 1; ser->write("FlowModel", flowModel); ser->write("Leakage", m_regionData->getRegionFlowModel().getValue().toDouble()); ser->write("BackgrdColor", m_clrBackgrd); } void nmObjRegion::onDeserialize(ZxSerializer* ser) { nmObjBase::onDeserialize(ser); int flowModel; double regionLeakage; ser->read("FlowModel", flowModel); ser->read("Leakage", regionLeakage); ser->read("BackgrdColor", m_clrBackgrd); this->setRegionLeakage(regionLeakage); } void nmObjRegion::onSaveTempl(ZxSerializer* ser) { nmObjBase::onSaveTempl(ser); int flowModel; if(m_regionData->getRegionFlowModel().getValue() == tr("Leaky")) flowModel = 0; else flowModel = 1; ser->write("FlowModel", flowModel); ser->write("Leakage", m_regionData->getRegionFlowModel().getValue().toDouble()); ser->write("BackgrdColor", m_clrBackgrd); } void nmObjRegion::onLoadTempl(ZxSerializer* ser) { nmObjBase::onLoadTempl(ser); int flowModel; double regionLeakage; ser->read("FlowModel", flowModel); ser->read("Leakage", regionLeakage); ser->read("BackgrdColor", m_clrBackgrd); this->setRegionLeakage(regionLeakage); } QColor nmObjRegion::getBackgrdColor() const { //if (m_nLineIndex) //return m_vecPresType[m_nLineIndex] return m_clrBackgrd; } void nmObjRegion::setBackgrdColor(QColor color) { if(m_clrBackgrd != color) { //m_vecPresType[m_nLineIndex] = m_clrBackgrd = color; update(); } } void nmObjRegion::paintBack(QPainter* painter, const ZxPaintParam& param) { if(NULL == m_pAxisX || NULL == m_pAxisY || \ m_pAxisX->getRangeMin() == m_pAxisX->getRangeMax() || \ m_pAxisY->getRangeMin() == m_pAxisY->getRangeMax()) { return; } painter->save(); QVector pts = getPosOf(m_vecPoints); if(m_regionData != nullptr) { m_regionData->setRegionName(m_sName); m_regionData->setVecPts(m_vecPoints); if(m_regionMark == nullptr) { if(m_regionData->getRegionMarkData() != nullptr) m_regionData->setRegionMarkData(nullptr); } else if(m_regionMark->getRegionMarkData() == nullptr) { m_regionMark = nullptr; m_regionData->setRegionMarkData(nullptr); this->setBackgrdColor(QColor(0, 0, 0, 0)); } else { m_regionData->setRegionMarkData(m_regionMark->getRegionMarkData()); } } // 不选中的状态 if(!pts.empty()) { // 填充 QPainterPath oPath; oPath.moveTo(pts[0]); for(int i = 1; i < pts.count(); i++) { oPath.lineTo(pts[i]); } oPath.closeSubpath(); painter->fillPath(oPath, QBrush(m_clrBackgrd)); // 判断标记区域对象的点是否存在 if(m_regionMark != nullptr) { QPointF ptPos = getValueOf(m_regionMark->getAllPos()[0]); QPolygonF regionPolygon(getValueOf(this->getAllPos())); if(!regionPolygon.containsPoint(ptPos, Qt::OddEvenFill)) { this->setBackgrdColor(QColor(0, 0, 0, 0)); m_regionMark->setSelectRegion(false); m_regionMark->getRegionMarkData()->setSelectRegion(false); m_regionMark = nullptr; } } // 区域填充 QPen pen = m_oPen; painter->setPen(pen); painter->drawPolygon(&pts[0], pts.count()); QRectF bound = getBounds(); // 绘制名称文字 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()) { //选中状态下 for(int i = 0; i < pts.count(); i++) { float w = 2.2f; QPointF pt = pts[i]; QRectF rect(pt.x() - w * 0.5f, pt.y() - w * 0.5f, w, w); if(m_nHitIndex == i && m_oHitOption == OHO_Point) { // 填充节点 QBrush br(Qt::red); painter->fillRect(rect, br); } else { painter->setPen(QColor(0, 0, 128)); painter->drawRect(rect); } // 击中线 if(m_nHitIndex == i && m_oHitOption == OHO_Bound) { nmDataLogFile::getInstance()->writeLog("select line " + QString::number(i)); QPen pen(Qt::DashLine); painter->setPen(pen); painter->drawPolygon(&pts[0], pts.count()); /* for(int i = 0; i < pts.count() - 1; i++) { painter->drawLine(pts[i], pts[i + 1]); } */ } } } painter->restore(); } int nmObjRegion::getRegionFlowModel() { if(m_regionData->getRegionFlowModel().getValue() == tr("Leaky")) return 0; else return 1; } void nmObjRegion::setRegionFlowModel(int newRegionFlowModel) { // 从Data中取数据 nmDataAttribute tempAttr = m_regionData->getRegionFlowModel(); if(newRegionFlowModel == 0) { tempAttr.setValue(tr("Leaky")); } else { tempAttr.setValue(tr("Composite limit")); } m_regionData->setRegionFlowModel(tempAttr); } double nmObjRegion::getRegionLeakage() { return m_regionData->getRegionLeakage().getValue().toDouble(); } void nmObjRegion::setRegionLeakage(double newRegionLeakage) { // 从Data中取数据 nmDataAttribute tempAttr = m_regionData->getRegionLeakage(); tempAttr.setValue(newRegionLeakage); m_regionData->setRegionLeakage(tempAttr); } void nmObjRegion::setRegionMark(nmObjRegionMark* newRegionMark) { m_regionMark = newRegionMark; } nmObjRegionMark* nmObjRegion::getRegionMark() const { return m_regionMark; } void nmObjRegion::setRegionData(nmDataRegion* newRegionData) { m_regionData = newRegionData; } nmDataRegion* nmObjRegion::getRegionData() const { return m_regionData; } //鞋带公式计算多边形有向面积。 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 nmObjRegion::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()); } } void nmObjRegion::afterCreated() { m_regionData = nmDataAnalyzeManager::getCurrentInstance()->createRegion(); // 初始化复合区基础数据 m_regionData->setRegionName(m_sName); // 在区域创建完成后,调用重新排序函数确保点的顺序为顺时针 reorderPointsToAntiClockwise(); m_regionData->setVecPts(m_vecPoints); // 判断复合区是否被区域标记 if(m_regionMark == nullptr) { if(m_regionData->getRegionMarkData() != nullptr) m_regionData->setRegionMarkData(nullptr); return; } // 添加区域标记对象 m_regionData->setRegionMarkData(m_regionMark->getRegionMarkData()); } void nmObjRegion::removeData() { if(m_regionData) { // 从数据中心移除该数据 nmDataAnalyzeManager::getCurrentInstance()->removeRegionData(m_regionData); m_regionData = nullptr; } } void nmObjRegion::onObjVisibleChanged(bool bIsVisible) { if(m_regionData != nullptr) { m_regionData->setPlotVisible(bIsVisible); } }