zoukankan      html  css  js  c++  java
  • 🔧 角度/高度选择器控件 for .NET

    Conmajia © 2012
    Updated on Mar. 5, 2018

    简介

    Adobe Photoshop有两个专业的控件:角度选择器角度与高度选择器,如图1所示.

    图1 两种控件外观

    这么可爱的控件,当然要拿来了. 仿制这两个控件很简单,一点点基本的数学知识就足够应付.

    基础知识

    勾股定理

    勾股定理是老祖宗传下的数学神器. 以图2的直角三角形为例,它可以通过对边 (c)邻边 (b) 计算斜边 (a)(a^2=b^2+c^2).

    图2 一个直角三角形

    单位圆

    如图3所示的单位圆是以 ((0,0)) 为圆心,半径为 (1) 的圆. 单位圆上,角度 (0^circ) 的点从 ((1,0)) 开始,按逆时针方向沿单位圆移动. 因此,(90^circ)((0,1))(180^circ)((-1,0))(270^circ)((0,-1))(360^circ)(0^circ) 重合.

    图3 一个单位圆

    三角函数

    三个基本的三角函数用于从角度计算各边比值:正弦函数 (sin(x))、余弦函数 (cos(x)) 和正切函数 ( an(x)),假设图2中 (angle BCA= heta),那么 (sin( heta)=dfrac{c}{a})(cos( heta)=dfrac{b}{a})( an( heta)=dfrac{c}{b}). 与它们对应的反三角函数则是已知比值计算角度.

    实现

    点和角度的函数

    下面这两个函数用于计算点和角度,这对我要完成的两个控件来说很重要. 一个函数把角度转换为点,另一个完成相反的功能,把点转换为角度.

    第一个函数 DegreesToXY

    private PointF DegreesToXY(float degrees, float radius, Point origin) 
    { 
      PointF xy = new PointF(); 
      double radians = degrees * Math.PI / 180.0; 
      xy.X = (float)Math.Cos(radians) * radius + origin.X; 
      xy.Y = (float)Math.Sin(-radians) * radius + origin.Y; 
      return xy; 
    } 
    

    注意

    代码中用到的是 $-y$,这是因为在 GDI+ 的画布上,$y$ 轴向下为正方向.

    第二个函数 XYToDegrees

    private float XYToDegrees(Point xy, Point origin) 
    { 
      double angle = 0.0; 
      if (xy.Y < origin.Y) 
      { 
        if (xy.X > origin.X) 
        { 
          angle = (double)(xy.X - origin.X) / (double)(origin.Y - xy.Y); 
          angle = Math.Atan(angle); 
          angle = 90.0 - angle * 180.0 / Math.PI; 
        } 
        else if (xy.X < origin.X) 
        { 
          //如此这般 
        } 
      } 
      else if (xy.Y > origin.Y) 
      { 
        //如此这般 
      } 
      if (angle > 180) angle -= 360; //控制角度范围 
      return (float)angle; 
    } 
    

    这个函数通过检查鼠标相对中心点的位置,确定它所在象限. 一旦知道了象限,就可以利用反三角函数计算出角度. 如果角度大于 (180^circ),则减去 (360^circ). 这样就和Photoshop一样,把角度控制在 (-180^circ)(180^circ) 之间.

    绘制控件

    这两个控件的背景相同:

    • 用宽度为2的Pen绘制外圈圆
    • 用 40% 不透明度的白色填充
    • 控件中心是 3×3 像素的正方形
    protected override void OnPaint(PaintEventArgs e) 
    { 
      //... 
       
      //Draw 
      g.SmoothingMode = SmoothingMode.AntiAlias; 
      g.DrawEllipse(outline, drawRegion); 
      g.FillEllipse(fill, drawRegion); 
       
      //...
       
      g.SmoothingMode = SmoothingMode.HighSpeed;  
      g.FillRectangle(Brushes.Black, originSquare); 
       
      //... 
    } 
    

    在绘制圆圈时把SmoothMode属性设置为AntiAlias(抗锯齿),这样看起来既光滑又专业. 但是如果画正方形时也用抗锯齿,就会显得模糊难看,所以画正方形的时候要把SmoothMode改为HighSpeed(高速),这样画出的正方形边缘整齐犀利. 根据控件不同,光标也有不同绘制方法,不多说. 角度选择器比较简单,只需要从圆心到DegreesToXY函数返回的点连一条直线即可. 角度与高度选择器则是在这点上绘制一个 1×1的矩形,然后在周围绘制一个十字型光标.

    处理用户点击

    有了XYToDegrees函数,处理用户点击变得特别简单. 为了让控件用起来和Photoshop一模一样,需要设置MouseDownMouseMove事件. 这样,各项数值将实时更新. 这里要用到一个辅助函数:

    private int findNearestAngle(Point mouseXY) 
    { 
      int thisAngle = (int)XYToDegrees(mouseXY, origin); 
      if (thisAngle != 0) 
        return thisAngle; 
      else 
        return -1; 
    } 
    

    高度控件需要额外的处理,就是找到中心点和鼠标点击点的距离:

    private int findAltitude(Point mouseXY) 
    { 
      float distance = getDistance(mouseXY, origin); 
      int alt = 90 - (int)(90.0f * (distance / origin.X)); 
      if (alt < 0) alt = 0; 
      return alt; 
    } 
    

    在Photoshop中,鼠标点击在圆心时,高度为90,在边缘处则为0. 这样,可以通过找到点击点到圆心距离和半径高度比值来计算出高度. 然后,用90减去这个值.

    自定义事件

    为了让控件更加专业,需要控件能够在数值发生变化时以编程方式进行提醒. 例如,像这样给角度变化添加一个事件:

    public delegate void AngleChangedDelegate(); 
    public event AngleChangedDelegate AngleChanged; 
    

    每次变更Angle属性时,调用AngleChanged()就可以触发这个事件了.

    下载

    Demo:点击下载

    源代码:点击下载

    The End. (Box)

  • 相关阅读:
    LeetCode15 3Sum
    LeetCode10 Regular Expression Matching
    LeetCode20 Valid Parentheses
    LeetCode21 Merge Two Sorted Lists
    LeetCode13 Roman to Integer
    LeetCode12 Integer to Roman
    LeetCode11 Container With Most Water
    LeetCode19 Remove Nth Node From End of List
    LeetCode14 Longest Common Prefix
    LeetCode9 Palindrome Number
  • 原文地址:https://www.cnblogs.com/conmajia/p/angle-altitude-control.html
Copyright © 2011-2022 走看看