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.

352 lines
6.7 KiB
C++

#include "stdafx.h"
#include "ItemEraser.h"
#include "SigmaDoc.h"
#include "SigmaView.h"
#include "ActionListItem.h"
#include "ActionComboItem.h"
#include "ActionAddItem.h"
#include "ActionDeleteItem.h"
#include "clipper2/clipper.h"
#undef min
#undef max
CItemEraser::CItemEraser(CSigmaDoc* ppDoc)
: CItem(ppDoc)
{
m_nodeDrawer = std::make_unique<CurveNodeDrawer>(ppDoc);
m_token = ppDoc->GetView()->GetViewChangedListener().AddListener([this] { isViewChanged = true; });
}
CItemEraser::~CItemEraser()
{
GetDoc()->GetView()->GetViewChangedListener().RemoveListener(m_token);
}
void CItemEraser::OnLButtonDblClk(UINT nFlags, CPoint point)
{
}
void CItemEraser::DrawAssistant(CDC* pDC, int mouseX, int mouseY)
{
if (isViewChanged)
{
DrawCurveNode(pDC);
isViewChanged = false;
}
DrawEraser(pDC, mouseX, mouseY, m_eraseRadius);
}
void CItemEraser::OnLButtonDown(CDC *pDC, UINT nFlags, CPoint point, int vk)
{
m_start = true;
m_path.clear();
}
void CItemEraser::OnLButtonUp(CDC* pDC, UINT nFlags, CPoint point, int vk)
{
// 根据需求,用户单击的时候要删除与当前框相交的部分,单击的时候 m_path 为空,我们这时只需要将当前点存到 m_path 中即可
if (m_path.empty())
{
CPoint2D realPoint = GetDC()->GetReal(point);
m_path.push_back(realPoint);
}
Erase();
DrawCurveNode(pDC);
DrawEraser(pDC, point.x, point.y, m_eraseRadius);
m_start = false;
}
void CItemEraser::OnRButtonDown(UINT nFlags, CPoint point)
{
}
BOOL CItemEraser::OnRButtonUp(UINT nFlags, CPoint point)
{
return TRUE;
}
void CItemEraser::OnMouseWheel(uint32_t button, int32_t clicks, int32_t x, int32_t y, int32_t delta)
{
ResetLast(-1, -1, -1);
}
int CItemEraser::OnMouseMove(CDC* pDC, UINT nFlags, CPoint point)
{
auto realPoint = GetDC()->GetReal(point);
if (m_start)
{
m_path.push_back(realPoint);
if (m_path.size() > 5)
{
Erase();
// 光标移动其实都是跳得移动的,这里如果不把最后一个点双重新放回去,可能会导致有一小段直接被跳过
CPoint2D last = m_path[m_path.size() - 1];
m_path.clear();
m_path.push_back(last);
}
}
DrawAssistant(pDC, point.x, point.y);
return 0;
}
BOOL CItemEraser::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
return FALSE;
}
BOOL CItemEraser::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags)
{
switch (nChar)
{
case VK_OEM_COMMA:
ZoomEraseRadius(-ZOOM_VALUE);
ResetLast(-1, -1, -1);
Redraw();
break;
case VK_OEM_PERIOD:
ZoomEraseRadius(ZOOM_VALUE);
ResetLast(-1, -1, -1);
Redraw();
break;
}
return FALSE;
}
void CItemEraser::OnDraw(CXyDC* pXyDC, CDC* pDC)
{
}
void CItemEraser::OnCancel(void)
{
}
EraserMode CItemEraser::GetEraserMode() const
{
return m_mode;
}
void CItemEraser::SetEraserMode(EraserMode mode)
{
m_mode = mode;
}
int CItemEraser::GetEraserRadius() const
{
return m_eraseRadius;
}
void CItemEraser::SetEraserRadius(int radius)
{
if (radius <= 0)
{
return;
}
m_eraseRadius = radius;
}
void CItemEraser::Erase()
{
CSigmaDoc* pDoc = GetDoc();
CXy* pXy = pDoc->GetDraw();
std::unique_ptr<CCurveEx> pCurve = GetEraserPath();
if (pCurve == nullptr)
{
return;
}
Eraser eraser;
eraser.SetMode(m_mode);
eraser.SetXY(pXy);
eraser.Cut(*pCurve);
SetAction(eraser.m_plAdd, eraser.m_plDel);
Redraw();
}
void CItemEraser::SetAction(const CPositionList& plAdd, const CPositionList& plDel)
{
CSigmaDoc* pDoc = GetDoc();
auto pAction = std::make_unique<CActionComboItem>(pDoc, 0);
if (plDel.GetCount() > 0)
{
auto pDelAction = std::make_unique<CActionDeleteItem>(pDoc, IDS_STRING_ACTION_DELETE, plDel);
pDelAction->Do();
pAction->AddAction(pDelAction.release());
}
if (plAdd.GetCount() > 0)
{
auto pAddAction = std::make_unique<CActionAddItem>(pDoc, IDS_STRING_ACTION_ADD, plAdd);
pAddAction->Do();
pAction->AddAction(pAddAction.release());
}
if (pAction->GetCount() > 0)
{
pDoc->SetActionItem(pAction.release());
}
}
void CItemEraser::Redraw()
{
GetView()->Notify(redraw);
}
void CItemEraser::DrawCircle(CDC* pDC, int centerX, int centerY, int radius)
{
CRect rect;
rect.left = centerX - radius;
rect.top = centerY - radius;
rect.right = centerX + radius;
rect.bottom = centerY + radius;
CPen pen(PS_SOLID, 3, m_penColor);
CPen* pOldPen = pDC->SelectObject(&pen);
pDC->Rectangle(rect);
pDC->SelectObject(pOldPen);
}
void CItemEraser::DrawEraser(CDC* pDC, int centerX, int centerY, int radius)
{
int nOldROP2 = pDC->SetROP2(R2_NOTXORPEN);
if (m_lastX > 0 && m_lastY > 0)
{
DrawCircle(pDC, m_lastX, m_lastY, m_lastRadius);
ResetLast(-1, -1, -1);
}
DrawCircle(pDC, centerX, centerY, radius);
ResetLast(centerX, centerY, radius);
pDC->SetROP2(nOldROP2);
}
void CItemEraser::DrawCurveNode(CDC* pDC)
{
CXyElementFilter filter;
filter.addType(DOUBLEFOX_CURVE);
CXy* pXy = GetDoc()->m_pXy;
CPositionList select;
pXy->GetElement(filter, select);
m_nodeDrawer->Draw(pDC, select);
}
void CItemEraser::ResetLast(int x, int y, int radius)
{
m_lastX = x;
m_lastY = y;
m_lastRadius = radius;
}
RTree<POSITION, double, 2>* CItemEraser::GetSpatialTree()
{
if (m_rtree == nullptr)
{
CXyElementFilter filter;;
filter.addType(DOUBLEFOX_CURVE);
m_rtree = BuildRTree(*GetDoc()->m_pXy, filter);
}
return m_rtree.get();
}
std::unique_ptr<CCurveEx> CItemEraser::GetEraserPath() const
{
using Clipper2Lib::PathD;
using Clipper2Lib::PathsD;
using Clipper2Lib::JoinType;
using Clipper2Lib::EndType;
using Clipper2Lib::InflatePaths;
using Clipper2Lib::MakePathD;
using Clipper2Lib::SimplifyPaths;
if (m_path.empty())
{
return nullptr;
}
PathD originalPath;
for (const auto& point : m_path)
{
originalPath.emplace_back(point.x0, point.y0);
}
PathsD subject{ std::move(originalPath) };
PathsD simplified = SimplifyPaths(subject, 1.5);
// 计算膨胀参数,我们使用画笔宽度对应的真实宽度
double delta = GetDC()->GetRealWidth(static_cast<double>(m_eraseRadius));
//膨胀
PathsD solution = InflatePaths(simplified, delta, JoinType::Miter, EndType::Square);
if (solution.empty())
{
return nullptr;
}
// 只使用结果中的第一条线
PathD& expandedPath = solution[0];
if (expandedPath.empty())
{
return nullptr;
}
auto pCurve = std::make_unique<CCurveEx>();
pCurve->Create((int)expandedPath.size() + 1);
size_t i = 0;
for (; i < expandedPath.size(); i++)
{
pCurve->x[i] = expandedPath[i].x;
pCurve->y[i] = expandedPath[i].y;
}
pCurve->x[i] = expandedPath[0].x;
pCurve->y[i] = expandedPath[0].y;
pCurve->GetLocation();
return pCurve;
}
std::vector<CPoint> CItemEraser::GetScreenPoints(const std::vector<CPoint2D>& points) const
{
std::vector<CPoint> screenPoints;
screenPoints.reserve(points.size());
CXyDC *pXyDC = GetDC();
for (CPoint2D point : points)
{
screenPoints.emplace_back(pXyDC->GetScreen(point));
}
return screenPoints;
}
void CItemEraser::ZoomEraseRadius(int radius)
{
m_eraseRadius = std::clamp(m_eraseRadius + radius, MIN_ERASE_RADIUS, MAX_ERASE_RADIUS);
}