#pragma once #include #include #include #include #include #include "DrawOperator/RTree.h" #include "VoronoiMap/InterfaceElements.h" /** * CList 遍历宏 * * \list CList 链表 * \pos POSITION 变量,元素的位置,要取对应的请使用 list.GetAt(pos) */ #define CListForEach(pos, list) \ for (POSITION pos = (list).GetHeadPosition(); pos != nullptr; (list).GetNext(pos)) /** * CList 反射遍历宏 * * \list CList 链表 * \pos POSITION 变量,元素的位置,要取对应的请使用 list.GetAt(pos) */ #define CListForEachReverse(pos, list) \ for (POSITION pos = (list).GetTailPosition(); pos != nullptr; (list).GetPrev(pos)) template inline CString ToCString(const T& value) { return _T(""); } template <> inline CString ToCString(const int32_t& value) { CString str; str.Format(_T("%d"), value); return str; } template <> inline CString ToCString(const uint32_t& value) { CString str; str.Format(_T("%u"), value); return str; } template <> inline CString ToCString(const long& value) { CString str; str.Format(_T("%ld"), value); return str; } template <> inline CString ToCString(const unsigned long& value) { CString str; str.Format(_T("%lu"), value); return str; } template <> inline CString ToCString(const int64_t& value) { CString str; str.Format(_T("%lld"), value); return str; } template <> inline CString ToCString(const uint64_t& value) { CString str; str.Format(_T("%llu"), value); return str; } template <> inline CString ToCString(const float& value) { CString str; str.Format(_T("%f"), value); return str; } template <> inline CString ToCString(const double& value) { CString str; str.Format(_T("%f"), value); return str; } template <> inline CString ToCString(const bool& value) { return value ? _T("true") : _T("false"); } template <> inline CString ToCString(const CString& value) { return value; } template T& GetInstance() { static T instance; return instance; } /** * CList 列表合并 * * \param source 源列表,会被修改 * \param target 被添加的元素列表 */ template void CListAddRange(CList &source, const CList &target) { for (POSITION pos = (target).GetHeadPosition(); pos != nullptr; (target).GetNext(pos)) { source.AddTail(target.GetAt(pos)); } } /** * CList 列表移除 * * 从 source 中移除所有在 target 中存在的元素 * * \param source 源列表,会被修改 * \param target 要移除的元素集合 */ template void CListRemoveRange(CList& source, const CList& target) { // 遍历源列表 POSITION pos = source.GetHeadPosition(); while (pos != nullptr) { POSITION currentPos = pos; T& element = source.GetNext(pos); if (target.Find(element) != nullptr) { source.RemoveAt(currentPos); } } } /** * 判断两条折线是否相等 * \param first * \param second * \return */ bool IsEqual(const CCurveEx *first, const CCurveEx *second); /** * 判断两条折线是否相等 * * \param first * \param second * \return */ bool IsEqual(const CPolyline &first, const CPolyline &second); /** * 判断两个点是否相等 * * \param first * \param second * \return */ bool IsEqual(const CPointXYZ &first, const CPointXYZ &second); /** * double 判等不能直接用 = */ bool IsEqual(double a, double b); /** * double 的大于判断 */ bool IsGreaterThan(double a, double b); /** * double 的小于判断 */ bool IsLessThan(double a, double b); /** * 判断曲线是不是多边形,这里先简单的判断是否首尾相接 */ bool IsPolygon(const CCurveEx * pCurve); /** * 判断是否近似于一个多边形,,注意,我们只考虑 x,y 坐标长度,不考虑 z 值 * \return 首尾未连接长度小于等于总长度的十分之一,返回 true,否则返回 false,如果是多边形也返回 true */ bool IsApproximatePolygon(const CCurveEx * pCurve); /** * 创建一个闭合曲线,首尾相连 * * \return 如果 pCurve 不足两条线,返回 nullptr, 如果是已经是闭合的曲线,返回坐标一样的新曲线,否则返回一个闭合的新曲线 * \note 这里面并没有考虑里面的线都在同一方向的情况,依然会闭合 */ CCurveEx* CreateCloseCurve(const CCurveEx * pCurve); /** * 判断某条线是否在某个集合内,这里不是判断 polyline 这个对象在没在这个集合内,而是判断它代表的那条线是否在集合内 * * \param polylines * \param polyline * \return */ bool CPolylinesContains(const std::list &polylines, const CPolyline &polyline); /** * 判断某个点是否在这个集合内 * \param pointXYZs * \param pointXYZ */ bool CPointXYZContains(const std::vector &pointXYZs, const CPointXYZ &pointXYZ); /** * 判断 x y 组成的点是否在 rect 内 * * \param x x 坐标 * \param y y 坐标 * \param rect 矩形 * \return */ inline bool IsInRect(double x, double y, const NBase::CRect8& rect) { return x >= rect.left && x <= rect.right && y >= rect.bottom && y <= rect.top; } inline bool IsInRect(const NBase::CRect8& subRect, const NBase::CRect8& rect) { return (subRect.left >= rect.left) && (subRect.right <= rect.right) && (subRect.bottom >= rect.bottom) && (subRect.top <= rect.top); } /** * 判断两个多边形是否相交 * \param */ inline bool IsIntersecting(const NBase::CRect8& rect1, const NBase::CRect8& rect2) { return rect1.right > rect2.left && rect1.left < rect2.right && rect1.top > rect2.bottom && rect1.bottom < rect2.top; } /** * 判断下标是否是曲线的起始下标 * * \param curve 曲线 * \param index 下标 * \return */ inline bool IsFirstIndex(CCurveEx& curve, int32_t index) { return curve.num > 0 && index == 0; } /** * 判断下标是否是曲线的结束下标 * * \param curve 曲线 * \param index 下标 * \return */ inline bool IsLastIndex(CCurveEx& curve, int32_t index) { return curve.num > 0 && index == curve.num - 1; } /** * @brief 判断点 (px0, py0) 是否在线段 (px1,py1)-(px2,py2) 上 * * @param px0 待测点的 x 坐标 * @param py0 待测点的 y 坐标 * @param px1 线段起点的 x 坐标 * @param py1 线段起点的 y 坐标 * @param px2 线段终点的 x 坐标 * @param py2 线段终点的 y 坐标 * @return true 如果点在线段上 * @return false 如果点不在线段上 */ inline bool IsPointOnLine(double px0, double py0, double px1, double py1, double px2, double py2) { double crossProduct = (px1 - px0) * (py2 - py0) - (px2 - px0) * (py1 - py0); return (std::abs(crossProduct) < std::numeric_limits::epsilon()) && ((px0 - px1) * (px0 - px2) <= 0) && ((py0 - py1) * (py0 - py2) <= 0); } /** * @brief 判断线段 (px1,py1)-(px2,py2) 和线段 (px3,py3)-(px4,py4) 是否相交 * * @note 本算法不处理共线段部分重叠的情况,仅检测非平行线的交点 * @param px1 第一线段起点 x 坐标 * @param py1 第一线段起点 y 坐标 * @param px2 第一线段终点 x 坐标 * @param py2 第一线段终点 y 坐标 * @param px3 第二线段起点 x 坐标 * @param py3 第二线段起点 y 坐标 * @param px4 第二线段终点 x 坐标 * @param py4 第二线段终点 y 坐标 * @return true 如果线段在非共线情况下相交 * @return false 如果线段不相交或共线 */ inline bool IsIntersect(double px1, double py1, double px2, double py2, double px3, double py3, double px4, double py4) { // 计算分母 double denominator = (px2 - px1) * (py4 - py3) - (py2 - py1) * (px4 - px3); // 平行线直接返回 if (std::abs(denominator) < std::numeric_limits::epsilon()) { return false; } // 计算参数方程系数 double r_numerator = (py1 - py3) * (px4 - px3) - (px1 - px3) * (py4 - py3); double s_numerator = (py1 - py3) * (px2 - px1) - (px1 - px3) * (py2 - py1); double r = r_numerator / denominator; double s = s_numerator / denominator; return (r >= 0 && r <= 1 && s >= 0 && s <= 1); } /** * @brief 判断点 (x, y) 是否在由曲线 curve 表示的多边形内 * * @note 使用射线法(Ray Casting Algorithm)实现: * 从点向左水平发射射线,统计与多边形边的交点数量。 * 奇数个交点表示在内部/边上,偶数个表示在外部。 * https://www.cnblogs.com/charlee44/p/10704156.html * * @param x 待测点的 x 坐标 * @param y 待测点的 y 坐标 * @param curve 表示多边形的曲线对象 * @return true 如果点在多边形内部或边上 * @return false 如果点在多边形外部 */ inline bool IsPointInPolygon(double x, double y, const CCurveEx& curve) { // 构造射线(向左延伸足够远) const double RAY_LENGTH = 1e9; // 使用固定大长度 double rayEndX = x - RAY_LENGTH; int intersectCount = 0; // 交点计数器 // 遍历多边形每条边 for (int i = 0; i < curve.num; ++i) { int next = (i + 1) % curve.num; // 自动闭合多边形 double x1 = curve.x[i]; double y1 = curve.y[i]; double x2 = curve.x[next]; double y2 = curve.y[next]; // 如果点在线段上直接返回true if (IsPointOnLine(x, y, x1, y1, x2, y2)) { return true; } // 排除水平线段 if (std::abs(y2 - y1) < std::numeric_limits::epsilon()) continue; // 检查是否与射线相交 if (IsIntersect(x1, y1, x2, y2, x, y, rayEndX, y)) { // 获取线段端点Y坐标的最大值 #pragma push_marco("max") #undef max double topY = std::max(y1, y2); #pragma pop_marco("max") // 特殊情况处理:交点是线段顶点时 if (IsPointOnLine(x1, y1, x, y, rayEndX, y)) { if (y1 == topY) intersectCount++; } else if (IsPointOnLine(x2, y2, x, y, rayEndX, y)) { if (y2 == topY) intersectCount++; } else { intersectCount++; } } } // 奇偶判断 return (intersectCount % 2 == 1); } /** * 合并相交矩形 * * \param rects 所有矩形 * \return 合并后的矩形 */ inline std::vector MergeRects(const std::vector& rects) { if (rects.empty()) { return {}; } std::vector data = rects; std::sort(data.begin(), data.end(), [](const NBase::CRect8& a, const NBase::CRect8& b) { return a.left < b.left; }); bool changed; do { changed = false; for (size_t i = 0; i < data.size(); ++i) { for (size_t j = i + 1; j < data.size() && !changed; ++j) { if (IsIntersecting(data[i], data[j])) { double newLeft = min(data[i].left, data[j].left); double newTop = max(data[i].top, data[j].top); double newRight = max(data[i].right, data[j].right); double newBottom = min(data[i].bottom, data[j].bottom); data[i] = NBase::CRect8(newLeft, newTop, newRight, newBottom); data.erase(data.begin() + j); changed = true; } } } } while (changed); return data; } /** * 清理曲线中重复的点 * * \param curve * \return */ inline std::unique_ptr DeduplicateCurvePoints(const CCurveEx& curve) { if (curve.num == 0) { return std::make_unique(); } auto pNewCurve = std::make_unique(); pNewCurve->Create(curve.num); // 预分配最大可能内存(curve.num) double lastX = curve.x[0]; double lastY = curve.y[0]; double lastZ = curve.z[0]; pNewCurve->x[0] = lastX; pNewCurve->y[0] = lastY; pNewCurve->z[0] = lastZ; int32_t index = 1; for (int32_t i = 1; i < curve.num; i++) { double currentX = curve.x[i]; double currentY = curve.y[i]; double currentZ = curve.z[i]; if (currentX != lastX || currentY != lastY || currentZ != lastZ) { pNewCurve->x[index] = currentX; pNewCurve->y[index] = currentY; pNewCurve->z[index] = currentZ; lastX = currentX; lastY = currentY; lastZ = currentZ; index++; } } pNewCurve->num = index; // 我们的 x y z 可能会分配更多的内存,但是我们通过 num 限制不要去访问它们 pNewCurve->GetLocation(); return pNewCurve; } /** * 移除曲线中指定下标的节点 * * \param pCurve 曲线 * \param index 要被移除的下标 * \return 曲线本身的地址 */ inline void RemoveCurveIndex(CCurveEx& pCurve, int index) { if (index < 0 || index >= pCurve.num) { return; } for (int i = index; i + 1 < pCurve.num; i++) { pCurve.x[i] = pCurve.x[i + 1]; pCurve.y[i] = pCurve.y[i + 1]; pCurve.z[i] = pCurve.z[i + 1]; } pCurve.num -= 1; pCurve.GetLocation(); } /** * 构造 R 树 * * \param xy 图件 * \param filter 过滤器,只对匹配的图元构建 R 树 * \return R 树 */ inline std::unique_ptr> BuildRTree(CXy& xy, const CXyElementFilter& filter) { auto tree = std::make_unique>(); NBase::CPositionList select; xy.GetElement(filter, select); POSITION pos = select.GetHeadPosition(); while (pos != nullptr) { POSITION pt = select.GetNext(pos); COne* pOne = xy.GetAt(pt); if (pOne == nullptr) { continue; } NBase::CRect8 range(1e100, -1e100, -1e100, 1e100); pOne->GetRange(range); double minX = min(range.left, range.right); double minY = min(range.top, range.bottom); double maxX = max(range.left, range.right); double maxY = max(range.top, range.bottom); double minValue[2]{ minX, minY }; double maxValue[2]{ maxX, maxY }; tree->Insert(minValue, maxValue, pt); } return tree; } /** * 根据可迭代对象(如vector,list)推导元素类型 */ template using ItemType = typename std::iterator_traits::value_type; /** * ForEach, std::for_each 简化版,不用再传递两个迭代器 * * \param items * \param pred */ template void ForEach(IteratorType &items, std::function &item)> pred) { for (typename IteratorType::iterator ptr = items.begin(); ptr != items.end(); ptr++) { pred(*ptr); } } /** * 判断是否有任意一个元素满足条件 * * \param items * \param pred * \return */ template bool AnyOf(const IteratorType &items, std::function &item)> pred) { return std::any_of(items.begin(), items.end(), pred); } /** * 判断所有元素是否都不满足条件 * * \param items * \param pred * \return */ template bool NoneOf(const IteratorType &items, std::function &item)> pred) { return std::none_of(items.begin(), items.end(), pred); } /** * 判断所有元素是否都满足条件 * * \param items * \param pred * \return */ template bool AllOf(const IteratorType &items, std::function &item)> pred) { return std::all_of(items.begin(), items.end(), pred); } /** * 过滤出符合条件的元素 * * \param items * \param pred * \return */ template IteratorType Filter(const IteratorType &items, std::function &item)> pred) { IteratorType filteredIterator{}; ForEach(const_cast(items), [&filteredIterator, &pred](auto &item) { if (pred(item)) { filteredIterator.push_back(item); } }); return filteredIterator; } /** * 释放集合中的指针,并将集合清空 * * \param items */ template void DeleteAll(IteratorType& items) { static_assert(std::is_pointer>::value); for (auto* p : items) { delete p; } items.clear(); } /** * 判断是否包含某个元素 * * \param items * \param item * \return */ template bool Contains(const IteratorType &items, const ItemType &item) { for (const auto &e : items) { if (e == item) { return true; } } return false; } /** * 获取图件所有图层: * * \param xy * \return */ inline std::vector CollectAllLayers(const CXy& xy) { std::vector layers; CClassList* pClassList = const_cast(xy).GetClassList(); for (POSITION classPos = pClassList->GetHeadPosition(); classPos != nullptr; pClassList->GetNext(classPos)) { CLayerList* pLayerList = pClassList->GetAt(classPos); for (POSITION pos = pLayerList->GetHeadPosition(); pos != nullptr; pLayerList->GetNext(pos)) { CLayer* pLayer = pLayerList->GetAt(pos); layers.push_back(pLayer); } } return layers; } /** * 将 CList 转换为 std::vector,适合只遍历不修改的 CList 元素中场景 * * \param list * \return */ template std::vector CListToStdVector(const CList &list) { std::vector result; CListForEach(pos, list) { result.push_back(list.GetAt(pos)); } return result; } /** * 将 CList 转换为 std::list,适合只遍历不修改的 CList 元素中场景 * * \param list * \return */ template std::list CListToStdList(const CList &list) { std::list result; CListForEach(pos, list) { result.push_back(list.GetAt(pos)); } return result; } /** * 由于要从 CList 中删除元素需要 POSITION,这里把 POSITION 和 元素一并返回 * * \param list * \return */ template std::list> CListToStdList2(const CList &list) { std::list> result; CListForEach(pos, list) { result.push_back({ pos, list.GetAt(pos) }); } return result; } /** * 将 POSITION 列表转换为 COne 列表 * * \param positions 存储了图元 POSITION 的列表 * \return */ std::list PtrsToCOnes(const CPtrList& pList); /** * 根据条件过滤 Cone,这里是筛选出获取条件的 COne * * \param list * \param pred 过滤条件 * \return */ std::list ConesFilter(const std::list &cones, std::function pred); /** * CList 不支持 =,这些实现一个通用函数 * * \param target 被赋值的列表,相当于 = 左边的列表 * \param source 被拷贝的列表,相当于 = 右边的列表 */ template void Assign(CList &target, const CList &source) { CListForEach(pos, source) { target.AddTail(source.GetAt(pos)); } } /** * 计算两点之间的距离 */ double CalcDistance(double x1, double y1, double x2, double y2); /** * 计算曲线的长度 */ double CalcCurveDistance(const CCurveEx* pCurve); /** * 计算斜率 * x1 x2 不能相同,事实上就计算斜率而言,y1 y2 也不能相同,需要调用方单独处理 */ double CalcSlope(double x1, double y1, double x2, double y2); /** * 给 CList 去重 * * \param items 出参,被去重的 items */ template void CListUnique(CList &items) { std::vector cacheItems; std::vector removePositions; CListForEach(pos, items) { T& value = items.GetAt(pos); if (Contains(cacheItems, value)) { removePositions.push_back(pos); } else { cacheItems.push_back(value); } } for (POSITION pos : removePositions) { items.RemoveAt(pos); } } /** * 判断字符串和搜索是否能够匹配上 * \param source 被搜索的字符串 * \param search 搜索的内容 * \param ignoreCase 忽略大小写 * \param matchWholeWord 完全匹配 * \return */ bool SearchMatch(const CString& source, const CString& search, bool ignoreCase, bool matchWholeWord); // 获取父图层名称 CString GetParentLayerPath(const CString& pLayer); /** * 判断两个图层名是否相同,它会处理图层名不标准(如左右有空格)以及有无 class 的情况 * \param layerName1 图层名1 * \param layerName2 图层名2 * \return 图层名是否相同 */ bool IsSameLayerName(const CString& layerName1, const CString& layerName2); /** * 连接字符串 * \param 字符串列表 * \param delimiter 连接符 * \return 返回连接后的字符串 */ CString JoinStrings(const std::vector& strings, const CString& delimiter); /** * 按指定分割符分割字符串 * * \param string 被切割的字符串 * \param delimiter 分割符 * \return 返回切割好的字符串 */ std::vector SplitString(const CString& string, const CString& delimiter); /** * 判断 position 是否合法 * \param pXy * \param position 被判断的 position * \return */ bool IsValidPosition(CXy *pXy, POSITION position); /** * 获取合法的 position 列表 * \param pXy * \param position 被判断的 position 数组 * \param count position 数组长度 * \return */ std::vector GetValidPositions(CXy* pXy, POSITION positions[], int count); std::wstring StringToWString(const std::string& str); std::string WStringToString(const std::wstring &wstr); /** * CString 转 utf-8 * * \param str 要被转换的字符串 * \return 转换后的字符串 */ std::string CStringToUtf8String(const CString& str); /** * utf8 std::string 转 CString */ CString Utf8StringToCString(const std::string& utf8Str); /** * 抹平,超过最小选最小,超过最大选最大 * * \param value 目标值 * \param min 最小值 * \param max 最大值 * \return */ inline double Flatten(double value, double min, double max) { assert(min <= max); if (value < min) { return min; } if (value > max) { return max; } return value; } /** * 判断是否在范围内 * * \param value 目标值 * \param min 最小值 * \param max 最大值 * \return */ inline bool InRange(double value, double min, double max) { assert(min < max); return (value >= min && value <= max); } /** * 判断是否不在范围内 * * \param value 目标值 * \param min 最小值 * \param max 最大值 * \return */ inline bool OutOfRange(double value, double min, double max) { assert(min < max); return !InRange(value, min, max); } /** * 将 COne 序列化为二进制数据 * * \param pOne * \return */ inline std::vector SerializeCOneToByteArray(COne* pOne) { CMemFile memFile; CArchive archive(&memFile, CArchive::store); pOne->Serialize(archive, 2022); archive.Close(); size_t dataSize = memFile.GetLength(); std::vector result(dataSize); memFile.SeekToBegin(); memFile.Read(result.data(), static_cast(dataSize)); return result; } /** * 将二进制数据反序列化 COne * * \param array * \return */ inline std::unique_ptr DeserializeCOneFromByteArray(const std::vector& byteArray) { CMemFile memFile(const_cast(byteArray.data()), static_cast(byteArray.size())); CArchive archive(&memFile, CArchive::load); std::unique_ptr pOne = std::make_unique(); pOne->Serialize(archive, 2022); return pOne; } /** * 将时间点转换为 "年.月.日 时:分:秒.毫秒" 格式的字符串 * * \param timepoint 时间点 * \return 格式化后的字符串 */ inline std::string FormatTime(const std::chrono::system_clock::time_point& timepoint) { std::time_t time_t = std::chrono::system_clock::to_time_t(timepoint); std::tm tm = *std::localtime(&time_t); std::stringstream ss; ss << std::put_time(&tm, "%Y-%m-%d %H:%M:%S"); // 精确到毫秒,秒的话,会有太多重复的了 auto now_ms = std::chrono::duration_cast(timepoint.time_since_epoch()) % 1000; ss << "." << now_ms.count(); return ss.str(); } /** * 将格式为 "年.月.日 时:分:秒.毫秒" 的字符串转换为时间点 * * \param timeString 格式化的字符串 * \return 时间点 */ inline std::chrono::system_clock::time_point ParseTime(const std::string& timeString) { std::stringstream ss(timeString); std::tm tm {}; ss >> std::get_time(&tm, "%Y-%m-%d %H:%M:%S"); std::time_t time_t = std::mktime(&tm); auto timePoint = std::chrono::system_clock::from_time_t(time_t); // 获取毫秒部分 char dot = 0; // 先忽略掉 "." ss >> dot; int ms = 0; ss >> ms; timePoint += std::chrono::milliseconds(ms); return timePoint; } /** * 生成 uuid * * \return */ inline std::string GenerateUUID() { GUID guid{}; CoInitialize(&guid); HRESULT hr = CoCreateGuid(&guid); char buf[40] = { 0 }; sprintf_s(buf, sizeof(buf), "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]); return std::string(buf); } /** * 获取单例对象 * * \return */ template T& GetInstace() { static T instance; return instance; } /** * 文件路径处理类,使用类包装处理,使得针对 Path 的操作集中起来 */ class Path { public: /** * 路径的文件名和扩展名 * * \param path * \return */ static CString GetFileName(const CString& path) { return PathFindFileName(path); } /** * 返回文件名,该文件名不带由只读字符范围表示的文件路径扩展名。 * * \param path 文件路径 * \return 包括最后一个句点 (.) 及其后面的所有字符 */ static CString GetFileNameWithoutExtension(const CString& path) { CString fileName = PathFindFileName(path); CString fileExtenstion = PathFindExtension(path); return fileName.Left(fileName.GetLength() - fileExtenstion.GetLength()); } /** * 返回指定路径字符串的扩展名(包括句点“.”) * * \param path * \return */ static CString GetExtension(const CString& path) { return PathFindExtension(path); } }; /** * 判断 z 是不是无效值 * \param z 要判断的 z 值本身 */ inline bool IsUnvalidValue(double z) { const double invalidZ = -1E300; return z <= invalidZ; } /** * 释放 CPtrList 中的所有元素,这里面的内存必须要是 new 分配的 * * \param T 里面存储的是哪些元素的指针,如果不正确指定它,它将无法正确调用析构函数,会造成很多问题 * \param ptrList 要释放的列表 */ template void DeleteAll(CPtrList& ptrList) { for (POSITION pos = ptrList.GetHeadPosition(); pos != nullptr; ptrList.GetNext(pos)) { delete reinterpret_cast(ptrList.GetAt(pos)); } ptrList.RemoveAll(); } /** * 交换两个指针,不同类型指针交换是无意义且危险的,所以不要使用 void * 代替这里的 T* * * \param first * \param second */ template void SwapPointer(T*& first, T*& second) { T* temp = first; first = second; second = temp; } /** * 从原始的 CPtrList 中去除重复的指针,并将唯一的指针添加到新的 CPtrList 中 * * @param 原始的 CPtrList,包含可能重复的指针 * @param 去重后的 CPtrList,仅包含唯一的指针 */ inline void UniquePtrList(const CPtrList& ptrList, CPtrList& newPtrList) { newPtrList.RemoveAll(); for (POSITION pos = ptrList.GetHeadPosition(); pos != nullptr; ptrList.GetNext(pos)) { void* ptr = const_cast(ptrList).GetAt(pos); if (newPtrList.Find(ptr) == nullptr) { newPtrList.AddTail(ptr); } } } /** * 从原始的 CPositionList 中去除重复的 POSITION,并将唯一的 POSITION 添加到新的 CPositionList 中 * * @param posList 原始的 CPositionList,包含可能重复的 POSITION * @param newPosList 去重后的 CPositionList,仅包含唯一的 POSITION */ inline void UniquePositionList(const NBase::CPositionList& posList, NBase::CPositionList& newPosList) { newPosList.RemoveAll(); std::unordered_set seenPositions; for (POSITION pos = posList.GetHeadPosition(); pos != nullptr; posList.GetNext(pos)) { POSITION pt = posList.GetAt(pos); if (seenPositions.find(pt) == seenPositions.end()) { seenPositions.insert(pt); newPosList.AddTail(pt); } } } /** * 计算线段交点 * https://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect/565282# * * \param p0_x 第一条曲线起点 x 坐标 * \param p0_y 第一条曲线起点 y 坐标 * \param p1_x 第一条曲线终点 x 坐标 * \param p1_y 第一条曲线终点 y 坐标 * \param p2_x 第二条曲线起点 x 坐标 * \param p2_y 第二条曲线起点 y 坐标 * \param p3_x 第二条曲线终点 x 坐标 * \param p3_y 第二条曲线终点 y 坐标 * \param i_x 交点 x 坐标 * \param i_y 交点 y 坐标 * \return 1 表示有交点,0 表示没交点 */ inline int GetLineIntersection(double p0_x, double p0_y, double p1_x, double p1_y, double p2_x, double p2_y, double p3_x, double p3_y, double& i_x, double& i_y) { double s1_x = p1_x - p0_x; double s1_y = p1_y - p0_y; double s2_x = p3_x - p2_x; double s2_y = p3_y - p2_y; double s = (-s1_y * (p0_x - p2_x) + s1_x * (p0_y - p2_y)) / (-s2_x * s1_y + s1_x * s2_y); double t = ( s2_x * (p0_y - p2_y) - s2_y * (p0_x - p2_x)) / (-s2_x * s1_y + s1_x * s2_y); if (s >= 0 && s <= 1 && t >= 0 && t <= 1) { // Collision detected i_x = p0_x + (t * s1_x); i_y = p0_y + (t * s1_y); return 1; } return 0; // No collision } /** * 交换修饰 * * \param first 第一个图元 * \param second 第二个图元 */ inline void SwapHowtoView(COne& first, COne& second) { auto* pViewPoint = first.HowToViewPoint; auto* pViewCurve = first.HowToViewCurve; first.HowToViewPoint = second.HowToViewPoint; first.HowToViewCurve = second.HowToViewCurve; second.HowToViewPoint = pViewPoint; second.HowToViewCurve = pViewCurve; } /** * 拷贝图层修饰 * * \param sourceLayer 被拷贝修饰的图层 * \param targetLayer 拷贝到的目标图层 */ inline void CopyLayerHowtoViewTo(const CLayer* pSourceLayer, CLayer* pTargetLayer) { assert(pSourceLayer != nullptr); assert(pTargetLayer != nullptr); SafeDelete(pTargetLayer->HowToViewCurve); SafeDelete(pTargetLayer->HowToViewPoint); if (pSourceLayer->HowToViewCurve != nullptr) { pTargetLayer->HowToViewCurve = new CHowToViewCurve(); *(pTargetLayer->HowToViewCurve) = *(pSourceLayer->HowToViewCurve); } if (pSourceLayer->HowToViewPoint != nullptr) { pTargetLayer->HowToViewPoint = new CHowToViewPoint(); *(pTargetLayer->HowToViewPoint) = *(pSourceLayer->HowToViewPoint); } } /** * 拷贝图元修饰 * * \param sourceOne 被拷贝修饰的图层 * \param targetOne 拷贝到的目标图层 */ inline void CopyOneHowtoViewTo(const COne* pSourceOne, COne* pTargetOne) { assert(pSourceOne != nullptr); assert(pTargetOne != nullptr); SafeDelete(pTargetOne->HowToViewCurve); SafeDelete(pTargetOne->HowToViewPoint); if (pSourceOne->HowToViewCurve != nullptr) { pTargetOne->HowToViewCurve = new CHowToViewCurve(); *(pTargetOne->HowToViewCurve) = *(pSourceOne->HowToViewCurve); } if (pSourceOne->HowToViewPoint != nullptr) { pTargetOne->HowToViewPoint = new CHowToViewPoint(); *(pTargetOne->HowToViewPoint) = *(pSourceOne->HowToViewPoint); } } /** * 保证函数在退出时执行指定函数 */ class FinalAction { public: /** * 构造函数 * * \param func 函数对象 * \param ...args 函数参数 */ template explicit FinalAction(Callable&& func, Args&&... args) : callable(std::bind(std::forward(func), std::forward(args)...)), active(true) { } FinalAction(FinalAction&& other) noexcept : callable(std::move(other.callable)), active(other.active) { } FinalAction(const FinalAction&) = delete; FinalAction& operator=(const FinalAction&) = delete; void Cancel() { active = false; } ~FinalAction() noexcept { if (active) { callable(); } } private: std::function callable; bool active = true; }; /** * CFile RAII 封装 */ class CFileHelper { public: CFileHelper(const CFileHelper&) = delete; CFileHelper& operator=(const CFileHelper&) = delete; CFileHelper(const CFileHelper&&) = delete; CFileHelper& operator=(const CFileHelper&&) = delete; CFileHelper(const CString& filePath, UINT openFlags) { m_isOpen = m_file.Open(filePath, CFile::modeCreate | CFile::modeWrite); } ~CFileHelper() { if (m_isOpen) { m_file.Close(); } m_isOpen = false; } bool IsOpen() const { return m_isOpen; } UINT Read(void* lpBuf, UINT nCount) { Validate(); return m_file.Read(lpBuf, nCount); } void Write(const void* lpBuf, UINT nCount) { Validate(); m_file.Write(lpBuf, nCount); } ULONGLONG GetPosition() const { Validate(); return m_file.GetPosition(); } ULONGLONG Seek(LONGLONG lOff, UINT nFrom) { Validate(); return m_file.Seek(lOff, nFrom); } ULONGLONG GetLength() const { Validate(); return m_file.GetLength(); } void SetLength(ULONGLONG nLength) { Validate(); return m_file.SetLength(nLength); } CString GetFileName() const { Validate(); return m_file.GetFileName(); } CString GetFilePath() const { Validate(); return m_file.GetFilePath(); } CString GetFileTitle() const { Validate(); return m_file.GetFileTitle(); } CFile& GetFile() { Validate(); return m_file; } private: void Validate() const { if (!m_isOpen) { throw std::runtime_error("File not open"); } } bool m_isOpen = false; CFile m_file; };