You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
nmWTAI-Platform/Src/nmNum/nmPlot/nmObjRegion.cpp

509 lines
13 KiB
C++

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include <QPainter>
#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 <algorithm>
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<QPointF> 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<QPointF> 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<QVariant> 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<QPointF> 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<QPointF>& 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);
}
}