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