OpenGL绘制两个等半径圆时如何判定重叠区域点并排除重叠部分
绘制排除重叠区域的等半径双圆方案
需求描述
需要绘制两个半径相等的圆,仅填充两圆的非重叠区域,排除相交重叠部分,预期效果如下:
要求仅在图中非重叠区域绘制、设置像素点。
现有问题代码
已编写的基础OpenGL/GLUT实现代码如下,运行结果不符合预期:
void draw_venn(){ float radian_to_degree_theta=2 * 3.14 / 360, r = 0.5, distance=0.3, theta=0.0, theta2=0.0, xR=0.0, yR=0.0, xG=0.0, yG=0.0, sum_radii=0, dis=0.0; sum_radii=r+r; for (r = 0.5; r >=0; r-=0.001) { for (float degree = 0; degree < 361; degree+=0.1) { theta =degree*radian_to_degree_theta; xR=r*cos(theta)+distance; yR=r*sin(theta); xG=r*cos(theta)-distance; yG=r*sin(theta); dis=sqrt(pow(xR-xG,2) + pow(yR-yG,2)); if (dis <= sum_radii) { set_point(xR,yR,0.1,1,0,0); set_point(xG,yG,0.1,0,1,0); } } } } void set_point(float x,float y,float size,float R,float G,float B){ glPointSize(size); glBegin (GL_POINTS); glColor3f (R, G, B); glVertex2f(x, y); glEnd (); } void draw(void) { glClearColor (1, 1, 1, 0); glClear (GL_COLOR_BUFFER_BIT); glPushMatrix(); draw_ven(); glPopMatrix (); glFlush(); } int main(int argc, char** argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_SINGLE); glutInitWindowSize(1400, 1400); glutInitWindowPosition(700, 500); glutCreateWindow("GL Sample"); glutDisplayFunc(draw); glutMainLoop(); return 0; }
代码实际运行效果如下,重叠区域未被排除:
重叠区域判定逻辑
两个等半径圆的圆心分别为右圆圆心(distance, 0)、左圆圆心(-distance, 0),半径均为r。
如果一个点同时落在两个圆的内部,它就属于重叠区域,绘制时需要跳过。
点在圆内的判定规则为:点到圆心的距离小于等于圆半径。为避免开方运算的性能损耗,可直接使用距离平方与半径平方比较:
- 对右圆上生成的点,计算它到左圆圆心的距离平方,若该值小于等于半径平方,说明点落在左圆内,属于重叠区。
- 对左圆上生成的点,计算它到右圆圆心的距离平方,若该值小于等于半径平方,说明点落在右圆内,属于重叠区。
原代码的核心错误是误用了两圆相交的判定逻辑:计算同一半径层级下两个生成点的距离与半径和比较,完全不匹配「点是否在另一圆内」的判定要求,因此无法得到正确结果。
修正后实现
核心绘制函数修复
void draw_venn(){ const float PI = 3.1415926f; float radian_to_degree_theta = 2 * PI / 360, r = 0.5, distance = 0.3, theta = 0.0, xR = 0.0, yR = 0.0, xG = 0.0, yG = 0.0, r_sq = r * r; // 预存半径平方,减少重复计算 // 使用独立循环变量,避免修改初始半径r的取值 for (float cur_r = r; cur_r >= 0; cur_r -= 0.001) { for (float degree = 0; degree < 361; degree += 0.1) { theta = degree * radian_to_degree_theta; // 生成右圆当前采样点 xR = cur_r * cos(theta) + distance; yR = cur_r * sin(theta); // 判定点是否在左圆外:外则属于非重叠区,绘制 float dist_to_left_sq = pow(xR + distance, 2) + pow(yR, 2); if (dist_to_left_sq > r_sq) { set_point(xR, yR, 0.1, 0.5, 0.5, 0.5); // 匹配预期的灰色填充 } // 生成左圆当前采样点 xG = cur_r * cos(theta) - distance; yG = cur_r * sin(theta); // 判定点是否在右圆外:外则属于非重叠区,绘制 float dist_to_right_sq = pow(xG - distance, 2) + pow(yG, 2); if (dist_to_right_sq > r_sq) { set_point(xG, yG, 0.1, 0.5, 0.5, 0.5); } } } }
其他需要修正的问题
- 原
draw函数中调用的函数名为draw_ven,与定义的draw_venn不一致,会触发编译错误,需要统一函数名。 - 原代码给左右圆分别设置红、绿颜色,与预期的统一灰色非重叠区效果不符,需要统一颜色值。
内容的提问来源于stack exchange,提问作者fbarikzehy
相关产品推荐
相关产品推荐

