#include "Stdafx.h" #include "SigmaView.h" #include "ItemCurveArc.h" #include "ItemCurveEdit.h" #include "ItemSelect.h" #include "ItemCurveProcess.h" #include "ItemCurveEditDeleteMulNodes.h" #include "ActionAddItem.h" #include "ActionListItem.h" #include "ActionBackupItem.h" #include "BufferAgency.h" #include "ItemLinkCurve.h" #include "TinyXml/tinyxml.h" #include "PolygonCombiner.h" #include "BoundaryDetection.h" #include "GapDetection.h" #include "OverlapDetection.h" #include "ActionComboItem.h" #include "ActionDeleteLayerItem.h" #include "Legend.h" #include "PolygonSnapper.h" #include "ActionComboItem.h" #pragma pack(1) struct CurveNode { double x; double y; double z; double len; double realLen; double angle; }; #pragma pack() wchar_t * AsciiToUnicodeChar(const char * str); static CItemCurve * GetItemCurveFromView(CSigmaView * pView); static CItemCurveMerge * GetMergeCurveFromView(CSigmaView * pView); static CItemCurveEdit * GetItemCurveEdit(CSigmaView * pView); static CCurveEx * GetCurveEx(CSigmaView * pView); static double _GetAngle(double x1, double y1, double x2, double y2); static bool getNodeFromCurve(CCurveEx* pcIn, CurveNode * nodeOut, int nodeIndex); static CItemCurveEditDeleteMulNodes * GetEditDeleteMulNodesFromView(CSigmaView * pView); static std::vector ConverToVector(CXy* pXy, CPositionList& positions); static std::optional GetBoundsCenter(const CCurveEx* pCurve); static POSITION AddTextMarker(CXy* pXy, CLayer* pLayer, double centerX, double centerY, const char* text); static std::unique_ptr CloseCurve(CSigmaView* pView, const std::vector& layers); static std::unique_ptr ResolveSnap(CSigmaView* pView, const std::vector& layers, double gap, double degrees, double snapRadius, double maxExtension); extern "C" __declspec(dllexport) int AutoCloseCurve(CSigmaView * pView) { CItemCurveMerge * pItem = GetMergeCurveFromView(pView); if (pItem == NULL) return -1; pItem->AutoClose(); return 1; } extern "C" __declspec(dllexport) int EndCurve(CSigmaView * pView) { CItemCurveMerge * pItem = GetMergeCurveFromView(pView); if (pItem == NULL) return -1; pItem->EndCurve(); return 1; } extern "C" __declspec(dllexport) int DrawNextCurve(CSigmaView * pView) { CItemCurveMerge * pItem = GetMergeCurveFromView(pView); if (pItem == NULL) return -1; pItem->NextCurve(); return 1; } extern "C" __declspec(dllexport) int ChangeCurveTypeForDrawing(CSigmaView * pView, LPCTSTR curveType, HDC hdc) { //CItemCurveMerge * pItem = GetMergeCurveFromView(pView); CItemCurve* pItem = GetItemCurveFromView(pView); if (pItem == NULL) return -1; CDC * pDC = CDC::FromHandle(hdc); if (_strcmpi("line", curveType) == 0) { pItem->SetCurveState(CURVE_STATE_LINE, pDC); } else if (_strcmpi("arc", curveType) == 0) { pItem->SetCurveState(CURVE_STATE_ARC, pDC); } else if (_strcmpi("spline", curveType) == 0) { pItem->SetCurveState(CURVE_STATE_SPLINE, pDC); } else if (_strcmpi("RightAngle", curveType) == 0) { pItem->SetCurveState(CURVE_STATE_ANGLE, pDC); } else if (_strcmpi("DragSegment", curveType) == 0) { pItem->SetCurveState(CURVE_STATE_DRAG_SEGMENT, pDC); } else if (_strcmpi("merage", curveType) == 0) { pItem->SetCurveState(CURVE_STATE_MERGE, pDC); } else if (_strcmpi("drawing", curveType) == 0) { pItem->SetCurveState(CURVE_STATE_DRAWING, pDC); } else { return -1; } CItemCurveEditDeleteMulNodes* itemEdit = GetEditDeleteMulNodesFromView(pView); if (itemEdit != nullptr) { itemEdit->GetMarkCurve(); } return 1; } extern "C" __declspec(dllexport) int CancelCurve(CSigmaView * pView) { CItemCurveMerge * pItem = GetMergeCurveFromView(pView); if (pItem == NULL) return -1; pItem->OnCancel(); return 1; } extern "C" __declspec(dllexport) int CurveCancelAll(CSigmaView * pView) { //CItemCurveMerge * pItem = GetMergeCurveFromView(pView); //CItemCurveEditDeleteMulNodes * pItem = GetEditDeleteMulNodesFromView(pView); //CItemCurveEdit * pItem = GetItemCurveEdit(pView); CItem * pItem = pView->m_pDoc->GetItem(); if (pItem == NULL) return -1; CItemCurveEdit * pItemSelect = dynamic_cast(pItem); if (pItemSelect == NULL) return -1; pItemSelect->CancelAll(); return 1; } extern "C" __declspec(dllexport) int ChangeCurveMergeState(CSigmaView * pView) { CItemCurveMerge * pItem = GetMergeCurveFromView(pView); if (pItem == NULL) return -1; pItem->ChangeMergeState(); return 1; } //获得选中曲线的坐标数量 extern "C" __declspec(dllexport) int CurveSelected_GetCountOfCoordinate(CSigmaView * pView) { CCurveEx * curve = GetCurveEx(pView); if (curve == NULL) return -1; return curve->num; } extern "C" __declspec(dllexport) bool GetXy_ProjectionIsEmpty(CSigmaView* pView) { CXy* xy = pView->m_pDoc->m_pXy; if (xy == NULL) return true; if (xy->GetProjection().IsEmpty()) { return true; } else { return false; } } extern "C" __declspec(dllexport) void CurveXY_EXChangeLatLong(CSigmaView * pView, double &x, double &y) { CXy* xy = pView->m_pDoc->m_pXy; if (xy == NULL) return; CProjection proj = xy->m_ExchangeXYZ; CExchangeXYZ exyz; exyz.SetProjection(&proj); exyz.ToBL_D(x, y); } extern "C" __declspec(dllexport) int CurveSelected_GetCurveNode(CSigmaView * pView, int nodeIndex, CurveNode * nodeOut) { if (nodeOut == NULL) return -1; CCurveEx* pc = GetCurveEx(pView); if (pc == NULL) return -1; if (!getNodeFromCurve(pc, nodeOut, nodeIndex)) return -1; return 1; } extern "C" __declspec(dllexport) int CurveSelected_SaveNodeToCSVFile(CSigmaView * pView, LPCTSTR fileFullPath) { CItemCurveEdit * pItemCurvEdit = GetItemCurveEdit(pView); if (pItemCurvEdit == NULL) return -1; CCurveEx* pc = (CCurveEx*)pView->m_pDoc->GetDraw()->GetAtValue(pItemCurvEdit->GetPos()); if (pc == NULL) return -1; FILE* fw = NULL; fw = fopen(fileFullPath, "wt"); if (fw == NULL) return -1; //保存文件头 if (fputs("X, Y, Z, L, 实际长度, 角度\n", fw) == EOF) goto FAIL; CurveNode node; for (int i = 0; i < pc->num; i++) { memset(&node, 0, sizeof(CurveNode)); if (!getNodeFromCurve(pc, &node, i)) goto FAIL; fprintf(fw, "%.6f,%.6f,%.6f,%.6f,%.6f,%.6f\n", node.x, node.y, node.z, node.len, node.realLen, node.angle); } fclose(fw); return 1; FAIL: fclose(fw); return -1; } extern "C" __declspec(dllexport) int CurveEditNode_Draw(CSigmaView* pView, HDC hdc) { CItemCurveEditDeleteMulNodes * pItem = GetEditDeleteMulNodesFromView(pView); if (pItem == NULL) return -1; CDC *pDC = CDC::FromHandle(hdc); pView->SetScreenDC(pDC); pItem->Draw(pDC); return 1; } extern "C" __declspec(dllexport) void CurveEditNode_EnableMulDelete(CSigmaView* pView, bool enable) { CItemCurveEditDeleteMulNodes * pItem = GetEditDeleteMulNodesFromView(pView); if (pItem == NULL) return; pItem->EnableMulDelete(enable); } extern "C" __declspec(dllexport) int CurveEditNode_EreaseHandles(CSigmaView* pView, HDC hdc) { CItemCurveEditDeleteMulNodes * pItem = GetEditDeleteMulNodesFromView(pView); if (pItem == NULL) return -1; CDC *pDC = CDC::FromHandle(hdc); pView->SetScreenDC(pDC); pItem->EreaseHandles(pDC); return 1; } extern "C" __declspec(dllexport) int CurveEditNode_GetNodeSelected(CSigmaView* pView, int mouseX, int mouseY) { CItemCurveEditDeleteMulNodes * pItem = GetEditDeleteMulNodesFromView(pView); if (pItem == NULL) return -1; CPoint pt(mouseX, mouseY); return pItem->HitTestHandle(pt); } extern "C" __declspec(dllexport) //返回添加的节点的索引 出错返回-1 int CurveEditNode_AddNode(CSigmaView* pView, int mouseX, int mouseY) { CItemCurveEditDeleteMulNodes * pItem = GetEditDeleteMulNodesFromView(pView); if (pItem == NULL) return -1; CPoint pt(mouseX, mouseY); return pItem->AddHandle(pt); } extern "C" __declspec(dllexport) //返回添加的节点的索引 出错返回-1 int CurveEditNode_DeleteNode(CSigmaView* pView, int mouseX, int mouseY) { CItemCurveEditDeleteMulNodes * pItem = GetEditDeleteMulNodesFromView(pView); if (pItem == NULL) return -1; if (pItem->GetHandleIndex() < 0) return -1; pItem->DeleteHandle(pItem->GetHandleIndex()); return 1; } extern "C" __declspec(dllexport) //-1出错 1 默认光标 2 添加光标 3 移动光标 int CurveEditNode_GetCursorTpye(CSigmaView* pView, int mouseX, int mouseY) { CItemCurveEditDeleteMulNodes * pItem = GetEditDeleteMulNodesFromView(pView); if (pItem == NULL) return -1; CPoint pt(mouseX, mouseY); int index = pItem->HitTestHandle(pt); if (index < 0) { if (pItem->IsCanAddHandle(pt))//当移动到曲线上时设置为增加点光标 { return 2; } return 1; } return 3; } extern "C" __declspec(dllexport) int CurveEditNode_GetCountOfNode(CSigmaView * pView) { CItemCurveEditDeleteMulNodes * pItem = GetEditDeleteMulNodesFromView(pView); if (pItem == NULL) return -1; return pItem->GetNumberOfNode(); } // 区域连通 extern "C" __declspec(dllexport) int DrawerCurveConnect(CSigmaView* pView, LPCTSTR layerName, bool deleteOriginalCurves) { CItemSelect * itemSelect = pView->m_pDoc->GetSelectItem(); if (itemSelect == 0) return 0; CItemCurveProcess curveProcess(pView->m_pDoc); CString strLayerName(layerName); return curveProcess.PolygonConnect(itemSelect->m_selection, strLayerName, deleteOriginalCurves); } //加密曲线节点 extern "C" __declspec(dllexport) int Curve_IncreaseDensityNode(CSigmaView * pView, int mode, double step, int rounding) { if (mode != 0 && mode != 1) return -1; if (rounding != 0 && rounding != 1) return -1; if (step <= 0) return -1; if (pView == 0) return -1; if (pView->m_pDoc == 0) return -1; //DrawTooolCurveDensityNode中 将item的类型设置为ITEM_SELECT 在调用此函数时 还未切换Item 所以当前的Item还是CItemSelect CItemSelect * itemSelect = pView->m_pDoc->GetSelectItem(); if (itemSelect == 0) return -1; CItemCurveProcess curveProcess(pView->m_pDoc); if (curveProcess.NodeEncrypt(mode, step, rounding == 1, itemSelect->m_selection)) return 1; return -1; } //去掉曲线上冗余的节点 extern "C" __declspec(dllexport) int Curve_Redundance(CSigmaView * pView, double tolerance) { if (tolerance < 0) return -1; if (pView == 0) return -1; if (pView->m_pDoc == 0) return -1; //DrawTooolCurveDensityNode中 将item的类型设置为ITEM_SELECT 在调用此函数时 还未切换Item 所以当前的Item还是CItemSelect CItemSelect * itemSelect = pView->m_pDoc->GetSelectItem(); if (itemSelect == 0) return -1; CItemCurveProcess curveProcess(pView->m_pDoc); if (curveProcess.ToRedundant(tolerance, itemSelect->m_selection)) return 1; return -1; } extern "C" __declspec(dllexport) int Curve_ArcToCurve(CSigmaView * pView, double step) { CItemSelect * itemSelect = pView->m_pDoc->GetSelectItem(); if (itemSelect == 0) return -1; CItemCurveProcess curveProcess(pView->m_pDoc); int num = curveProcess.ArcToCurve(itemSelect->m_selection, step); if (num <= 0) { return -1; } return 1; } extern "C" __declspec(dllexport) int Curve_Smooth(CSigmaView * pView, int mode, double step) { if (mode != 0 && mode != 1) return -1; if (pView == 0) return -1; if (pView->m_pDoc == 0) return -1; CItemSelect * itemSelect = pView->m_pDoc->GetSelectItem(); if (itemSelect == 0) return -1; CItemCurveProcess curveProcess(pView->m_pDoc); if (curveProcess.SmoothCurve(mode, step, itemSelect->m_selection)) return 1; return -1; } /// 自动连接曲线 extern "C" __declspec(dllexport) int Curve_AutoJoin(CSigmaView * pView, double maxError, bool onlySameName) { CItemCurveProcess itemCurveProcess(pView->m_pDoc); return itemCurveProcess.AutoLinkCurve(maxError, onlySameName); } extern "C" __declspec(dllexport) int Curve_SetName(CSigmaView * pView, LPCTSTR curveName) { if (pView == 0) return -1; if (pView->m_pDoc == 0) return -1; //DrawTooolCurveDensityNode中 将item的类型设置为ITEM_SELECT 在调用此函数时 当前的Item还是CItemSelect CItemSelect * itemSelect = pView->m_pDoc->GetLastSelectItem(); if (itemSelect == 0) return -1; CItemCurveProcess curveProcess(pView->m_pDoc); curveProcess.SetCurveName(curveName, itemSelect->m_selection); return 1; } extern "C" __declspec(dllexport) int Curve_GetCurvesOfEmptyName(CSigmaView * pView, BufferAgency * bufferOut) { bufferOut->buffer = nullptr; bufferOut->len = 0; if (pView == 0) return -1; if (pView->m_pDoc == 0) return -1; CItemSelect * itemSelect = pView->m_pDoc->GetSelectItem(); if (itemSelect == 0) return -1; CItemCurveProcess curveProcess(pView->m_pDoc); std::string curvesStr = curveProcess.FindNameIsNull(itemSelect->m_selection); if (curvesStr.empty()) return -1; bufferOut->buffer = AsciiToUnicodeChar(curvesStr.c_str()); bufferOut->len = (int)wcslen(bufferOut->buffer); return 1; } /** * 获取角度和距离 * * \param pView * \return 如果失败,返回空字符串,否则返回由 "," 分割的字符串,第一个是角度,第二个距离 */ extern "C" __declspec(dllexport) bool GetCurveAngleAndDistance(CSigmaView* pView, double *angle, double *distance) { if (pView == 0) return false; if (pView->m_pDoc == 0) return false; CItem * pItem = pView->GetItem(); if (pItem == NULL) return false; if (CItemCurve* pItemCurve = dynamic_cast(pItem)) { return pItemCurve->GetAngleAndDistance(*angle, *distance); } return false; } static std::vector FilterCurves(CXy* pXy, const CString& layers) { assert(pXy != nullptr); CXyElementFilter filter; filter.addType(DOUBLEFOX_CURVE); std::vector vec = SplitString(layers, _T(",")); for (const auto& layer : vec) { filter.addLayer(layer, false); } CPositionList select; pXy->GetElement(filter, select); return ConverToVector(pXy, select); } /** * 完善的删除和恢复太复杂,这里实现的,只供下面几个检测功能使用,其它地方使用可能出现意想不到的问题 * * \param pView * \param pItem * \param layerName */ static void ClearLayer(CSigmaView* pView, CActionComboItem& pItem, const CString& layerName) { CXy* pXy = pView->m_pDoc->m_pXy; CLayer* pLayer = pXy->FindLayer(layerName); if (pLayer == nullptr) { return; } CPtrList layers; layers.AddTail(pLayer); NBase::CPositionList select; int n = pXy->GetElement(layerName, select, FALSE, FALSE); pItem.AddAction(new CActionLayerDeleteItem(pView->m_pDoc, select, layers)); CLayer* pCurrentLayer = pXy->GetCurrentLayer(); if (layers.Find(pCurrentLayer)) { if (pXy->GetClassList()->GetLayerCount() == 0) { if (pXy->GetClassList()->GetCount() == 0) pXy->InitLayerClass(); else pXy->GetClassList()->GetHead()->FindAdd("0"); } pXy->SetCurrentLayer(pXy->GetClassList()->GetHead()->GetHead()); } } /** * 曲线未闭合检测 * * \param pView * \param layers 需要检测的图层 */ extern "C" __declspec(dllexport) void MineralRightsUnclosedDetection(CSigmaView* pView, const LPCTSTR layers) { if (pView == nullptr || pView->m_pDoc == nullptr || pView->m_pDoc->m_pXy == nullptr) { TRACE("pView 不能为 nullptr\n"); return; } if (pView->m_pDoc == nullptr || pView->m_pDoc->m_pXy == nullptr) { TRACE("图件异常\n"); return; } CXy* pXy = pView->m_pDoc->m_pXy; std::unique_ptr pItem = std::make_unique(pView->m_pDoc, 0); const CString layerName = _T("未闭合"); ClearLayer(pView, *pItem, layerName); std::vector curves = FilterCurves(pXy, layers); BoundaryDetection boundaryDetection; auto closeResult = boundaryDetection.CheckClosure(curves); auto* pUnclosedLayer = pXy->FindAddLayer(layerName); CPositionList positions; for (auto& pair : closeResult) { if (!pair.isClosed) { COne* pOne = new COne(); double width = 10.0; double height = 26.0; CCurveEx* pCurve = pair.pCurve; double startX = pCurve->x[0]; double startY = pCurve->y[0]; int lastIdx = pCurve->num - 1; double endX = pCurve->x[lastIdx]; double endY = pCurve->y[lastIdx]; double midX = (startX + endX) / 2; double midY = (startY + endY) / 2; double textX = midX - (width / 2); // 调整 X 位置 double textY = midY + (height / 2); // 调整 Y 位置 POSITION pos = AddTextMarker(pXy, pUnclosedLayer, midX, midY, "-|-"); positions.AddTail(pos); } } if (positions.GetCount() > 0) { pItem->AddAction(new CActionAddItem(pView->m_pDoc, IDS_STRING_ACTION_ADD, positions)); } if (pItem->GetCount() > 0) { pView->m_pDoc->SetActionItem(pItem.release()); } } /** * 曲线相交检测 * * \param pView * \param layers 需要检测的图层 */ extern "C" __declspec(dllexport) void MineralRightsOverlapDetection(CSigmaView* pView, const LPCTSTR layers) { if (pView == nullptr || pView->m_pDoc == nullptr || pView->m_pDoc->m_pXy == nullptr) { TRACE("pView 不能为 nullptr\n"); return; } if (pView->m_pDoc == nullptr || pView->m_pDoc->m_pXy == nullptr) { TRACE("图件异常\n"); return; } CXy* pXy = pView->m_pDoc->m_pXy; std::unique_ptr pItem = std::make_unique(pView->m_pDoc, 0); const CString layerName = _T("相交"); ClearLayer(pView, *pItem, layerName); std::vector curves = FilterCurves(pXy, layers); OverlapDetection overlapDetection; auto overlaps = overlapDetection.DetectOverlap(curves); auto* pOverlapLayer = pXy->FindAddLayer(layerName); CPositionList positions; for (auto&& overlap : overlaps) { auto centerPoint = GetBoundsCenter(overlap.pArea.get()); if (centerPoint) { POSITION pos = AddTextMarker(pXy, pOverlapLayer, centerPoint->x0, centerPoint->y0, "→|←"); positions.AddTail(pos); } } if (positions.GetCount() > 0) { pItem->AddAction(new CActionAddItem(pView->m_pDoc, IDS_STRING_ACTION_ADD, positions)); } if (pItem->GetCount() > 0) { pView->m_pDoc->SetActionItem(pItem.release()); } } /** * 曲线缝隙检测 * * \param pView * \param layers 要检测的图层 */ extern "C" __declspec(dllexport) void MineralRightsGapDetection(CSigmaView* pView, const LPCTSTR layers, double gap) { if (pView == nullptr || pView->m_pDoc == nullptr || pView->m_pDoc->m_pXy == nullptr) { TRACE("pView 不能为 nullptr\n"); return; } if (pView->m_pDoc == nullptr || pView->m_pDoc->m_pXy == nullptr) { TRACE("图件异常\n"); return; } CXy* pXy = pView->m_pDoc->m_pXy; std::unique_ptr pItem = std::make_unique(pView->m_pDoc, 0); const CString layerName = _T("缝隙"); ClearLayer(pView, *pItem, layerName); std::vector curves = FilterCurves(pXy, layers); GapDetection gapDetection; gapDetection.SetGapValue(gap); auto gaps = gapDetection.DetectGaps(curves); auto* pGapLayer = pXy->FindAddLayer(layerName); CPositionList positions; for (auto&& gap : gaps) { auto centerPoint = GetBoundsCenter(gap.pArea.get()); if (centerPoint) { POSITION pos = AddTextMarker(pXy, pGapLayer, centerPoint->x0, centerPoint->y0, "←|→"); positions.AddTail(pos); } } if (positions.GetCount() > 0) { pItem->AddAction(new CActionAddItem(pView->m_pDoc, IDS_STRING_ACTION_ADD, positions)); } if (pItem->GetCount() > 0) { pView->m_pDoc->SetActionItem(pItem.release()); } } /** * 矿权检测问题区域自动修复(未闭合、 * * \param pView * \param layers */ extern "C" __declspec(dllexport) void MineralRightsAutoFix(CSigmaView* pView, const LPCTSTR layers, double gap, double degress, double snapRadius, double maxExtension) { CString layersStr(layers); std::vector layersVec = SplitString(layersStr, _T(",")); std::unique_ptr pItem1 = CloseCurve(pView, layersVec); std::unique_ptr pItem2 = ResolveSnap(pView, layersVec, gap, degress, snapRadius, maxExtension); std::unique_ptr pItem = std::make_unique(); if (pItem1->GetCount() > 0) { pItem->AddAction(pItem1.release()); } if (pItem2->GetCount() > 0) { pItem->AddAction(pItem2.release()); } if (pItem->GetCount() > 0) { pView->m_pDoc->SetActionItem(pItem.release()); } } static void GetClosedCurves(CXy& xy, CPositionList& selection, CPositionList& closedCurves) { POSITION pos = selection.GetHeadPosition(); while (pos != nullptr) { POSITION prev = pos; POSITION pt = selection.GetNext(pos); COne* pOne = xy.GetAt(pt); if (pOne != nullptr && pOne->GetType() == DOUBLEFOX_CURVE) { CCurveEx* pCurve = pOne->GetValueSafe(); if (pCurve->IsClosed()) { closedCurves.AddTail(pt); } } } } static void GroupCurves(CXy& xy, CPositionList& curves, std::vector& left, std::vector& right) { int half = curves.GetCount() / 2; POSITION pos = curves.GetHeadPosition(); while (pos != nullptr) { POSITION pt = curves.GetNext(pos); COne* pOne = xy.GetAt(pt); assert(pOne != nullptr); CCurveEx* pCurve = pOne->GetValueSafe(); if (left.size() < half) { left.push_back(pCurve); } else { right.push_back(pCurve); } } } /** * 多边形运算 * * \param pView pView * \param booleanOp 合并方式 * 0: // Difference * 1: // Intersect * 2: // Union * 3: // XOR * \return */ extern "C" __declspec(dllexport) bool PolygonBooleanOperation(CSigmaView* pView, int booleanOp) { if (pView == nullptr) { TRACE("pView 不能为 nullptr\n"); return false; } if (booleanOp < 0 || booleanOp > 3) { TRACE("booleanOp 必须在 [0, 3] 之间\n"); return false; } auto* pItem = pView->GetItem(); if (pItem == nullptr) { return false; } CItemSelect * pItemSelect = dynamic_cast(pItem); if (pItemSelect == nullptr) { TRACE("必须先选中曲线\n"); return false; } CXy* pXy = pView->m_pDoc->m_pXy; CPositionList& selection = pItemSelect->m_selection; if (selection.GetCount() < 2) { return false; } CPositionList closedCurves; GetClosedCurves(*pXy, selection, closedCurves); if (closedCurves.GetCount() < 2) { return true; } // 从选中列表中移除闭合曲线,这些曲线即将从图件中移除 CListRemoveRange(selection, closedCurves); std::vector left; std::vector right; GroupCurves(*pXy, closedCurves, left, right); CPositionList plAdd; BooleanOp op = static_cast(booleanOp); auto newCurves = PolygonCombiner::Execute(left, right, op); for (auto& curve : newCurves) { POSITION pos = pXy->AddElement(curve.release(), DOUBLEFOX_CURVE); plAdd.AddTail(pos); } CPositionList plDel; CListAddRange(plDel, closedCurves); auto pAction = std::make_unique(pView->m_pDoc, 0); pAction->AddDeleteItem(plDel); pAction->AddAddItem(plAdd); pView->m_pDoc->SetActionItem(pAction.release()); pView->Notify("redraw"); return true; } /** * 设置连接模式为合并模式 * * \param mergeIdea 合并方式 * 0: //And * 1: //Or * 2: //Xor * 3: //Cross * \param isDelete 是否删除原来的线 * \return 如果当前不是 CItemLinkCurve 或者参数错误,返回 false */ extern "C" __declspec(dllexport) bool SetLinkCurveMergeMode(CSigmaView * pView, int mergeIdea, bool isDelete) { if (pView == nullptr) { return false; } if (mergeIdea < 0 || mergeIdea > 4) { return false; } if (auto* pLinkCurve = dynamic_cast(pView->GetItem())) { pLinkCurve->m_nMode = LINK_CURVE_MERGE; pLinkCurve->SetMergeIdea(mergeIdea); pLinkCurve->SetDelete(isDelete); } return true; } /** * 曲线连接模式 */ enum class LinkCurveMode { And, Or, Xor, Cross, }; static std::unique_ptr CCurveFromXml(const std::string& xml) { TiXmlDocument doc; doc.Parse(xml.c_str()); if (doc.Error()) { throw std::runtime_error("不是合法的 xml"); } const char* Pline = "Pline"; const char* Point = "Point"; const char* X = "X"; const char* Y = "Y"; const char* Z = "Z"; TiXmlElement* plineElement = doc.FirstChildElement(Pline); if (plineElement == nullptr) { throw std::runtime_error("xml 中没有数据"); } std::vector points; for (TiXmlElement* pointElement = plineElement->FirstChildElement(Point); pointElement != nullptr; pointElement = pointElement->NextSiblingElement(Point)) { TiXmlElement* xElement = pointElement->FirstChildElement(X); TiXmlElement* yElement = pointElement->FirstChildElement(Y); TiXmlElement* zElement = pointElement->FirstChildElement(Z); if (xElement == nullptr || yElement == nullptr || zElement == nullptr) { throw std::runtime_error("点数据元素缺少 x y z 中的元素"); } const char* xText = xElement->GetText(); const char* yText = yElement->GetText(); const char* zText = zElement->GetText(); if (xText == nullptr || yText == nullptr || zText == nullptr) { throw std::runtime_error("点元素中 x y z 中有的没有内容"); } try { points.emplace_back(std::stod(xText), std::stod(yText), std::stod(zText)); } catch (const std::invalid_argument&) { throw std::runtime_error("点数据格式不是合法的数字"); } } auto pCurve = std::make_unique(); pCurve->Create(points.size()); for (size_t i = 0; i < points.size(); i++) { pCurve->x[i] = points[i].x0; pCurve->y[i] = points[i].y0; pCurve->z[i] = points[i].z0; TRACE("%lf, %lf, %lf\n", pCurve->x[i], pCurve->y[i], pCurve->x[i]); } return pCurve; } static std::string CCurvesToXml(const std::vector>& curves, const std::string& message) { TiXmlDocument doc; std::unique_ptr root = std::make_unique("Polyline"); std::unique_ptr messageElement = std::make_unique("ErrorMessage"); messageElement->LinkEndChild(new TiXmlText(message.c_str())); root->LinkEndChild(messageElement.release()); const char* Pline = "Pline"; const char* Point = "Point"; const char* X = "X"; const char* Y = "Y"; const char* Z = "Z"; for (const auto& curve : curves) { std::unique_ptr plineElement = std::make_unique(Pline); for (int i = 0; i < curve->num; i++) { std::unique_ptr pointElement = std::make_unique(Point); std::unique_ptr xElement = std::make_unique(X); xElement->LinkEndChild(new TiXmlText(std::to_string(curve->x[i]).c_str())); pointElement->LinkEndChild(xElement.release()); std::unique_ptr yElement = std::make_unique(Y); yElement->LinkEndChild(new TiXmlText(std::to_string(curve->y[i]).c_str())); pointElement->LinkEndChild(yElement.release()); std::unique_ptr zElement = std::make_unique(Z); zElement->LinkEndChild(new TiXmlText(std::to_string(curve->z[i]).c_str())); pointElement->LinkEndChild(zElement.release()); plineElement->LinkEndChild(pointElement.release()); } root->LinkEndChild(plineElement.release()); } doc.LinkEndChild(root.release()); TiXmlPrinter printer; doc.Accept(&printer); return std::string(printer.CStr()); } static std::vector> MergeCurveImpl(const CCurveEx& curve1, const CCurveEx& curve2, LinkCurveMode mode) { CCurveAnd curveAnd; curveAnd.SetCurve(const_cast(&curve1), const_cast(&curve2)); switch (mode) { case LinkCurveMode::And: curveAnd.GetAndCurve(); break; case LinkCurveMode::Or: curveAnd.GetOrCurve(); break; case LinkCurveMode::Xor: curveAnd.GetXorCurve(); break; case LinkCurveMode::Cross: curveAnd.GetCrossCurve(); break; } std::vector> result; for (POSITION pos = curveAnd.m_ptrCurveList.GetHeadPosition(); pos != nullptr; curveAnd.m_ptrCurveList.GetNext(pos)) { CCurveEx* pCurve = reinterpret_cast(curveAnd.m_ptrCurveList.GetAt(pos)); result.emplace_back(pCurve); } return result; } static LinkCurveMode LinkCurveModeFromString(const std::string& modeString) { static std::map map { { "And", LinkCurveMode::And }, { "Or", LinkCurveMode::Or }, { "Xor", LinkCurveMode::Xor }, { "Cross", LinkCurveMode::Cross }, }; if (map.find(modeString) != map.end()) { return map.at(modeString); } throw std::runtime_error("error mode: " + modeString + ", only support and or xor cross"); } static BSTR ConvertMBSToBSTR(const std::string& str) { int wslen = ::MultiByteToWideChar(CP_ACP, 0 /* no flags */, str.data(), str.length(), nullptr, 0); BSTR wsdata = ::SysAllocStringLen(nullptr, wslen); ::MultiByteToWideChar(CP_ACP, 0 /* no flags */, str.data(), str.length(), wsdata, wslen); return wsdata; } /** * 曲线合并接口 * * \param curve1 曲线1 * \param curve2 曲线2 * \param mode 曲线合并模式 And Or Xor Cross * \return */ extern "C" __declspec(dllexport) BSTR MergeCurve(wchar_t* curve1, wchar_t* curve2, wchar_t* mode) { std::string curve1String = WStringToString(curve1); std::string curve2String = WStringToString(curve2); std::string modeString = WStringToString(mode); try { LinkCurveMode linkMode = LinkCurveModeFromString(modeString); std::unique_ptr curve1 = CCurveFromXml(curve1String); std::unique_ptr curve2 = CCurveFromXml(curve2String); std::vector> curves = MergeCurveImpl(*curve1, *curve2, linkMode); std::string result = CCurvesToXml(curves, ""); return ConvertMBSToBSTR(result); } catch (std::runtime_error& e) { std::string errorMessage = CCurvesToXml({}, e.what()); return ConvertMBSToBSTR(errorMessage); } } static CItemCurveEditDeleteMulNodes * GetEditDeleteMulNodesFromView(CSigmaView * pView) { if (pView == NULL) return 0; CItem * pItem = pView->GetItem(); if (pItem == NULL) return 0; CItemCurveEditDeleteMulNodes * itemPoint = dynamic_cast(pItem); if (itemPoint == NULL) return 0; return itemPoint; } static CItemCurveMerge * GetMergeCurveFromView(CSigmaView * pView) { if (pView == NULL) return 0; CItem * pItem = pView->GetItem(); if (pItem == NULL) return 0; CItemCurveMerge * itemPoint = dynamic_cast(pItem); if (itemPoint == NULL) return 0; return itemPoint; } static CItemCurve * GetItemCurveFromView(CSigmaView * pView) { if (pView == NULL) return 0; CItem * pItem = pView->GetItem(); if (pItem == NULL) return 0; CItemCurve * itemPoint = dynamic_cast(pItem); if (itemPoint == NULL) return 0; return itemPoint; } static CItemCurveEdit * GetItemCurveEdit(CSigmaView * pView) { if (pView == NULL) return NULL; if (pView->m_pDoc == NULL) return NULL; CItem * pItem = pView->m_pDoc->GetItem(); if (pItem == NULL) return NULL; CItemSelect * pItemSelect = dynamic_cast(pItem); if (pItemSelect == NULL) return NULL; pItemSelect->InitItemForGrid(); CItem * pItemGraph = pItemSelect->GetItemForGrid(); if (pItemGraph == NULL) return NULL; //CItemCurveEdit* pItemCurve = dynamic_cast(pItemGraph); CItemCurveEdit* pItemCurve = (CItemCurveEdit *)(pItemGraph); return pItemCurve; } static CCurveEx * GetCurveEx(CSigmaView * pView) { if (pView == NULL) return NULL; if (pView->m_pDoc == NULL) return NULL; CItem * pItem = pView->m_pDoc->GetItem(); if (pItem == NULL) return NULL; CItemSelect * pItemSelect = dynamic_cast(pItem); if (pItemSelect == NULL) return NULL; if (pItemSelect->m_selection.IsEmpty()) return NULL; POSITION pos = pItemSelect->m_selection.GetHead(); CXy * xy = pView->m_pDoc->m_pXy; if (xy == NULL) return NULL; COne * pOne = xy->GetAt(pos); if (pOne == NULL) return NULL; if (pOne->GetType() != DOUBLEFOX_CURVE) return NULL; //CItemCurveEdit* pItemCurve = dynamic_cast(pItemGraph); CCurveEx * curve = (CCurveEx *)(xy->GetAtValue(pos)); return curve; } /* CItemSelect * pItemSelect = dynamic_cast(pItem); if (pItemSelect == NULL) return NULL; if (pItemSelect->m_selection.GetSize() == 0) return NULL; POSITION pos = pItemSelect->m_selection.GetHead(); CXy * xy = pView->m_pDoc->m_pXy; if (xy == 0) return NULL; COne * pOne = xy->GetAt(pos); pOne->GetType(); */ static bool getNodeFromCurve(CCurveEx* pcIn, CurveNode * nodeOut, int nodeIndex) { if (pcIn == NULL || nodeOut == NULL) return false; if (nodeIndex < 0 || nodeIndex >= pcIn->num) return false; nodeOut->x = pcIn->x[nodeIndex]; nodeOut->y = pcIn->y[nodeIndex]; nodeOut->z = pcIn->z[nodeIndex]; nodeOut->len = pcIn->l[nodeIndex]; if (nodeIndex == 0) nodeOut->realLen = 0; else nodeOut->realLen = AfxGetPublicFunction()->Distance(pcIn->x[nodeIndex - 1], pcIn->y[nodeIndex - 1], pcIn->x[nodeIndex], pcIn->y[nodeIndex]); if (nodeIndex == 0) nodeOut->angle = 0; else nodeOut->angle = ::_GetAngle(pcIn->x[nodeIndex - 1], pcIn->y[nodeIndex - 1], pcIn->x[nodeIndex], pcIn->y[nodeIndex]); return true; } static double _GetAngle(double x1, double y1, double x2, double y2) { double dx = x2 - x1; double dy = y2 - y1; return atan2(dy, dx)*RHO; } static std::vector ConverToVector(CXy* pXy, CPositionList& positions) { std::vector result; result.reserve(positions.GetCount()); for (POSITION pos = positions.GetHeadPosition(); pos != nullptr; positions.GetNext(pos)) { POSITION pt = positions.GetAt(pos); COne* pOne = pXy->GetAt(pt); if (pOne->GetType() == DOUBLEFOX_CURVE) { result.push_back(pOne->GetValueSafe()); } } return result; } static std::optional GetBoundsCenter(const CCurveEx* pCurve) { if (pCurve == nullptr || pCurve->num < 2) { return std::nullopt; } auto xRange = std::minmax_element(pCurve->x, pCurve->x + pCurve->num); auto yRange = std::minmax_element(pCurve->y, pCurve->y + pCurve->num); double centerX = (*xRange.first + *xRange.second) / 2.0; double centerY = (*yRange.first + *yRange.second) / 2.0; return CPoint2D { centerX, centerY }; } static POSITION AddTextMarker(CXy* pXy, CLayer* pLayer, double centerX, double centerY, const char* text) { // 保持原有逻辑中的尺寸设置 double width = 10.0; double height = 26.0; // 计算左上角位置 (原有逻辑) double textX = centerX - (width / 2.0); double textY = centerY + (height / 2.0); std::unique_ptr pText = CTextBuilder() .FontLocation(textX, textY) .FontSize(width, height) .Text(text) .Build(); COne* pOne = new COne(); pOne->SetValueSafe(pText.release()); pOne->SetLayer(pLayer); return pXy->AddTailOne(pOne); } /** * 闭合曲线 * * \param pView * \param layers */ static std::unique_ptr CloseCurve(CSigmaView* pView, const std::vector& layers) { CXy* pXy = pView->m_pDoc->m_pXy; CXyElementFilter filter; for (const auto& layer : layers) { filter.addLayer(layer, false); } filter.addType(DOUBLEFOX_CURVE); CPositionList select; pXy->GetElement(filter, select); std::unique_ptr pItem = std::make_unique(pView->m_pDoc, 0); for (POSITION pos = select.GetHeadPosition(); pos != nullptr; select.GetNext(pos)) { POSITION pt = select.GetAt(pos); COne* pOne = pXy->GetAt(pt); CCurveEx* pCurve = pOne->GetValueSafe(); if (!pCurve->IsClosed()) { pItem->Backup(pOne); std::unique_ptr pClosedCurve(CreateCloseCurve(pCurve)); *pCurve = *pClosedCurve; } } pItem->BackupNew(); return pItem; } static std::unique_ptr ResolveSnap(CSigmaView* pView, const std::vector& layers, double gap, double degrees, double snapRadius, double maxExtension) { CXy* pXy = pView->m_pDoc->m_pXy; CXyElementFilter filter; filter.addType(DOUBLEFOX_CURVE); for (const auto& layer : layers) { filter.addLayer(layer, false); } CPositionList select; pXy->GetElement(filter, select); double delta = gap; std::vector curves; curves.reserve(select.GetCount()); std::unique_ptr pItem = std::make_unique(pView->m_pDoc, 0); for (POSITION pos = select.GetHeadPosition(); pos != nullptr; select.GetNext(pos)) { POSITION pt = select.GetAt(pos); COne* pOne = pXy->GetAt(pt); pItem->Backup(pOne); CCurveEx* pCurve = pOne->GetValueSafe(); curves.push_back(pCurve); } PolygonSnapper::Snap(curves, gap, degrees, snapRadius, maxExtension); pItem->BackupNew(); return pItem; }