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++

#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());
}
}