Photoshop 样式的角度/高度选择器控件
Conmajia © 2012
Updated on Mar. 5, 2018
简介
Adobe Photoshop有两个专业的控件:角度选择器和角度与高度选择器,如图1所示.

这么可爱的控件,当然要拿来了. 仿制这两个控件很简单,一点点基本的数学知识就足够应付.
基础知识
勾股定理
勾股定理是老祖宗传下的数学神器. 以图2的直角三角形为例,它可以通过对边 \(c\)、邻边 \(b\) 计算斜边 \(a\),\(a^2=b^2+c^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\) 重合.

三角函数
三个基本的三角函数用于从角度计算各边比值:正弦函数 \(\sin(x)\)、余弦函数 \(\cos(x)\) 和正切函数 \(\tan(x)\),假设图2中 \(\angle BCA=\theta\),那么 \(\sin(\theta)=\dfrac{c}{a}\),\(\cos(\theta)=\dfrac{b}{a}\),\(\tan(\theta)=\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一模一样,需要设置MouseDown
和MouseMove
事件. 这样,各项数值将实时更新. 这里要用到一个辅助函数:
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\)
Photoshop 样式的角度/高度选择器控件的更多相关文章
- iOS:选择器控件UIPickerView的详解和演示
选择器控件UIPickerView: 功能:它能够创建一个类似于密码锁式的单列或多列的选择菜单,用户可以通过它设置的代理来选择需要菜单中的任意的数据.例如创建日历.字体表(类型.大小.颜色).图库等. ...
- Flex 日期选择器控件
在构建用户界面时,经常用到日期的输入和选择. 输入日期时,用户可以使用键盘输入,也可以在类似于日历的弹出式对话框中,通过鼠标单击所选日期. 在Flex中可以通过日期输入控件实现输入和选择日期.Flex ...
- WPF 自定义ComboBox样式,自定义多选控件
原文:WPF 自定义ComboBox样式,自定义多选控件 一.ComboBox基本样式 ComboBox有两种状态,可编辑和不可编辑状态.通过设置IsEditable属性可以切换控件状态. 先看基本样 ...
- WPF自定义控件与样式(5)-Calendar/DatePicker日期控件自定义样式及扩展
一.前言 申明:WPF自定义控件与样式是一个系列文章,前后是有些关联的,但大多是按照由简到繁的顺序逐步发布的等,若有不明白的地方可以参考本系列前面的文章,文末附有部分文章链接. 本文主要内容: 日历控 ...
- 【转】WPF自定义控件与样式(5)-Calendar/DatePicker日期控件自定义样式及扩展
一.前言 申明:WPF自定义控件与样式是一个系列文章,前后是有些关联的,但大多是按照由简到繁的顺序逐步发布的等. 本文主要内容: 日历控件Calendar自定义样式: 日期控件DatePicker自定 ...
- Android UI 统一修改Button控件的样式,以及其它系统控件的默认样式
先介绍下修改原理:首先打开位于android.widget包下面的Button.java文件,这里有一句关键的代码如下: public Button(Context context, Attribut ...
- 安卓开发笔记(二十八):仿写IOS switch选择器控件实现,checkbox
我们先来看看效果: 这里我们主要使用了github上的一个开源项目,配置起来比较方便,下面解释一下该如何使用:首先是:Gradle文件当中进行配置: dependencies { implementa ...
- WPF自定义控件与样式(7)-列表控件DataGrid与ListView自定义样式
一.前言 申明:WPF自定义控件与样式是一个系列文章,前后是有些关联的,但大多是按照由简到繁的顺序逐步发布的等,若有不明白的地方可以参考本系列前面的文章,文末附有部分文章链接. 本文主要内容: Dat ...
- WPF自定义控件与样式(10)-进度控件ProcessBar自定义样
一.前言 申明:WPF自定义控件与样式是一个系列文章,前后是有些关联的,但大多是按照由简到繁的顺序逐步发布的等,若有不明白的地方可以参考本系列前面的文章,文末附有部分文章链接. 本文主要内容: Pro ...
随机推荐
- jsp页面取值
一般就用el表达式 ${recordList[4].baseRate8.split("/")[0] } <s:date name="recordList[#id]. ...
- Spring Cache For Redis
一.概述 缓存(Caching)可以存储经常会用到的信息,这样每次需要的时候,这些信息都是立即可用的. 常用的缓存数据库: Redis 使用内存存储(in-memory)的非关系数据库,字符串.列 ...
- js 判断一个文本框是否获得焦点
1.js 判断一个文本框是否获得焦点 // 可以用document.activeElement判断 // document.activeElement表示当前活动的元素 // 查找你要判断的文本框 ...
- Codeforce D. Make a Permutation!
D. Make a Permutation! time limit per test 2 seconds memory limit per test 256 megabytes input stand ...
- redis通过pipeline提升吞吐量
案例目标 简单介绍 redis pipeline 的机制,结合一段实例说明pipeline 在提升吞吐量方面发生的效用. 案例背景 应用系统在数据推送或事件处理过程中,往往出现数据流经过多个网元: 然 ...
- css模板
最近好多人问我博客的css模板.... 现在是高三,没多少时间,趁放假赶紧更一下 主体就是把博客园的一个模板改动了一点 上面的图片特效,也是从别人那里得到的代码,大致就是下面那些,下面的三个图片换成自 ...
- [偏序关系与CDQ分治]【学习笔记】
组合数学真是太棒了 $CDQ$真是太棒了(雾 参考资料: 1.<组合数学> 2.论文 课件 很容易查到 3.sro __stdcall 偏序关系 关系: 集合$X$上的关系是$X$与$X$ ...
- ubuntu14.04安装、NVIDIA显卡驱动安装及CUDA8.0、Cudnn5.1的环境搭建
安装环境:hp-Z440工作站.64位Ubuntu14.04(64位Ubuntu16.04).Cuda8.0.Cudnn5.1.Nvidia GeForce GT 705.Tesla K40c 本文可 ...
- 微信小程序项目踩过的几个坑
一.前言 近期,开始了一段辛酸的还未开始就已经结束的"创业"(参见我的第二次创业,以梦为马,莫负韶华).大体上是开发了一款微信小程序,关于创业这件事情就不细说了,本文主要介绍一下开 ...
- php get_called_class()函数与get_class()函数的区别
get_class (): 获取当前调用方法的类名: get_called_class():获取静态绑定后的类名: class Foo{ public function test(){ var_dum ...