• 🔧 角度/高度选择器控件 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)

  • 相关阅读:
    loadrunner提高篇
    loadrunner提高篇
    loadrunner提高篇
    STM32F4XX高效驱动篇1-UART
    系统封装接口层 cmsis_os
    uCGUI 按键窗口切换机制
    xming+xshell让linux变成桌面化操作
    jmeter做WebSocket请求解读
    性能监控工具spotlight操作
    linux性能监控命令,磁盘,网络,cpu,内存
  • 原文地址:https://www.cnblogs.com/conmajia/p/angle-altitude-control.html
Copyright © 2020-2023  润新知