计算空间中圆弧的包围框
本文介绍一种计算空间中圆弧包围框的计算方法。空间中存在一个圆弧,计算一个能够包围圆弧的包围框。
算法思想
圆弧表示的定义:
本文中使用以下参数定义空间中的圆弧:圆弧的圆点center
、圆弧圆点指向起始点的单位向量start
、圆弧所在面的法向normal
、圆弧半径radius
和圆弧扫过的角度sweepAngle
。其中,右手大拇指指向法向方向,握拳时手指环绕的方向为sweepAngle
的正方向。
对于圆弧的包围框来说,可以分为两种情况进行计算:Minor Arc(小于等于180度)和Major Arc(大于180度)。如下图所示(假设圆弧的法向朝向屏幕外):
如果圆弧小于等于180度,那么矩形包围框ABCD的AB两点分别可以使用圆弧的终点和起点表示,计算得到圆心到圆弧中点的方向和CA的长度,可以通过AB两点计算得到CD两个点;如果圆弧大于180度,矩形包围框有三个边的偏移量都是半径长度,根据几何关系可以确定另外一个边的偏移量,在圆弧的平面上建立二维坐标系,可以很方便的根据各个方向上的偏移量计算得到ABCD四个点组成的包围框。
代码实现
圆弧包围框计算的代码实现如下:
class CircleArc {
public:
CircleArc(){}
CircleArc(glm::vec3 c, glm::vec3 s, glm::vec3 n, float r, float a)
: center(c), start(glm::normalize(s)), normal(glm::normalize(n)), radius(r), sweepAngle(a) {}
CircleArc(glm::vec3 c, float r) : center(c), radius(r) {}
explicit CircleArc(float r) : radius(r) {}
float sweepAngleByDegrees() const { return sweepAngle; }
float sweepAngleByRadians() const { return glm::radians(sweepAngle); }
// 计算指定角度处的圆弧上的点, 单位为角度
glm::vec3 pointAt(float angle) const {
angle = glm::radians(angle);
glm::vec3 baseX = start;
glm::vec3 baseY = glm::cross(normal, baseX);
glm::vec3 offset = glm::cos(angle) * baseX + glm::sin(angle) * baseY;
return center + offset * radius;
}
glm::vec3 startPoint() const { return pointAt(0.0f); }
glm::vec3 endPoint() const {
return pointAt(sweepAngle);
}
// 计算包围盒,返回圆弧所在平面上包围盒的四个顶点(以normal相反的方向观察,逆时针顺序)
std::array<glm::vec3, 4> boundingBox() const {
auto ptStart = startPoint();
auto ptEnd = endPoint();
auto ptMid = pointAt(sweepAngle / 2.0f);
auto offsetDir = glm::normalize(ptMid - center);
if(sweepAngle <= 180.0f){
auto offset = radius * (1.0f - glm::cos(sweepAngleByRadians() * 0.5f));
return { ptEnd, ptStart, ptStart + offsetDir * offset, ptEnd + offsetDir * offset};
} else {
auto offset = radius * glm::cos(PI - sweepAngleByRadians() * 0.5f);
auto baseX = glm::cross(normal, offsetDir);
return {
center + (-baseX * radius - offsetDir * offset),
center + (baseX * radius - offsetDir * offset),
ptMid + (baseX * radius),
ptMid - (baseX * radius)
};
}
}
public:
glm::vec3 center = glm::vec3(0.0f);
glm::vec3 start = glm::vec3(1.0, 0.0, 0.0); // 起始方向
glm::vec3 normal = glm::vec3(0.0, 0.0, 1.0); // 法线方向
float radius = 1.0f;
float sweepAngle = 360.0f; // 扫过的角度,degrees
};
演示示例
根据上述算法,使用OpenGL绘制圆弧及其包围框,其效果如下图所示:
可以戳这里下载可执行程序体验效果。
Comments | NOTHING