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

Silverlight数学引擎(9)——尺规作图之线上点

2013年05月25日 ⁄ 综合 ⁄ 共 2145字 ⁄ 字号 评论关闭

线上点,这个线可以是直线也可以是曲线,这里统称为线了,因为尺规作图无外乎这两种。按照我们前面对点的分析,线上点是半自由点:

  1. 它可以随着鼠标拖动
  2. 始终位于所在的线上

这就出现一个问题:比如当线的长度或者圆的大小放生变化时,自由点的位置如何变化?我们前面给出的方案是,比例保持不变:

  1. 对于直线是“距离比例不变”(严格说是向量CA:BA不变,因为点可能在线段延长线上)
  2. 对于圆是“角度比例不变”(即圆上点C与参照直径PQ的起点P相对圆心的夹角不变)

如下图所示:

上图中红色点即为鼠标拖动线上点C(黄色)时,鼠标的位置,因为我们不控制鼠标的移动,所以鼠标可以移动到线外,那么要确定C的Ratio,就需要计算了,从上图可以看出,C点其实就是Mouse在直线AB上的投影点(垂足),MouseC就是AB的垂线。对于圆来说C就是直线MouseO和圆的交点。

先来看看直线AB,假设其方程为y=ax+b,那么与它垂直的线的斜率一定是-1/a,(初中学过的啦),可以表示成y=-x/a +b2, 将鼠标的点代入可以求得b2,所以计算C的Ratio就变成了先求两条直线的交点,这一点我们上节刚刚介绍过,直接看看代码吧:

        //线外一点到线的投影(垂足)
        public static LogicalPoint GetProjectionPoint(LogicalPoint point, LogicalLine line)
        {
            if(line.a.IsZero())
                return new LogicalPoint(point.X, line.P1.Y);

            //line:y=ax+b, 则其垂线line2的方程为:y=-x/a+b2,将point坐标代入得b2
            var b2 = point.Y + point.X/line.a;
            var point2 = new LogicalPoint(point.X + 1, -(point.X + 1)/line.a + b2);
            var line2 = new LogicalLine(point, point2);
            //求line、line2的交点:
            return LineAndLine(line2, line);
        }

        //线外一点分线段的向量比
        public static double GetProjectionRatio(LogicalPoint point, LogicalLine line)
        {
            var projectionPoint = GetProjectionPoint(point, line);
            var result = 0.0;
            if (!(line.P1.X - line.P2.X).IsZero())
            {
                result = (projectionPoint.X - line.P1.X)/(line.P2.X - line.P1.X);
            }
            else if (!(line.P1.Y - line.P2.Y).IsZero())
            {
                result = (projectionPoint.Y - line.P1.Y)/(line.P2.Y - line.P1.Y);
            }
            return result;
        }

哈哈,是不是挺简单的,当然还有一种更简单的方式,就是不用垂足的概念,只用鼠标的X来确定交点(意思是MouseC始终与Y轴平行啦),呵呵,我们不用这种方式,怕便宜没好货:)

再来看看圆的情况,就更简单了,其实我们不用采用交点的方式,因为有两个交点,处理起来比较麻烦,我们直接用三角函数确定的了:

        //计算直线AB与水平线(X轴)的夹角
        public static double GetRadians(this LogicalPoint A, LogicalPoint B)
        {
            //不能用Math.Atan因为Atan无法确定方向
            return Math.Atan2((A.Y - B.Y), (A.X - B.X));
        }

        //计算角ABC的弧度
        public static double GetRadians(this LogicalPoint A, LogicalPoint B, LogicalPoint C)
        {
            var r1 = GetRadians(A, B);
            var r2 = GetRadians(C, B);
            return r1 - r2;
        }
        public void UpdateRatio(LogicalPoint point)
        {
            Ratio = point.GetRadians(Circle.Center, Circle.Circle.Radius.P1)/Math.PI/2;
        }

好了,只要确定了规则,线上点实现起来也没那么麻烦,不以规矩不成方圆嘛。来开始我们的测试:

  1. 使用前面创建的元素
  2. 创建一个直线P1P2上的点PointOnLine(黄色)
  3. 创建一个圆PO1上的点PointOnCircle(黄色)
  4. 更改鼠标事件允许拖动黄色的点

编码很简单,都是根据以前的模子刻的,看看运行效果吧:

【源代码和演示地址】

随着点的介绍完毕,尺规作图大三元素的另外两个(直线和圆)也已经水落石出了。做了这么多的分析只是为了理清他们之间的关系,如果用一个词形容这种关系,那就是——依赖,我们只要动一下红色点,其他的点都会自动发生变化,这就是依赖的魔力。如何利用这种依赖使我们的编码和设计工作变得和这个现实世界更加类似呢?显然我们的测试程序没有实现这一点,那么下一节我们将重新梳理一下依赖关系,完成坐标元素的设计,给各个图形元素一个恰当的名分!

 

抱歉!评论已关闭.