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

221 lines
7.2 KiB
C++

#include <QPainter>
#include "ZxBaseUtil.h"
#include "nmObjDelete.h"
#include "nmObjDeleteTool.h"
#include "nmSingalCenter.h"
#include "ZxPlot.h"
#include "zxGmyBase.h"
#include "nmObjLineCrack.h"
#include "nmDataFracture.h"
#include "nmPlotGraphicBinder.h"
nmObjDeleteTool::nmObjDeleteTool() : nmObjToolBase()
, m_pPlot(nullptr)
{
setText(tr("nObjDeleteTool"));
m_oNot = NMOT_Delete;
initDeleteCursor();
}
nmObjDeleteTool::nmObjDeleteTool(ZxPlot* pPlot)
: nmObjToolBase()
, m_pPlot(pPlot)
{
setText(tr("nObjDeleteTool"));
m_oNot = NMOT_Delete;
nmDataLogFile::getInstance()->writeLog(" nmObjDeleteTool ++++++++ " + QString::number(m_oNot));
initDeleteCursor();
}
nmObjDeleteTool::~nmObjDeleteTool()
{
// 视口可能已销毁,由 QPointer 避免访问失效窗口。
resetDeleteCursor();
}
void nmObjDeleteTool::initDeleteCursor()
{
m_hadExplicitCursor = false;
QPixmap icon(ZxBaseUtil::getDirOf(s_Dir_Res, "Icon") + "nmTrash.png");
QPixmap cursorPixmap(40, 40);
cursorPixmap.fill(Qt::transparent);
QPainter painter(&cursorPixmap);
// 仅显示小垃圾桶,以中心作为点击热点,大小不受画布缩放影响。
painter.drawPixmap(cursorPixmap.rect(), icon, icon.rect());
painter.end();
m_deleteCursor = QCursor(cursorPixmap, 20, 20);
}
bool nmObjDeleteTool::updateDeleteCursor(const QPointF& pt)
{
// 与点击使用同一命中规则,仅在绘图视口内切换光标。
if (m_pObj != nullptr || QApplication::activeModalWidget() != nullptr ||
findNearestDeleteObj(pt) == nullptr) {
resetDeleteCursor();
return false;
}
QWidget* viewport = getView()->viewport();
if (m_cursorViewport != viewport) {
resetDeleteCursor();
m_cursorViewport = viewport;
m_hadExplicitCursor = viewport->testAttribute(Qt::WA_SetCursor);
m_previousCursor = viewport->cursor();
}
viewport->setCursor(m_deleteCursor);
return true;
}
void nmObjDeleteTool::resetDeleteCursor()
{
// 只恢复本工具设置的光标,不覆盖后来由其他工具设置的样式。
if (m_cursorViewport &&
m_cursorViewport->cursor().pixmap().cacheKey() == m_deleteCursor.pixmap().cacheKey()) {
if (m_hadExplicitCursor) {
m_cursorViewport->setCursor(m_previousCursor);
} else {
m_cursorViewport->unsetCursor();
}
}
m_cursorViewport = nullptr;
}
bool nmObjDeleteTool::onMouseMove(const QPointF& pt)
{
if (m_pObj != nullptr) {
return nmObjToolBase::onMouseMove(pt);
}
// 命中时阻止图元自身的移动光标覆盖删除提示。
return updateDeleteCursor(pt);
}
bool nmObjDeleteTool::onRightDown(const QPointF& pt)
{
// 右键退出或打开菜单前恢复普通光标。
resetDeleteCursor();
return nmObjToolBase::onRightDown(pt);
}
void nmObjDeleteTool::onDeactivated()
{
// 切换活动工具时释放自定义光标。
resetDeleteCursor();
nmObjToolBase::onDeactivated();
}
bool nmObjDeleteTool::onLeftDown(const QPointF& pt)
{
if (m_pObj != nullptr) {
return nmObjToolBase::onLeftDown(pt);
}
// 命中时接管按下事件,防止区域标记等图元的移动工具抢先处理抬起事件。
return findNearestDeleteObj(pt) != nullptr;
}
bool nmObjDeleteTool::onLeftUp(const QPointF& pt)
{
if (m_pObj != nullptr) {
return nmObjToolBase::onLeftUp(pt);
}
// 按抬起位置重新命中,使用与悬停提示相同的对象筛选和距离规则。
ZxObjBase* obj = findNearestDeleteObj(pt);
if (obj != nullptr) {
// 弹出确认框之前恢复,确认或取消后都不残留删除光标。
resetDeleteCursor();
nmSingalCenter::getInstance()->emitSigDeleteOneObj(obj->getName(), (void*)this);
}
return true;
}
ZxObjBase* nmObjDeleteTool::findNearestDeleteObj(const QPointF& pt)
{
ZxRenderView* view = getView();
if (m_pPlot == nullptr || view == nullptr) {
return nullptr;
}
// 转为视口坐标后按 16 像素判断,保持不同缩放下的点击容差一致。
const QPointF mousePos = view->mapFromScene(pt);
const double hitRadius = 16.0;
double nearestDistanceSquared = hitRadius * hitRadius;
ZxObjBase* nearestObj = nullptr;
zxGmyBase geometry;
for (int i = 0; i < m_pPlot->getObjCount(); ++i) {
ZxObjBase* obj = m_pPlot->getObjByIndex(i);
if (obj == nullptr || !obj->isVisible()) {
continue;
}
const bool isFracture = _isSame("nObjLineFracture", obj->getTagName());
const bool isRegion = _isSame("nObjRegion", obj->getTagName());
if (!isFracture && !isRegion &&
!_isSame("nObjLineFault", obj->getTagName()) &&
!_isSame("nObjPointWell", obj->getTagName()) &&
!_isSame("nObjRegionMark", obj->getTagName())) {
continue;
}
// 保留原有规则:未绑定数据及 DFN 生成的裂缝不参与单独删除。
if (isFracture) {
nmObjLineCrack* fracture = dynamic_cast<nmObjLineCrack*>(obj);
nmPlotGraphicBinder* binder = fracture ? fracture->getGraphicBinder() : nullptr;
nmDataFracture* data = binder ? binder->getCrackDataFor(fracture) : nullptr;
if (data == nullptr || data->getFractureType().getValue() == tr("DFN")) {
continue;
}
}
QVector<QPointF> points = obj->getAllPos();
if (points.isEmpty()) {
continue;
}
for (int j = 0; j < points.count(); ++j) {
points[j] = view->mapFromScene(points[j]);
}
double distance = QLineF(mousePos, points[0]).length();
double distanceSquared = distance * distance;
// 裂缝、断层按开放折线计算,只有区域包含末点到首点的闭合边。
const int segmentCount = isRegion ? points.count() : points.count() - 1;
for (int j = 0; j < segmentCount; ++j) {
const QPointF& start = points[j];
const QPointF& end = points[(j + 1) % points.count()];
double segmentDistanceSquared;
if (start == end) {
// 重复节点按单点处理,避免零长度线段参与投影计算。
distance = QLineF(mousePos, start).length();
segmentDistanceSquared = distance * distance;
} else {
double t = 0.0;
segmentDistanceSquared = geometry.pointSegmentDistanceSquared2D(mousePos, start, end, t);
}
distanceSquared = qMin(distanceSquared, segmentDistanceSquared);
}
// 范围内优先选择最近图元;距离相同时保留先命中的对象。
if (distanceSquared <= hitRadius * hitRadius &&
(nearestObj == nullptr || distanceSquared < nearestDistanceSquared)) {
nearestDistanceSquared = distanceSquared;
nearestObj = obj;
}
}
return nearestObj;
}
void nmObjDeleteTool::onPaint(QPainter* painter, const ZxPaintParam& param)
{
if (m_pObj == nullptr) {
// 缩放或图元变化后重新判断命中,不再在画布上绘制垃圾桶。
updateDeleteCursor(getCurrentPos());
}
}