现在的位置: 首页 > 综合 > 正文

Silverlight数学引擎(18)——几何作图游戏

2012年10月02日 ⁄ 综合 ⁄ 共 2762字 ⁄ 字号 评论关闭

在Win7上装了个IE10,虽然有不少诱人的新特性,但是在调试SL项目的时候却发现它对SL的支持有不少的问题,看来哥也要紧跟时代潮流了。本节应该是该系列的封篇之作了,哪天有空了再实现一个Html5版本吧,不过这个SL代码我会不经意地去维护的,比如Bug修复、易用性改善等等都不需要不断地完善。

在前面我们已经实现了基本的画图功能,如点、线、圆等,这些已经是尺规作图的全部功能了。但是要实现快速方便的作图,仅仅靠这三大件是非常低效的,因此我们需要提供更为复杂的一些画图技能,比如多边形、平行线、对称点之类的,我们称为复合图形。本节的主要工作就是对每个复合图形提供一个画图练习题,用户完成了该练习题后就获得相应的画图技能,并且可以使用新的技能去解新的题目,不必仅仅依靠点线圆三大件了。

首先我们整理一下哪些复合图形是必要的,并且根据优先级进行排序,例如中点是中垂线与线的交点,所以位于中垂线之后:

    public enum BehaviorOrder
    {
        中垂线 = 5,
        中点 = 6,
        对称点 = 7,
        外接圆 = 8,
        平行线 = 9,
        垂直线 =10,
        角平分线 = 11,
        None=100,
}

然后提供一个练习题的接口定义:

    public interface IExcercises
    {
        BehaviorOrder BehaviorOrder { get; }//技能
        string Content { get; }//题目内容(xml)
        bool Validate(CoordinateBase shape);//验证策略
        IExcercises Next { get; }//下一个练习
    }

该接口很容易理解,但是不是那么容易实现的,难点就是验证策略不好确定,因为作图方法有很多种,我们不能遍历所有的方法,也不能通过简单坐标计算来判断,因为这有可能遇到偶然情况,比如用户画的点恰好在哪个位置。So,那该咋办呢?在我的练习中,主要验证最简单通用的作图思路,然后通过寻找依赖来判断所做的图是否正确,比如作线段AB的中垂线,最简单的作图方法是这样的:

  1.作以AB(或BA)为半径,A为中心的圆A。

  2.作以AB(或BA)为半径,B为中心的圆B。

  3.作两圆的交点C、D,连接CD,CD即为所求。

那么,验证策略就是根据A、B两点来验证其与线段CD的关系,如果符合,通过验证,代码如下:

        public static bool Validate(PointShape A, PointShape B, LineShape line)
        {
            var CS = A.CS;

            var circleA = CS.FindCircle(A, A, B, false, false) ?? CS.FindCircle(A, B, A, false, false);
            var circleB = CS.FindCircle(B, A, B, false, false) ?? CS.FindCircle(B, B, A, false, false);
            if (circleA == null || circleB == null) return false;
            if (circleA.Parents[0] != A) return false;
            if (circleB.Parents[0] != B) return false;

            if (line.P1.DependentOnAll(false, circleA, circleB) &&
                line.P2.DependentOnAll(false, circleA, circleB))
                return true;

            return false;
        }

可以发现,我将该方法写成了静态的,是为了可以复用,比如画中点的最简单方法就是先画中垂线,然后直接取交点即可。所以我们只要通过以上验证方法找出一条中垂线,然后再进行交点的验证,比如MidPoint的验证方法如下:

        public static bool Validate(PointShape A, PointShape B, IntersectionPointOfLines midPoint)
        {
            var cs = A.CS;
            var AB = cs.FindShapes<LineShape>(false, A, B).FirstOrDefault();
            var circleA = cs.FindCircle(A, A, B, false, false) ?? cs.FindCircle(A, B, A, false, false);
            var circleB = cs.FindCircle(B, A, B, false, false) ?? cs.FindCircle(B, B, A, false, false);
            if (circleA == null || circleB == null) return false;
            if (circleA.Parents[0] != A) return false;
            if (circleB.Parents[0] != B) return false;

            var ps = cs.FindShapes<IntersectionPointOfCircles>(false, circleA, circleB);
            if (ps.Count >= 2)
            {
                var CD = cs.FindShapes<LineShape>(false, ps[0], ps[1]).FirstOrDefault();
                if (CD != null && midPoint.DependentOnAll(false, AB, CD))
                    return true;
            }

            return false;
        }

OK,验证策略有了,那么还有一个问题:何时验证呢?当然也有不少方法,比如每作一个图就遍历验证一下,这样的效率毕竟很差,我们可以最大限度的缩小要验证对象的范围,通过Behavior传入是一个不错的方案,实现代码如下:

    public abstract class BehaviorBase : IBehavior
    {
    //……
        public static IExcercises Excercise;
        
        public void Finished(CoordinateBase shape)
        {
            if(Excercise!=null)
            {
                if (Excercise.Validate(shape))
                {
                    Behaviors.Where(b => b.Order == Excercise.BehaviorOrder).First().Host.Visibility
                        = Visibility.Visible;
                    Excercise = Excercise.Next;
                    if (Excercise != null)
                    {
                        MessageBox.Show("恭喜你成功过关!");
                        ShapeSerializer.Parse(Excercise.Content, CS);
                    }
                    else
                    {
                        MessageBox.Show("闯关结束!");
                    }
                }
            }
        }
    }

【源代码和演示地址】

 http://www.diyuexi.com/webpages/query/ShareRes.aspx

抱歉!评论已关闭.