|
|
#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;
|
|
|
};
|