用于确定不完整椭圆的开口的代码

时间:2015-02-23 06:58:27

标签: c# algorithm

我有一个我想渲染的截断椭圆。切割部分由与另一个椭圆的交点完成(想象另一个椭圆阻挡原始椭圆)。

参考下面的2个椭圆,其中t1,t2表示参数t1,t2表示我跟踪椭圆,给出点p1,p2。

enter image description here

如何使用数据结构捕获空的部分?我的初始伪代码是这样的:

for(t = 0 to 360 degrees)
{
    if(point_t intersects with ellipse)
    {
        if(P1 is not set) set t corresponding to P1 as start_t;
        else set t corresponding to P2 as end_t
    }
}

然后“截止”区域将由start_t <= t <= end_t给出。

但是,此代码仅适用于第一个图表。在第二个图中,由于“截止”区域对于该范围不连续,因为实际截止区域将表示为

0 <= t <= P1 && P2 <= t <= 360 degrees

我应该如何编写算法来解决所有这些情况?我需要一个精确的定义(例如切断部分的t值范围),因为我需要使用这个值来解决这些截止椭圆的进一步线交叉问题。

3 个答案:

答案 0 :(得分:2)

如果不是真的话,你的图像意味着轴对齐的椭圆在OP

中指定它
  1. 两个椭圆的交集可以创建

    • 0完全在内/外
    • 时切断
    • 1如果在单面交叉时切断
    • 如果在两侧相交,则切断2次
    • 完全切断是两个椭圆是相同的
    • examples
  2. 交叉点

    • 您需要获得所有交叉点
    • 所以要么用代数/数字来解决呢
    • x0+a0*cos(t0)=x1*a1*cos(t1)
    • y0+b0*sin(t0)=y1*b1*sin(t1)
    • 其中:x0,y0为中心,a0,b0为半轴,t0为第一个椭圆的参数
    • 其中:x1,y1为中心,a1,b1为半轴,t1为第二个椭圆的参数
    • 或将问题转换为行的交集
    • lines intersection
    • 通过以恒定角度扫描展位椭圆
    • 将每个椭圆变换为凸多边形
    • 点越多,你得到的交叉越精确
    • 如果线条相互叠加,则选择其共同部分的中间点
  3. 要存储哪些信息?

    • 您需要间隙的参数对或其他起始端形式
    • 因此,对于每个交叉点,您需要确定它是否是间隙的开始或结束
    • 所以你应该有一个交叉点参数列表,最多4个点/角度(按角度排序)
  4. 你可以忽略0,1分案件

  5. 2分是最简单的情况

    • 因此您的交叉角ang0,ang1位于ang0<ang1
    • 所以只需检查点ang0+0.01*(ang0+ang1)是否在第二个椭圆内
    • 如果是,则ang0是差距的开始,ang1结束
    • 所以渲染:
    • for(t=0 ;t<=ang0 ;t+=step)...
    • for(t=ang1;t<=360deg;t+=step)...
    • 如果没有那么它是另一种方式
    • for(t=ang0;t<=ang1 ;t+=step)...
  6. 3分案例很少

    • 当椭圆切割一侧而只接触另一侧
    • 时会发生这种情况
    • 因此找到哪一个交叉角度在每一侧的交叉点之外并忽略它
    • 所以计算dang=0.01*min(|ang1-ang0|,|ang2-ang0|,|ang2-ang1|)
    • 找到ang? +/- dang在交叉点之外的触摸点
    • 然后忽略它并将其余部分作为2点案例处理
  7. 4分案例

    • 所以计算dang=0.01*min(|ang1-ang0|,|ang2-ang0|,|ang3-ang0|,...)
    • 现在查找ang0是否是差距ang0-ang1
    • 的开头
    • 如果是渲染
    • for(t=0 ;t<=ang0 ;t+=step)...
    • for(t=ang1;t<=ang2 ;t+=step)...
    • for(t=ang3;t<=360deg;t+=step)...
    • 如果没有那么
    • for(t=ang0;t<=ang1 ;t+=step)...
    • for(t=ang2;t<=ang3 ;t+=step)...
  8. 相同的省略号

    • 如果两个椭圆具有相同的中心,半轴和旋转
    • 然后切断整个椭圆(无渲染)
  9. 统一

    • 你可以将整个转换成存储非椭圆的椭圆块
    • 作为一组角度对
    • 每个渲染的
    • (start,end)角度为......
  10. [注释]

    • dang可以是足够小的角度步长(必须小于最小间隙)但如果太小那么你的交叉计算可以给出误报
    • 多边形转换甚至适用于非轴对齐的椭圆
    • 如果您使用清扫,那么如果交叉点是间隙的开始或结束,您已经有信息,所以如果知道如何使用它就可以忽略dang
    • 注意角度测量功能的弧度/度......
    • 如果你想用代数方法求解交叉点并想避免超越函数,那么使用隐式椭圆方程代替
    • 渲染时应处理结束角度交叉,例如

      for (t=ang0;t<=ang1;t+=step)
       {
       if (t>=ang1) { t=ang1; e=1; } else e=0;
       ...
       if (e) break
       }
      
    • 如何确定点x,y是否在轴对齐的椭圆x0,y0,a,b内?
    • 首先通过翻译坐标系
    • (x0,y0)转换为(0,0)
    • 所以问题更改为:(x-x0,y-y0)内的0,0,a,b点?
    • 这很简单:
    • ang=atan2(y-y0,x-x0);
    • if ((x-x0<=a*cos(ang))&&(y-y0<=a*sin(ang))) is_inside else is_outside;
    • 如果您有非轴对齐的椭圆,则添加旋转以匹配轴角...

答案 1 :(得分:0)

由于两个椭圆可能最多交叉4次(因此无论如何都会产生两个截止区域),我建议您按照建议运行代码,但将所有结果区间存储在列表中。 / p>

含义:

if(point_t intersects with ellipse)true的第一个值存储为第一个间隔的起始值。继续,直到if(point_t intersects with ellipse)为false并将其设置为结束参数。然后重复一遍。

结果是一个开始/结束值列表,您可以在其中进行迭代以进行进一步计算。此外,如果您需要每个间隔的长度,您可以在事件以0开头的情况下修改列表,以便将例如{340,20}存储为交叉间隔。

答案 2 :(得分:0)

假设您只需要描述一个截止间隔,我建议使用(Begin,Extent)表示而不是(Begin,End)。 Extent参数是0°到360°范围内的值,对角度的跳跃不敏感。您甚至可以为其指定一个符号,以区分顺时针和逆时针遍历。

如果要描述一系列弧,也可以使用类似的技巧:您可以为每个弧提供(Begin,Extent)信息,或者包含交替的(Begin,Extent,Extent,Extent ...)边界/排除弧。