如何逆转科恩 - 萨瑟兰算法?

时间:2017-12-19 10:12:15

标签: c#

这是Cohen-Sutherland代码:

public static Tuple<PointF, PointF> ClipSegment(RectangleF r, PointF p1, PointF p2)
        {
            //classify the endpoints of the line
            var outCodeP1 = ComputeOutCode(p1, r);
            var outCodeP2 = ComputeOutCode(p2, r);
            var accept = false;
            while (true)
            { 
                // Case 1!
                if ((outCodeP1 | outCodeP2) == OutCode.Inside)
                {
                    accept = true;
                    break;
                }
                // Case 2!
                if ((outCodeP1 & outCodeP2) != 0)
                {                    
                    break;
                }

                //Case 3!
                var outCode = outCodeP1 != OutCode.Inside ? outCodeP1 : outCodeP2; 
                // calculate the intersection of the line with the clipping rectangle using parametric line equations
                var p = CalculateIntersection(r, p1, p2, outCode);
                // update the point after clipping and recalculate outcode
                if (outCode==outCodeP1)
                {
                    p1 = p;
                    outCodeP1 = ComputeOutCode(p1, r);
                }
                else
                {
                    p2 = p;
                    outCodeP2 = ComputeOutCode(p2, r);
                }
            }
            // if clipping area contained a portion of the line
            if (accept)
            {
                return new Tuple<PointF, PointF>(p1, p2);
            }
            // the line did not intersect the clipping area 
            return null;
        }

案例1:两个端点都在裁剪区域内。

案例2:两个端点共享一个被排除的区域,它们之间的一条线不可能在裁剪区域内。

案例3:端点位于不同的区域,并且该段部分位于剪切矩形内,选择剪切矩形外的一个端点。

我尝试将所有片段都放在剪切窗口之外而不是内部。 有人能帮助我吗?

1 个答案:

答案 0 :(得分:0)

在意识到找到剪切区域之外的段可能产生多个元组并更新方法的返回类型之后,只需要遍历逻辑并反转每个决策:

public static IEnumerable<Tuple<PointF, PointF>> ClipSegment(RectangleF r, PointF p1, PointF p2)
{
    // classify the endpoints of the line
    var outCodeP1 = ComputeOutCode(p1, r);
    var outCodeP2 = ComputeOutCode(p2, r);
    var results = new List<Tuple<PointF,PointF>>();
    while (true)
    { 
        // Case 1:
       // both endpoints are within the clipping region
        if ((outCodeP1 | outCodeP2) == OutCode.Inside)
        {
            //Completely clipped
            break;
        }
        // Case 2:
        // both endpoints share an excluded region, impossible for a line between them to be within the clipping region
        if ((outCodeP1 & outCodeP2) != 0)
        {
            //Not clipped at all
            results.Add(Tuple.Create(p1,p2));                    
            break;
        }
        //Case 3: The endpoints are in different regions, and the segment is partially within
        //the clipping rectangle Select one of the endpoints outside the clipping rectangle

        var outCode = outCodeP1 != OutCode.Inside ? outCodeP1 : outCodeP2; 
        // calculate the intersection of the line with the clipping rectangle using parametric line equations
        var p = CalculateIntersection(r, p1, p2, outCode);
        // update the point after clipping and recalculate outcode
        if (outCode==outCodeP1)
        {
            //Keep "discarded" segment
            results.Add(Tuple.Create(p1,p));
            p1 = p;
            outCodeP1 = ComputeOutCode(p1, r);
        }
        else
        {
            //ditto
            results.Add(Tuple.Create(p,p2));
            p2 = p;
            outCodeP2 = ComputeOutCode(p2, r);
        }
    }
    return results;
}

您可以将调用代码更改为:

var clipped = CohenSutherland.ClipSegment(_rect, p1, p2);
newLines.AddRange(clipped);

(因为我们现在保证始终返回一个列表,即使是空的,也不再需要null检查。)

我们可能希望考虑的一个改进是将函数更改为迭代器并使用yield return而不是在内部维护单独的列表。