Conmajia

Stop stealing sheep!

导航

< 20253 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

统计

🔧 角度/高度选择器控件 for .NET

Conmajia © 2012
Updated on Mar. 5, 2018

简介

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

1 两种控件外观

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

基础知识

勾股定理

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

2 一个直角三角形

单位圆

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

3 一个单位圆

三角函数

三个基本的三角函数用于从角度计算各边比值正弦函数 sin(x)余弦函数 cos(x) 和正切函数 tan(x)假设图2BCA=θ那么 sin(θ)=cacos(θ)=batan(θ)=cb. 与它们对应的反三角函数则是已知比值计算角度.

实现

点和角度的函数

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

第一个函数 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则减去 360. 这样就和Photoshop一样把角度控制在 180180 之间.

绘制控件

这两个控件的背景相同

  • 用宽度为2Pen绘制外圈圆
  • 用 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.

posted on2018-03-06   Conmajia  阅读(867)  评论(0编辑  收藏  举报

编辑推荐:
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示