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.

1491 lines
31 KiB
C++

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#pragma once
#include <algorithm>
#include <functional>
#include <chrono>
#include <ctime>
#include <iomanip>
#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 <typename T>
inline CString ToCString(const T& value)
{
return _T("");
}
template <>
inline CString ToCString<int32_t>(const int32_t& value)
{
CString str;
str.Format(_T("%d"), value);
return str;
}
template <>
inline CString ToCString<uint32_t>(const uint32_t& value)
{
CString str;
str.Format(_T("%u"), value);
return str;
}
template <>
inline CString ToCString<long>(const long& value)
{
CString str;
str.Format(_T("%ld"), value);
return str;
}
template <>
inline CString ToCString<unsigned long>(const unsigned long& value)
{
CString str;
str.Format(_T("%lu"), value);
return str;
}
template <>
inline CString ToCString<int64_t>(const int64_t& value)
{
CString str;
str.Format(_T("%lld"), value);
return str;
}
template <>
inline CString ToCString<uint64_t>(const uint64_t& value)
{
CString str;
str.Format(_T("%llu"), value);
return str;
}
template <>
inline CString ToCString<float>(const float& value)
{
CString str;
str.Format(_T("%f"), value);
return str;
}
template <>
inline CString ToCString<double>(const double& value)
{
CString str;
str.Format(_T("%f"), value);
return str;
}
template <>
inline CString ToCString<bool>(const bool& value)
{
return value ? _T("true") : _T("false");
}
template <>
inline CString ToCString<CString>(const CString& value)
{
return value;
}
template <typename T>
T& GetInstance()
{
static T instance;
return instance;
}
/**
* CList 列表合并
*
* \param source 源列表,会被修改
* \param target 被添加的元素列表
*/
template<typename T, typename E>
void CListAddRange(CList<T, E> &source, const CList<T, E> &target)
{
for (POSITION pos = (target).GetHeadPosition(); pos != nullptr; (target).GetNext(pos))
{
source.AddTail(target.GetAt(pos));
}
}
/**
* CList 列表移除
*
* 从 source 中移除所有在 target 中存在的元素
*
* \param source 源列表,会被修改
* \param target 要移除的元素集合
*/
template<typename T, typename E>
void CListRemoveRange(CList<T, E>& source, const CList<T, E>& 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<CPolyline> &polylines, const CPolyline &polyline);
/**
* 判断某个点是否在这个集合内
* \param pointXYZs
* \param pointXYZ
*/
bool CPointXYZContains(const std::vector<CPointXYZ> &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<double>::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<double>::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<double>::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<NBase::CRect8> MergeRects(const std::vector<NBase::CRect8>& rects)
{
if (rects.empty())
{
return {};
}
std::vector<NBase::CRect8> 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<CCurveEx> DeduplicateCurvePoints(const CCurveEx& curve)
{
if (curve.num == 0)
{
return std::make_unique<CCurveEx>();
}
auto pNewCurve = std::make_unique<CCurveEx>();
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<RTree<POSITION, double, 2>> BuildRTree(CXy& xy, const CXyElementFilter& filter)
{
auto tree = std::make_unique<RTree<POSITION, double, 2>>();
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 <typename IteratorType>
using ItemType = typename std::iterator_traits<typename IteratorType::iterator>::value_type;
/**
* ForEach, std::for_each 简化版,不用再传递两个迭代器
*
* \param items
* \param pred
*/
template <typename IteratorType>
void ForEach(IteratorType &items, std::function<void(ItemType<IteratorType> &item)> pred)
{
for (typename IteratorType::iterator ptr = items.begin(); ptr != items.end(); ptr++)
{
pred(*ptr);
}
}
/**
* 判断是否有任意一个元素满足条件
*
* \param items
* \param pred
* \return
*/
template <typename IteratorType>
bool AnyOf(const IteratorType &items, std::function<bool(const ItemType<IteratorType> &item)> pred)
{
return std::any_of(items.begin(), items.end(), pred);
}
/**
* 判断所有元素是否都不满足条件
*
* \param items
* \param pred
* \return
*/
template <typename IteratorType>
bool NoneOf(const IteratorType &items, std::function<bool(const ItemType<IteratorType> &item)> pred)
{
return std::none_of(items.begin(), items.end(), pred);
}
/**
* 判断所有元素是否都满足条件
*
* \param items
* \param pred
* \return
*/
template <typename IteratorType>
bool AllOf(const IteratorType &items, std::function<bool(const ItemType<IteratorType> &item)> pred)
{
return std::all_of(items.begin(), items.end(), pred);
}
/**
* 过滤出符合条件的元素
*
* \param items
* \param pred
* \return
*/
template <typename IteratorType>
IteratorType Filter(const IteratorType &items, std::function<bool(const ItemType<IteratorType> &item)> pred)
{
IteratorType filteredIterator{};
ForEach<IteratorType>(const_cast<IteratorType&>(items), [&filteredIterator, &pred](auto &item)
{
if (pred(item))
{
filteredIterator.push_back(item);
}
});
return filteredIterator;
}
/**
* 释放集合中的指针,并将集合清空
*
* \param items
*/
template <typename IteratorType>
void DeleteAll(IteratorType& items)
{
static_assert(std::is_pointer<ItemType<IteratorType>>::value);
for (auto* p : items)
{
delete p;
}
items.clear();
}
/**
* 判断是否包含某个元素
*
* \param items
* \param item
* \return
*/
template <typename IteratorType>
bool Contains(const IteratorType &items, const ItemType<IteratorType> &item)
{
for (const auto &e : items)
{
if (e == item)
{
return true;
}
}
return false;
}
/**
* 获取图件所有图层:
*
* \param xy
* \return
*/
inline std::vector<CLayer*> CollectAllLayers(const CXy& xy)
{
std::vector<CLayer*> layers;
CClassList* pClassList = const_cast<CXy&>(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 <typename T>
std::vector<T> CListToStdVector(const CList<T, T> &list)
{
std::vector<T> result;
CListForEach(pos, list)
{
result.push_back(list.GetAt(pos));
}
return result;
}
/**
* 将 CList 转换为 std::list适合只遍历不修改的 CList 元素中场景
*
* \param list
* \return
*/
template <typename T>
std::list<T> CListToStdList(const CList<T, T> &list)
{
std::list<T> result;
CListForEach(pos, list)
{
result.push_back(list.GetAt(pos));
}
return result;
}
/**
* 由于要从 CList 中删除元素需要 POSITION这里把 POSITION 和 元素一并返回
*
* \param list
* \return
*/
template <typename T>
std::list<std::pair<POSITION, T>> CListToStdList2(const CList<T, T> &list)
{
std::list<std::pair<POSITION, T>> result;
CListForEach(pos, list)
{
result.push_back({ pos, list.GetAt(pos) });
}
return result;
}
/**
* 将 POSITION 列表转换为 COne 列表
*
* \param positions 存储了图元 POSITION 的列表
* \return
*/
std::list<COne*> PtrsToCOnes(const CPtrList& pList);
/**
* 根据条件过滤 Cone这里是筛选出获取条件的 COne
*
* \param list
* \param pred 过滤条件
* \return
*/
std::list<COne*> ConesFilter(const std::list<COne*> &cones, std::function<bool(COne*)> pred);
/**
* CList 不支持 =,这些实现一个通用函数
*
* \param target 被赋值的列表,相当于 = 左边的列表
* \param source 被拷贝的列表,相当于 = 右边的列表
*/
template <typename T>
void Assign(CList<T, T> &target, const CList<T, T> &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 <typename T>
void CListUnique(CList<T, T> &items)
{
std::vector<T> cacheItems;
std::vector<POSITION> 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<CString>& strings, const CString& delimiter);
/**
* 按指定分割符分割字符串
*
* \param string 被切割的字符串
* \param delimiter 分割符
* \return 返回切割好的字符串
*/
std::vector<CString> 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<POSITION> 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<BYTE> SerializeCOneToByteArray(COne* pOne)
{
CMemFile memFile;
CArchive archive(&memFile, CArchive::store);
pOne->Serialize(archive, 2022);
archive.Close();
size_t dataSize = memFile.GetLength();
std::vector<BYTE> result(dataSize);
memFile.SeekToBegin();
memFile.Read(result.data(), static_cast<UINT>(dataSize));
return result;
}
/**
* 将二进制数据反序列化 COne
*
* \param array
* \return
*/
inline std::unique_ptr<COne> DeserializeCOneFromByteArray(const std::vector<BYTE>& byteArray)
{
CMemFile memFile(const_cast<BYTE*>(byteArray.data()), static_cast<UINT>(byteArray.size()));
CArchive archive(&memFile, CArchive::load);
std::unique_ptr<COne> pOne = std::make_unique<COne>();
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<std::chrono::milliseconds>(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 <typename T>
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<typename T>
void DeleteAll(CPtrList& ptrList)
{
for (POSITION pos = ptrList.GetHeadPosition(); pos != nullptr; ptrList.GetNext(pos))
{
delete reinterpret_cast<T*>(ptrList.GetAt(pos));
}
ptrList.RemoveAll();
}
/**
* 交换两个指针,不同类型指针交换是无意义且危险的,所以不要使用 void * 代替这里的 T*
*
* \param first
* \param second
*/
template <typename T>
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<CPtrList&>(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<POSITION> 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 <typename Callable, typename... Args>
explicit FinalAction(Callable&& func, Args&&... args)
: callable(std::bind(std::forward<Callable>(func), std::forward<Args>(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<void()> 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;
};