logplus/logPlus/TransparentDraggableCrackObject.cpp
2026-04-16 10:22:24 +08:00

620 lines
24 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "TransparentDraggableCrackObject.h"
#include <QMouseEvent>
#include <QWheelEvent>
#include <QDebug>
#include <cmath>
QPointer<TransparentDraggableCrackObject> TransparentDraggableCrackObject::s_activeObject = nullptr;
TransparentDraggableCrackObject::TransparentDraggableCrackObject(QCustomPlot *parentPlot,
const QString &strUuid,
double depth,
CurveType type)
: QObject(parentPlot)
, mPlot(parentPlot)
, m_type(type)
, m_uuid(strUuid)
, m_depth(depth)
, m_endX(mPlot->xAxis->range().upper)
, mCurve(nullptr)
, m_tracer1(nullptr)
, m_tracer2(nullptr)
, m_dragStateA(IdleA)
, m_isAddingLine(false)
, m_offsetXB(0.0), m_offsetYB(0.0)
, m_dragStateB(IdleB)
, m_draggedLineIndex(-1)
, m_curveC(nullptr)
, m_offsetXC(0.0), m_offsetYC(0.0)
, m_cFinished(false)
, m_cDragging(false)
, m_draggingPoint(false)
, m_draggedPointIndex(-1)
{
// 注意:不要在这里清除全局活动对象,否则会影响已创建的其他对象
if (m_type == TYPE_A) {
m_orig_x1 = m_endX * 0.18;
m_orig_x2 = m_endX * 0.82;
m_orig_y1 = depth + 1.0;
m_orig_y2 = depth - 1.0;
m_orig_startX = 0;
m_orig_endX = m_endX;
m_orig_startDirX = m_endX * 0.3;
m_orig_endDirX = m_endX * 0.7;
m_offsetXA = 0.0;
m_offsetYA = 0.0;
mCurve = new QCPItemCurve(mPlot);
mCurve->setPen(QPen(Qt::black, 2));
mCurve->setLayer("overlay");
updateCurveFromTargets();
updateCurvePosition();
auto createTracer = [this](double x, double y, const QColor &color) -> QCPItemTracer* {
QCPItemTracer *t = new QCPItemTracer(mPlot);
t->setStyle(QCPItemTracer::tsCircle);
t->setSize(12);
t->setPen(QPen(Qt::black, 1));
t->setBrush(QBrush(color));
t->setSelectable(true);
t->setLayer("overlay");
t->position->setCoords(x, y);
return t;
};
m_tracer1 = createTracer(m_orig_x1 + m_offsetXA, m_orig_y1 + m_offsetYA, Qt::cyan);
m_tracer2 = createTracer(m_orig_x2 + m_offsetXA, m_orig_y2 + m_offsetYA, Qt::cyan);
updateTracers();
}
else if (m_type == TYPE_B) {
// nothing
}
else if (m_type == TYPE_C) {
m_curveC = new QCPCurve(mPlot->xAxis, mPlot->yAxis);
m_curveC->setPen(QPen(Qt::red, 2));
m_curveC->setScatterStyle(QCPScatterStyle::ssCircle);
m_curveC->setLineStyle(QCPCurve::lsLine);
m_curveC->setLayer("overlay");
}
mPlot->installEventFilter(this);
mPlot->replot();
}
TransparentDraggableCrackObject::~TransparentDraggableCrackObject()
{
if (m_type == TYPE_B) clearLines();
if (m_type == TYPE_C) clearPolylineC();
if (s_activeObject == this) s_activeObject = nullptr;
}
// ========== 模式A辅助 ==========
void TransparentDraggableCrackObject::updateCurveFromTargets()
{
double t1 = m_orig_x1 / m_endX;
double t2 = m_orig_x2 / m_endX;
double u1 = 1 - t1, u2 = 1 - t2;
double P0x = 0, P0y = m_depth;
double P3x = m_endX, P3y = m_depth;
double P1x = m_orig_x1, P2x = m_orig_x2;
double a11 = 3 * u1 * u1 * t1;
double a12 = 3 * u1 * t1 * t1;
double b1 = m_orig_y1 - (u1*u1*u1 * P0y + t1*t1*t1 * P3y);
double a21 = 3 * u2 * u2 * t2;
double a22 = 3 * u2 * t2 * t2;
double b2 = m_orig_y2 - (u2*u2*u2 * P0y + t2*t2*t2 * P3y);
double det = a11 * a22 - a12 * a21;
if (fabs(det) > 1e-6) {
double P1y = (b1 * a22 - a12 * b2) / det;
double P2y = (a11 * b2 - b1 * a21) / det;
m_orig_startDirX = P1x;
m_orig_startDirY = P1y;
m_orig_endDirX = P2x;
m_orig_endDirY = P2y;
}
}
void TransparentDraggableCrackObject::updateCurvePosition()
{
if (!mCurve) return;
mCurve->start->setCoords(m_orig_startX + m_offsetXA, m_depth + m_offsetYA);
mCurve->end->setCoords(m_orig_endX + m_offsetXA, m_depth + m_offsetYA);
mCurve->startDir->setCoords(m_orig_startDirX + m_offsetXA, m_orig_startDirY + m_offsetYA);
mCurve->endDir->setCoords(m_orig_endDirX + m_offsetXA, m_orig_endDirY + m_offsetYA);
}
void TransparentDraggableCrackObject::updateTracers()
{
if (!m_tracer1 || !m_tracer2) return;
m_tracer1->position->setCoords(m_orig_x1 + m_offsetXA, m_orig_y1 + m_offsetYA);
m_tracer2->position->setCoords(m_orig_x2 + m_offsetXA, m_orig_y2 + m_offsetYA);
}
void TransparentDraggableCrackObject::setTracerHighlight(QCPItemTracer *tracer, bool highlight)
{
if (!tracer) return;
if (highlight) {
tracer->setPen(QPen(Qt::red, 2));
tracer->setBrush(QBrush(Qt::red));
} else {
tracer->setPen(QPen(Qt::black, 1));
tracer->setBrush(QBrush(Qt::cyan));
}
mPlot->replot();
}
// ========== 模式B辅助 ==========
void TransparentDraggableCrackObject::updateLineEndpoints(LineItem &item)
{
item.startTracer->position->setCoords(item.startOrig.x() + m_offsetXB, item.startOrig.y() + m_offsetYB);
item.endTracer->position->setCoords(item.endOrig.x() + m_offsetXB, item.endOrig.y() + m_offsetYB);
}
void TransparentDraggableCrackObject::updateLinesPosition()
{
for (auto &item : m_lines) {
item.line->start->setCoords(item.startOrig.x() + m_offsetXB, item.startOrig.y() + m_offsetYB);
item.line->end->setCoords(item.endOrig.x() + m_offsetXB, item.endOrig.y() + m_offsetYB);
updateLineEndpoints(item);
}
}
void TransparentDraggableCrackObject::clearLines()
{
for (auto &item : m_lines) {
mPlot->removeItem(item.line);
delete item.line;
if (item.startTracer) { mPlot->removeItem(item.startTracer); delete item.startTracer; }
if (item.endTracer) { mPlot->removeItem(item.endTracer); delete item.endTracer; }
}
m_lines.clear();
m_isAddingLine = false;
mPlot->replot();
}
// ========== 模式C辅助 ==========
void TransparentDraggableCrackObject::updatePolylineC(bool closed)
{
if (!m_curveC) return;
if (m_pointsC.isEmpty()) {
m_curveC->setData(QVector<double>(), QVector<double>());
return;
}
QVector<double> xs, ys;
for (const auto &p : m_pointsC) {
xs << p.x() + m_offsetXC;
ys << p.y() + m_offsetYC;
}
if (closed && m_pointsC.size() >= 3) {
xs << m_pointsC.first().x() + m_offsetXC;
ys << m_pointsC.first().y() + m_offsetYC;
}
m_curveC->setData(xs, ys);
}
void TransparentDraggableCrackObject::clearPolylineC()
{
QVector<QCPItemText*> labelsToDelete = m_labelsC;
m_labelsC.clear();
for (auto label : labelsToDelete) {
if (label) {
mPlot->removeItem(label);
delete label;
}
}
m_pointsC.clear();
if (m_curveC) {
m_curveC->setData(QVector<double>(), QVector<double>());
}
m_offsetXC = m_offsetYC = 0.0;
m_cFinished = false;
m_draggingPoint = false;
m_draggedPointIndex = -1;
if (s_activeObject == this) s_activeObject = nullptr;
mPlot->replot();
}
// ========== 状态重置 ==========
void TransparentDraggableCrackObject::deactivate()
{
qDebug() << "[Deactivate]" << m_uuid;
if (m_type == TYPE_A) {
setTracerHighlight(m_tracer1, false);
setTracerHighlight(m_tracer2, false);
m_dragStateA = IdleA;
} else if (m_type == TYPE_B) {
m_dragStateB = IdleB;
m_draggedLineIndex = -1;
} else if (m_type == TYPE_C) {
m_cDragging = false;
m_draggingPoint = false;
m_draggedPointIndex = -1;
}
mPlot->replot();
}
// ========== 事件过滤器 ==========
bool TransparentDraggableCrackObject::eventFilter(QObject *obj, QEvent *event)
{
if (obj != mPlot) return false;
if (event->type() == QEvent::Wheel) return false;
QMouseEvent *me = static_cast<QMouseEvent*>(event);
bool shiftPressed = (me->modifiers() & Qt::ShiftModifier);
// 鼠标按下
if (event->type() == QEvent::MouseButtonPress && me->button() == Qt::LeftButton) {
bool hit = false;
// ========== 模式B添加直线优先级最高 ==========
if (m_type == TYPE_B && shiftPressed) {
if (!m_isAddingLine) {
double x = mPlot->xAxis->pixelToCoord(me->pos().x());
double y = mPlot->yAxis->pixelToCoord(me->pos().y());
m_tempPoint = QPointF(x - m_offsetXB, y - m_offsetYB);
m_isAddingLine = true;
qDebug() << "Start adding line, point:" << m_tempPoint;
event->accept();
return true;
} else {
double x = mPlot->xAxis->pixelToCoord(me->pos().x());
double y = mPlot->yAxis->pixelToCoord(me->pos().y());
QPointF p2(x - m_offsetXB, y - m_offsetYB);
QCPItemLine *line = new QCPItemLine(mPlot);
line->setPen(QPen(Qt::blue, 2));
line->setLayer("overlay");
auto createTracer = [this](double x, double y, const QColor &color) -> QCPItemTracer* {
QCPItemTracer *t = new QCPItemTracer(mPlot);
t->setStyle(QCPItemTracer::tsCircle);
t->setSize(10);
t->setPen(QPen(Qt::black, 1));
t->setBrush(QBrush(color));
t->setSelectable(true);
t->setLayer("overlay");
t->position->setCoords(x + m_offsetXB, y + m_offsetYB);
return t;
};
QCPItemTracer *startTracer = createTracer(m_tempPoint.x(), m_tempPoint.y(), Qt::green);
QCPItemTracer *endTracer = createTracer(p2.x(), p2.y(), Qt::green);
LineItem item;
item.startOrig = m_tempPoint;
item.endOrig = p2;
item.line = line;
item.startTracer = startTracer;
item.endTracer = endTracer;
m_lines.append(item);
updateLinesPosition();
m_isAddingLine = false;
mPlot->replot();
qDebug() << "Line added from" << m_tempPoint << "to" << p2;
event->accept();
return true;
}
}
// ========== 模式A ==========
if (m_type == TYPE_A && !shiftPressed) {
QPointF pixel = me->localPos();
double d1 = m_tracer1->selectTest(pixel, false);
double d2 = m_tracer2->selectTest(pixel, false);
double curveDist = mCurve->selectTest(pixel, false);
if (d1 >= 0 && d1 < 15 || d2 >= 0 && d2 < 15 || (curveDist >= 0 && curveDist < 5)) {
hit = true;
if (s_activeObject && s_activeObject != this) {
s_activeObject->deactivate();
}
s_activeObject = this;
if (d1 >= 0 && d1 < 15) {
m_dragStateA = DraggingPoint1;
setTracerHighlight(m_tracer1, true);
} else if (d2 >= 0 && d2 < 15) {
m_dragStateA = DraggingPoint2;
setTracerHighlight(m_tracer2, true);
} else {
m_dragStateA = DraggingCurveA;
m_lastDragPixelA = pixel;
}
event->accept();
return true;
}
}
// ========== 模式B拖拽非Shift ==========
if (m_type == TYPE_B && !shiftPressed && !m_isAddingLine) {
QPointF pixel = me->localPos();
bool found = false;
for (int i = 0; i < m_lines.size(); ++i) {
auto &item = m_lines[i];
if (item.startTracer->selectTest(pixel, false) >= 0 && item.startTracer->selectTest(pixel, false) < 10) {
found = true;
hit = true;
if (s_activeObject && s_activeObject != this) s_activeObject->deactivate();
s_activeObject = this;
m_dragStateB = DraggingStartPoint;
m_draggedLineIndex = i;
m_bDragStart = pixel;
event->accept();
return true;
}
if (item.endTracer->selectTest(pixel, false) >= 0 && item.endTracer->selectTest(pixel, false) < 10) {
found = true;
hit = true;
if (s_activeObject && s_activeObject != this) s_activeObject->deactivate();
s_activeObject = this;
m_dragStateB = DraggingEndPoint;
m_draggedLineIndex = i;
m_bDragStart = pixel;
event->accept();
return true;
}
}
if (!found) {
for (int i = 0; i < m_lines.size(); ++i) {
double dist = m_lines[i].line->selectTest(pixel, false);
if (dist >= 0 && dist < 3) {
hit = true;
if (s_activeObject && s_activeObject != this) s_activeObject->deactivate();
s_activeObject = this;
m_dragStateB = DraggingLineOverall;
m_bDragStart = pixel;
event->accept();
return true;
}
}
}
}
// ========== 模式C ==========
// ========== 模式C ==========
if (m_type == TYPE_C) {
// 1. 左键添加点Shift
if (shiftPressed && me->button() == Qt::LeftButton && !m_cFinished) {
if (s_activeObject != this) {
if (s_activeObject) s_activeObject->deactivate();
s_activeObject = this;
}
double x = mPlot->xAxis->pixelToCoord(me->pos().x());
double y = mPlot->yAxis->pixelToCoord(me->pos().y());
QPointF pt(x - m_offsetXC, y - m_offsetYC);
m_pointsC.append(pt);
QCPItemText *label = new QCPItemText(mPlot);
label->setText(QString::number(m_pointsC.size()));
label->setFont(QFont("Arial", 10, QFont::Bold));
label->setColor(Qt::white);
label->setBrush(QBrush(Qt::blue));
label->setPen(QPen(Qt::black, 1));
// label->setPadding(2);
label->position->setCoords(x, y);
label->setPositionAlignment(Qt::AlignCenter);
label->setLayer("overlay");
m_labelsC.append(label);
updatePolylineC(false);
mPlot->replot();
event->accept();
return true;
}
// 2. 左键空白闭环非Shift未完成时
if (!shiftPressed && me->button() == Qt::LeftButton && !m_cFinished) {
if (m_pointsC.size() >= 3) {
m_cFinished = true;
updatePolylineC(true);
mPlot->replot();
} else {
clearPolylineC();
}
if (s_activeObject == this) {
s_activeObject->deactivate();
s_activeObject = nullptr;
}
event->accept();
return true;
}
// 3. 拖拽整体或点已完成非Shift左键
if (!shiftPressed && me->button() == Qt::LeftButton && m_cFinished) {
QPointF pixel = me->localPos();
// 优先检测是否命中某个数据点(通过标签)
int hitPointIndex = -1;
for (int i = 0; i < m_labelsC.size(); ++i) {
double dist = m_labelsC[i]->selectTest(pixel, false);
if (dist >= 0 && dist < 10) {
hitPointIndex = i;
break;
}
}
if (hitPointIndex >= 0) {
hit = true;
if (s_activeObject != this) {
if (s_activeObject) s_activeObject->deactivate();
s_activeObject = this;
}
m_draggingPoint = true;
m_draggedPointIndex = hitPointIndex;
// 记录拖拽开始时的鼠标像素坐标和点的原始坐标(用于计算偏移)
m_cDragStart = pixel;
event->accept();
return true;
} else {
// 未命中点,检测曲线本体
double curveDist = m_curveC ? m_curveC->selectTest(pixel, false) : -1;
if (curveDist >= 0 && curveDist < 3) {
hit = true;
if (s_activeObject != this) {
if (s_activeObject) s_activeObject->deactivate();
s_activeObject = this;
}
m_cDragging = true;
m_cDragStart = pixel;
event->accept();
return true;
} else {
// 未命中任何图元,清除活动对象
if (s_activeObject == this) {
s_activeObject->deactivate();
s_activeObject = nullptr;
}
return false;
}
}
}
}
// 未命中任何图元:清除全局活动对象
if (!hit) {
if (s_activeObject) {
s_activeObject->deactivate();
s_activeObject = nullptr;
}
return false;
}
return false;
}
// 鼠标移动
if (event->type() == QEvent::MouseMove) {
if (s_activeObject != this) return false;
// 模式A移动
if (m_type == TYPE_A) {
if (m_dragStateA == DraggingPoint1 || m_dragStateA == DraggingPoint2) {
double newY = mPlot->yAxis->pixelToCoord(me->localPos().y());
double currentY = (m_dragStateA == DraggingPoint1) ? m_orig_y1 : m_orig_y2;
double deltaY = newY - (currentY + m_offsetYA);
if (fabs(deltaY) > 5) deltaY = (deltaY > 0 ? 5 : -5);
double newOrigY = currentY + deltaY;
double minY = m_depth - 100;
double maxY = m_depth + 100;
newOrigY = qBound(minY, newOrigY, maxY);
if (m_dragStateA == DraggingPoint1) m_orig_y1 = newOrigY;
else m_orig_y2 = newOrigY;
updateCurveFromTargets();
updateCurvePosition();
updateTracers();
mPlot->replot();
return true;
} else if (m_dragStateA == DraggingCurveA) {
QPointF current = me->localPos();
double oldX = mPlot->xAxis->pixelToCoord(m_lastDragPixelA.x());
double newX = mPlot->xAxis->pixelToCoord(current.x());
double oldY = mPlot->yAxis->pixelToCoord(m_lastDragPixelA.y());
double newY = mPlot->yAxis->pixelToCoord(current.y());
double dx = newX - oldX;
double dy = newY - oldY;
m_offsetXA += dx;
m_offsetYA += dy;
updateCurvePosition();
updateTracers();
mPlot->replot();
m_lastDragPixelA = current;
return true;
}
}
// 模式B移动
else if (m_type == TYPE_B && m_dragStateB != IdleB) {
QPointF current = me->localPos();
double oldX = mPlot->xAxis->pixelToCoord(m_bDragStart.x());
double newX = mPlot->xAxis->pixelToCoord(current.x());
double oldY = mPlot->yAxis->pixelToCoord(m_bDragStart.y());
double newY = mPlot->yAxis->pixelToCoord(current.y());
double dx = newX - oldX;
double dy = newY - oldY;
if (m_dragStateB == DraggingLineOverall) {
m_offsetXB += dx;
m_offsetYB += dy;
updateLinesPosition();
} else if (m_dragStateB == DraggingStartPoint || m_dragStateB == DraggingEndPoint) {
int idx = m_draggedLineIndex;
if (idx >= 0 && idx < m_lines.size()) {
LineItem &item = m_lines[idx];
if (m_dragStateB == DraggingStartPoint) {
item.startOrig.rx() += dx;
item.startOrig.ry() += dy;
} else {
item.endOrig.rx() += dx;
item.endOrig.ry() += dy;
}
item.line->start->setCoords(item.startOrig.x() + m_offsetXB, item.startOrig.y() + m_offsetYB);
item.line->end->setCoords(item.endOrig.x() + m_offsetXB, item.endOrig.y() + m_offsetYB);
updateLineEndpoints(item);
}
}
mPlot->replot();
m_bDragStart = current;
return true;
}
// 模式C移动
// 模式C移动
else if (m_type == TYPE_C) {
if (m_cDragging) {
// 整体拖拽
QPointF current = me->localPos();
double oldX = mPlot->xAxis->pixelToCoord(m_cDragStart.x());
double newX = mPlot->xAxis->pixelToCoord(current.x());
double oldY = mPlot->yAxis->pixelToCoord(m_cDragStart.y());
double newY = mPlot->yAxis->pixelToCoord(current.y());
double dx = newX - oldX;
double dy = newY - oldY;
m_offsetXC += dx;
m_offsetYC += dy;
for (int i = 0; i < m_pointsC.size(); ++i) {
m_labelsC[i]->position->setCoords(m_pointsC[i].x() + m_offsetXC, m_pointsC[i].y() + m_offsetYC);
}
updatePolylineC(m_cFinished && m_pointsC.size() >= 3);
mPlot->replot();
m_cDragStart = current;
return true;
} else if (m_draggingPoint) {
// 点拖拽:支持二维移动
QPointF current = me->localPos();
// 计算鼠标在轴坐标系下的移动量(直接使用像素转坐标,但为了精确,计算差值)
double oldX = mPlot->xAxis->pixelToCoord(m_cDragStart.x());
double newX = mPlot->xAxis->pixelToCoord(current.x());
double oldY = mPlot->yAxis->pixelToCoord(m_cDragStart.y());
double newY = mPlot->yAxis->pixelToCoord(current.y());
double dx = newX - oldX;
double dy = newY - oldY;
// 更新点的原始坐标
m_pointsC[m_draggedPointIndex].rx() += dx;
m_pointsC[m_draggedPointIndex].ry() += dy;
// 更新标签位置
m_labelsC[m_draggedPointIndex]->position->setCoords(m_pointsC[m_draggedPointIndex].x() + m_offsetXC,
m_pointsC[m_draggedPointIndex].y() + m_offsetYC);
updatePolylineC(m_cFinished && m_pointsC.size() >= 3);
mPlot->replot();
// 更新拖拽起始点
m_cDragStart = current;
return true;
}
}
return false;
}
// 鼠标释放
if (event->type() == QEvent::MouseButtonRelease && me->button() == Qt::LeftButton) {
if (s_activeObject != this) return false;
if (m_type == TYPE_A && m_dragStateA != IdleA) {
setTracerHighlight(m_tracer1, false);
setTracerHighlight(m_tracer2, false);
m_dragStateA = IdleA;
return true;
} else if (m_type == TYPE_B && m_dragStateB != IdleB) {
m_dragStateB = IdleB;
m_draggedLineIndex = -1;
return true;
} else if (m_type == TYPE_C) {
if (m_cDragging) {
m_cDragging = false;
return true;
} else if (m_draggingPoint) {
m_draggingPoint = false;
m_draggedPointIndex = -1;
return true;
}
}
}
return false;
}