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++
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);
|
|
}
|