#pragma once #include "stdafx.h" #include "ItemEraser.h" #include "SigmaDoc.h" #include "ActionListItem.h" #include "ActionBackupItem.h" #include "Util.h" #include enum class EraserMode { Normal, Segments, Nodes, }; class ICurveEraseStrategy { public: virtual ~ICurveEraseStrategy() = default; virtual void SetContext(CXy* pXy, CPositionList* pPlDel, CPositionList* pPlAdd) = 0; virtual void Erase(POSITION pos, const CCurveEx& cutter) = 0; }; class NormalEraseStrategy : public ICurveEraseStrategy { public: void SetContext(CXy* pXy, CPositionList* pPlDel, CPositionList* pPlAdd) override { m_pXy = pXy; m_plDel = pPlDel; m_plAdd = pPlAdd; } void Erase(POSITION pos, const CCurveEx& cutter) override { const auto erasePolygon = CurveToPathD(cutter); if (erasePolygon.size() < 3) { return; } CRect8 range(1e100, -1e100, -1e100, 1e100); const_cast(cutter).GetRange(range); COne* pOne = m_pXy->GetAt(pos); CCurveEx* pCurve = pOne->GetValueSafe(); if (!pCurve) { return; } CRect8 currRange(1e100, -1e100, -1e100, 1e100); pCurve->GetRange(currRange); if (!IsIntersecting(range, currRange)) { return; } auto sourcePath = CurveToPathD(*pCurve); // 如果完全包含,则只需要将这条曲线直接删除 if (IsPathInsidePolygon(sourcePath, erasePolygon)) { m_plDel->AddTail(pos); return; } const Clipper2Lib::PathsD subject{ sourcePath }; const Clipper2Lib::PathsD clip{ erasePolygon }; Clipper2Lib::ClipperD clipper; clipper.AddOpenSubject(subject); clipper.AddClip(clip); Clipper2Lib::PathsD closed_paths; Clipper2Lib::PathsD open_paths; clipper.Execute(Clipper2Lib::ClipType::Difference, Clipper2Lib::FillRule::NonZero, closed_paths, open_paths); // FIXME: 我不知道能不能用这个判断是否进行了切割,先暂时这么处理 if (open_paths.empty()) { return; } m_plDel->AddTail(pos); for (const auto& path : open_paths) { if (path.size() < 2) continue; auto newCurve = std::make_unique(); newCurve->Create(static_cast(path.size())); for (size_t i = 0; i < path.size(); ++i) { newCurve->x[i] = path[i].x; newCurve->y[i] = path[i].y; } newCurve->GetLocation(); POSITION newPos = m_pXy->AddElement(newCurve.release(), DOUBLEFOX_CURVE); COne* pNewOne = m_pXy->GetAt(newPos); pNewOne->SetLayer(pOne->GetLayer()); pNewOne->CloneOtherParameter(*pOne); m_plAdd->AddTail(newPos); } } private: Clipper2Lib::PathD CurveToPathD(const CCurveEx& curve) const { Clipper2Lib::PathD path; for (int32_t i = 0; i < curve.num; i++) { double x = curve.x[i]; double y = curve.y[i]; path.emplace_back(x, y); } return path; } bool IsPathInsidePolygon(const Clipper2Lib::PathD& path, const Clipper2Lib::PathD& polygon) { if (polygon.empty()) { return false; } const auto& clip = polygon; for (const auto& pt : path) { if (Clipper2Lib::PointInPolygon(pt, clip) != Clipper2Lib::PointInPolygonResult::IsInside) { return false; // 只要有一个点不在多边形内,就认为不完全包含 } } return true; } CXy* m_pXy = nullptr; CPositionList* m_plDel = nullptr; CPositionList* m_plAdd = nullptr; }; class SegmentEraseStrategy : public ICurveEraseStrategy { public: void SetContext(CXy* pXy, CPositionList* pPlDel, CPositionList* pPlAdd) override { m_pXy = pXy; m_plDel = pPlDel; m_plAdd = pPlAdd; } void Erase(POSITION pos, const CCurveEx& cutter) override { CRect8 range(1e100, -1e100, -1e100, 1e100); const_cast(cutter).GetRange(range); COne* pOne = m_pXy->GetAt(pos); CCurveEx* pCurve = pOne->GetValueSafe(); if (pCurve == nullptr) { return; } CRect8 currRange(1e100, -1e100, -1e100, 1e100); pCurve->GetRange(currRange); if (!IsIntersecting(range, currRange)) { return; } if (IsInRect(currRange, range)) { m_plDel->AddTail(pos); return; } DoCut(pCurve, cutter, pos, pOne); } private: void DoCut(CCurveEx* pOriginCurve, const CCurveEx& cutter, POSITION pos, COne* pOne) { std::vector segmentsIntersection; for (int32_t i = 0; i < pOriginCurve->num - 1; i++) { double p0_x = pOriginCurve->x[i], p0_y = pOriginCurve->y[i]; double p1_x = pOriginCurve->x[i + 1], p1_y = pOriginCurve->y[i + 1]; bool result = IsPointInPolygon(p0_x, p0_y, cutter) && IsPointInPolygon(p1_x, p1_y, cutter); if (!result) result = IsIntersection(p0_x, p0_y, p1_x, p1_y, cutter); segmentsIntersection.push_back(result ? 1 : 0); } if (NoneOf(segmentsIntersection, [](int32_t n) { return n == 1; })) { return; } auto ranges = GetSplitIndexs(segmentsIntersection, 1); for (const auto& vec : ranges) { auto newCurve = SliceCurve(*pOriginCurve, vec[0], *(vec.end() - 1) + 2); POSITION newPos = m_pXy->AddElement(newCurve.release(), DOUBLEFOX_CURVE); COne* pNewOne = m_pXy->GetAt(newPos); pNewOne->SetLayer(pOne->GetLayer()); pNewOne->CloneOtherParameter(*pOne); pNewOne->SetName(pOne->GetName()); m_plAdd->AddTail(newPos); } m_plDel->AddTail(pos); } std::unique_ptr SliceCurve(const CCurveEx& curve, int32_t startIndex, int32_t endIndex) const { auto newCurve = std::make_unique(); int32_t count = endIndex - startIndex; newCurve->Create(count); memcpy(newCurve->x, curve.x + startIndex, count * sizeof(double)); memcpy(newCurve->y, curve.y + startIndex, count * sizeof(double)); newCurve->GetLocation(); return newCurve; } bool IsIntersection(double x1, double y1, double x2, double y2, const CCurveEx& curve) const { for (int32_t i = 0; i < curve.num - 1; i++) { double x3 = curve.x[i], y3 = curve.y[i]; double x4 = curve.x[i + 1], y4 = curve.y[i + 1]; double ix = 0.0, iy = 0.0; if (GetLineIntersection(x1, y1, x2, y2, x3, y3, x4, y4, ix, iy) > 0) { return true; } } return false; } std::vector> GetSplitIndexs(const std::vector& vec, int32_t sep) { std::vector> result, curr; std::vector temp; for (size_t i = 0; i < vec.size(); i++) { if (vec[i] == sep) { if (!temp.empty()) { result.push_back(temp); temp.clear(); } } else { temp.push_back((int32_t)i); } } if (!temp.empty()) { result.push_back(temp); } return result; } CXy* m_pXy = nullptr; CPositionList* m_plDel = nullptr; CPositionList* m_plAdd = nullptr; }; class NodeEraseStrategy : public ICurveEraseStrategy { public: void SetContext(CXy* pXy, CPositionList* pPlDel, CPositionList* pPlAdd) override { m_pXy = pXy; m_plDel = pPlDel; m_plAdd = pPlAdd; } void Erase(POSITION pos, const CCurveEx& cutter) override { CRect8 range(1e100, -1e100, -1e100, 1e100); const_cast(cutter).GetRange(range); COne* pOne = m_pXy->GetAt(pos); CCurveEx* pCurve = pOne->GetValueSafe(); if (!pCurve) { return; } CRect8 currRange(1e100, -1e100, -1e100, 1e100); pCurve->GetRange(currRange); if (!IsIntersecting(range, currRange)) { return; } std::vector toRemove; for (int32_t i = 0; i < pCurve->num; ++i) { if (IsPointInPolygon(pCurve->x[i], pCurve->y[i], cutter)) { toRemove.push_back(i); } } if (toRemove.empty()) { return; } auto pNewCurve = std::make_unique(*pCurve); for (auto it = toRemove.rbegin(); it != toRemove.rend(); ++it) { RemoveCurveIndex(*pNewCurve, *it); } m_plDel->AddTail(pos); if (pNewCurve->num >= 2) { POSITION newPos = m_pXy->AddElement(pNewCurve.release(), DOUBLEFOX_CURVE); COne* pNewOne = m_pXy->GetAt(newPos); pNewOne->SetLayer(pOne->GetLayer()); pNewOne->CloneOtherParameter(*pOne); m_plAdd->AddTail(newPos); } } private: CXy* m_pXy = nullptr; CPositionList* m_plDel = nullptr; CPositionList* m_plAdd = nullptr; }; class Eraser { public: Eraser() { SetMode(EraserMode::Normal); } void SetXY(CXy* pXy) { m_pXy = pXy; } void Cut(const CCurveEx& curve) { m_plAdd.RemoveAll(); m_plDel.RemoveAll(); if (!m_pXy) { return; } CXyElementFilter filter; filter.addType(DOUBLEFOX_CURVE); CPositionList select; m_pXy->GetElement(filter, select); m_strategy->SetContext(m_pXy, &m_plDel, &m_plAdd); for (POSITION pos : CListToStdVector(select)) { m_strategy->Erase(pos, curve); } } void SetMode(EraserMode mode) { if (mode == EraserMode::Normal && dynamic_cast(m_strategy.get()) == nullptr) { m_strategy = std::make_unique(); } else if (mode == EraserMode::Nodes && dynamic_cast(m_strategy.get()) == nullptr) { m_strategy = std::make_unique(); } else if (mode == EraserMode::Segments && dynamic_cast(m_strategy.get()) == nullptr) { m_strategy = std::make_unique(); } m_mode = mode; } EraserMode GetMode() const { return m_mode; } CPositionList m_plDel; CPositionList m_plAdd; private: EraserMode m_mode; CXy* m_pXy = nullptr; std::unique_ptr m_strategy; };