Open CASCADE学习|将多段圆弧曲线平移至最低点位于坐标原点
核心原理分析
1. 寻找全局最低点
要将曲线平移到原点,首先需要确定曲线的最低点。对于由多段圆弧组成的曲线,最低点可能出现在以下位置:
- 某个圆弧段的起点或终点
- 某个圆弧段的最低点(当圆弧包含该点时)
对于每个圆弧段,需要判断其是否包含最低点,这取决于圆弧的方向和覆盖的角度范围。
2. 计算平移向量
一旦找到全局最低点 ( m i n x , m i n y ) (min_x, min_y) (minx,miny),平移向量即为 ( − m i n x , − m i n y ) (-min_x, -min_y) (−minx,−miny),将该向量应用到曲线的所有点上,即可将最低点移动到原点。
3. 应用平移
对每个圆弧段的起点、终点和圆心应用平移向量,更新它们的坐标。
公式推导
圆弧最低点判断
对于一个圆弧段,圆心为 C ( C x , C y ) C(C_x, C_y) C(Cx,Cy),半径为 r r r,起点为 S ( S x , S y ) S(S_x, S_y) S(Sx,Sy),终点为 E ( E x , E y ) E(E_x, E_y) E(Ex,Ey)。
- 计算角度范围:
- 起点和终点相对于圆心的角度:
θ S = arctan 2 ( S y − C y , S x − C x ) \theta_S = \arctan2(S_y - C_y, S_x - C_x) θS=arctan2(Sy−Cy,Sx−Cx)
- 起点和终点相对于圆心的角度:
θ E = arctan 2 ( E y − C y , E x − C x ) \theta_E = \arctan2(E_y - C_y, E_x - C_x) θE=arctan2(Ey−Cy,Ex−Cx)
- 通过叉积判断圆弧方向(逆时针或顺时针):
cross = ( S x − C x ) ( E y − C y ) − ( S y − C y ) ( E x − C x ) \text{cross} = (S_x - C_x)(E_y - C_y) - (S_y - C_y)(E_x - C_x) cross=(Sx−Cx)(Ey−Cy)−(Sy−Cy)(Ex−Cx)
若 cross > 0 \text{cross} > 0 cross>0,则为逆时针方向;否则为顺时针方向。
-
判断270度是否在角度范围内:
- 对于逆时针圆弧:
- 若 θ S ≤ θ E \theta_S \leq \theta_E θS≤θE,范围为 [ θ S , θ E ] [\theta_S, \theta_E] [θS,θE]
- 否则,范围为 [ θ S , θ E + 2 π ] [\theta_S, \theta_E + 2\pi] [θS,θE+2π]
- 对于顺时针圆弧:
- 若 θ S ≥ θ E \theta_S \geq \theta_E θS≥θE,范围为 [ θ E , θ S ] [\theta_E, \theta_S] [θE,θS]
- 否则,范围为 [ θ E , θ S + 2 π ] [\theta_E, \theta_S + 2\pi] [θE,θS+2π]
- 检查 3 π / 2 3\pi/2 3π/2(270度)是否在上述范围内。
- 对于逆时针圆弧:
-
确定最低点:
- 若270度在范围内,最低点为圆心下方的点 ( C x , C y − r ) (C_x, C_y - r) (Cx,Cy−r)
- 否则,最低点为起点或终点中y坐标较小的那个。
平移向量计算
找到全局最低点
(
m
i
n
x
,
m
i
n
y
)
(min_x, min_y)
(minx,miny) 后,平移向量为:
translation
=
(
−
m
i
n
x
,
−
m
i
n
y
,
0
)
\text{translation} = (-min_x, -min_y, 0)
translation=(−minx,−miny,0)
完整代码实现
#include <vector>
#include <gp_Pnt.hxx>
#include <gp_Vec.hxx>
#include <cmath>
#include <limits>
struct ArcSegment {
gp_Pnt start;
gp_Dir startTangent;
gp_Pnt end;
gp_Dir endTangent;
gp_Pnt center;
double radius;
};
// 查找所有圆弧段中的全局最低点
gp_Pnt findGlobalLowestPoint(const std::vector<ArcSegment>& arcs) {
double min_y = std::numeric_limits<double>::max();
gp_Pnt lowest_point;
for (const auto& arc : arcs) {
// 处理直线段(半径接近0的情况)
if (arc.radius < 1e-6) {
const gp_Pnt& candidate = (arc.start.Y() < arc.end.Y()) ? arc.start : arc.end;
if (candidate.Y() < min_y) {
min_y = candidate.Y();
lowest_point = candidate;
}
continue;
}
// 提取圆弧几何参数
const double Cx = arc.center.X();
const double Cy = arc.center.Y();
const double r = arc.radius;
const double Sy = arc.start.Y();
const double Ey = arc.end.Y();
// 计算起点和终点相对于圆心的角度(弧度)
const double theta_S = atan2(arc.start.Y() - Cy, arc.start.X() - Cx);
const double theta_E = atan2(arc.end.Y() - Cy, arc.end.X() - Cx);
// 通过叉积判断圆弧方向(逆时针/顺时针)
const double cross = (arc.start.X() - Cx) * (arc.end.Y() - Cy)
- (arc.start.Y() - Cy) * (arc.end.X() - Cx);
const bool is_ccw = (cross > 0); // 逆时针方向
// 标准化角度到[0, 2π)
double theta_start = fmod(theta_S + 2*M_PI, 2*M_PI);
double theta_end = fmod(theta_E + 2*M_PI, 2*M_PI);
const double theta_270 = 3*M_PI/2; // 270度对应弧度
// 判断圆弧是否包含最低点(圆心正下方)
bool contains_270 = false;
if (is_ccw) {
if (theta_start <= theta_end) {
contains_270 = (theta_start <= theta_270) && (theta_270 <= theta_end);
} else {
contains_270 = (theta_270 >= theta_start) || (theta_270 <= theta_end);
}
} else {
if (theta_start >= theta_end) {
contains_270 = (theta_end <= theta_270) && (theta_270 <= theta_start);
} else {
contains_270 = (theta_270 >= theta_start) || (theta_270 <= theta_end);
}
}
// 确定本段最低点
double current_min_y = std::min(Sy, Ey);
gp_Pnt current_lowest = (Sy < Ey) ? arc.start : arc.end;
// 如果包含圆心正下方点则参与比较
if (contains_270) {
const double cy_low = Cy - r;
if (cy_low < current_min_y) {
current_min_y = cy_low;
current_lowest = gp_Pnt(Cx, cy_low, 0.0);
}
}
// 更新全局最低点
if (current_min_y < min_y) {
min_y = current_min_y;
lowest_point = current_lowest;
}
}
return lowest_point;
}
// 平移所有圆弧段使最低点位于原点
std::vector<ArcSegment> translateArcsToOrigin(const std::vector<ArcSegment>& arcs) {
// 找到全局最低点
const gp_Pnt lowest = findGlobalLowestPoint(arcs);
// 计算平移向量
const gp_Vec translation(-lowest.X(), -lowest.Y(), 0);
// 平移所有圆弧几何元素
std::vector<ArcSegment> translated;
for (const auto& arc : arcs) {
ArcSegment newArc = arc;
// 平移起点
newArc.start.Translate(translation);
// 平移终点
newArc.end.Translate(translation);
// 平移圆心
newArc.center.Translate(translation);
translated.push_back(newArc);
}
return translated;
}
/* 使用示例
int main() {
std::vector<ArcSegment> inputArcs; // 输入的多段圆弧数据
std::vector<ArcSegment> outputArcs = translateArcsToOrigin(inputArcs);
// outputArcs中的圆弧最低点已位于坐标原点
return 0;
}
*/
代码说明
-
寻找全局最低点:
- 遍历每个圆弧段,计算其可能的最低点,包括起点、终点和圆心下方的点(如果圆弧包含该点)。
- 比较所有圆弧段的最低点,找到全局最低点。
-
计算平移向量:
- 根据全局最低点的坐标,计算将该点移动到原点所需的平移向量。
-
应用平移:
- 对每个圆弧段的起点、终点和圆心应用平移向量,更新它们的坐标。
该实现确保了多段圆弧构成的连续曲线能够准确地平移到原点,同时保留了曲线的几何特性和连续性。