计算空间中圆弧的包围框

  本文介绍一种计算空间中圆弧包围框的计算方法。空间中存在一个圆弧,计算一个能够包围圆弧的包围框。

算法思想

  圆弧表示的定义:
  本文中使用以下参数定义空间中的圆弧:圆弧的圆点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绘制圆弧及其包围框,其效果如下图所示:



  可以戳这里下载可执行程序体验效果。


当珍惜每一片时光~