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/nmObjPolygon.cpp

352 lines
8.5 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 "nmObjPolygonTool.h"
#include "nmObjPolygon.h"
#include "nmDataAnalyzeManager.h"
#include <algorithm>
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<QPointF> 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<QPointF> 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<QPointF> 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<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 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());
}
}