소개
Adobe Photoshop에는 그림자, 경사 및 릴리프와 같은 특수 효과를 설정하기 위한 두 가지 매우 전문적인 컨트롤이 있습니다. 하나는 각도 선택기이고 다른 하나는 각도 및 높이 선택기입니다(위 이미지 참조).
이 기사에서는 독자가 Photoshop에서 이 두 컨트롤의 모양과 동작을 모방하기 위해 두 개의 사용자 정의 컨트롤을 만들도록 안내합니다.
기초지식 - 수학
피타고라스 정리
(즉, 피타고라스 정리, 원문을 존중하기 위해 이하 피타고라스 정리라고 합니다. 조금 복잡하긴 하지만 — —Nobi's note)
피타고라스의 정리를 이용하면 직각삼각형의 빗변(가장 긴 변)을 계산할 수 있습니다. 계산식은 입니다. 이런 식으로 빗변 c는 과 같습니다.
단위원
다음 작업은 각도와 원에 관한 것이므로 먼저 단위원의 형태를 익히는 것이 도움이 됩니다. 단위원은 중심이 (0,0)이고 반지름이 1인 원입니다. 일반 그리드(캔버스 참조 - Nobi의 메모)에서는 0도(좌표)가 (1,0)(오른쪽) 지점에서 시작하여 시계 반대 방향으로 증가합니다. 따라서 90도는 (0,1), 180도는 (-1,0), 270도는 (0,-1), 마지막으로 360도는 0점과 일치합니다.
삼각 함수
여기에서는 sin, cos 및 tan(사인, 코사인 및 탄젠트 - Nobi Note)의 세 가지 기본 삼각 함수만 알면 됩니다. SOH-CAH-TOA(주석 +)를 기억하면 (직각) 삼각형의 사인은 빗변에 대한 반대쪽의 비율과 같고, 코사인은 인접한 변의 비율과 같다는 것을 알 수 있습니다 빗변에 대한 접선은 대변과 인접 변의 비율과 같습니다.
마찬가지로, 우리는 역삼각 함수가 미지의 각도를 계산하는 데 사용된다는 것을 알고 있습니다.
주석+:
SOH-CAH-TOA는 외국인들이 삼각함수를 외울 때 사용하는 공식입니다. 그 중 O는 대변(대변), H는 빗변(빗변), A는 인접변입니다.
SOH: 사인 = 반대 ¼ 빗변
CAH: 코사인 = 인접 ¼ 빗변
TOA: 접선 = 반대 ¼ 인접
공통 함수
우리가 만드는 사용자 정의 컨트롤에는 다음 두 가지 중요한 함수(메서드)가 사용됩니다.
함수는 각도와 반경을 매개변수로 받고 특정 원점 주위의 각도를 반환합니다. 대응점 위치. (간단히 말하면 각도를 점으로 변환해줍니다)
점(X, Y)을 Parameter로 하여 가장 잘 어울리는 각도를 찾아 반대말을 완성하는 함수입니다.
첫 번째 기능은 더 간단합니다.
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; }
먼저 각도를 라디안으로 변환해야 합니다. 일반적으로 단위원만 공부하면 됩니다.
이 함수는 각도와 반경을 알고 삼각 함수를 사용하여 X와 Y 값을 계산한 다음 주어진 원점의 초기 좌표를 추가하기만 하면 됩니다.
또한 함수 코드에 Y 구성 요소의 음수 값이 사용된다는 점에 유의해야 합니다. 이는 컴퓨터 모니터의 그리드가 거꾸로 되어 있기 때문입니다(아래쪽이 양수임).
두 번째 함수의 기능은 사용자가 컨트롤을 클릭한 점 위치를 해당 각도 값으로 변환하는 것입니다. 몇 가지를 추가하는 것에 대해 생각해야 하기 때문에 이것은 조금 더 까다롭습니다. 기사의 길이가 제한되어 있기 때문에 코드의 일부를 여기에 게시했습니다:
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과 동일하며 -180도에서 180도 사이에서 각도를 제어합니다. 물론 이 단계를 수행할 필요는 없습니다. 이 코드 줄을 추가하지 않고도 컨트롤을 사용할 수 있습니다.
컨트롤 만들기
그리기 컨트롤
두 컨트롤의 배경은 동일합니다.
펜으로 바깥쪽 그리기 너비가 2인 원
은 불투명도가 40%(약 100)인 흰색으로 채워집니다.
컨트롤의 중앙은 3x3 픽셀 정사각형
注意SmoothMode属性。在绘制圆圈时将该属性设置为AntiAlias(抗锯齿),这样看起来既光滑又专业。但是如果画正方形时也用抗锯齿,就会显得模糊难看,所以将SmoothMode设置为HighSpeed(高速),这样画出的正方形边缘整齐犀利。
根据控件不同,光标也有不同绘制方法。角度选择器比较简单,只需要从圆心到DegreesToXY函数返回的点连一条直线即可。角度与高度选择器则是在这点上绘制一个1x1的矩形,然后在周围绘制一个十字型光标。
处理用户点击
多亏我们有了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减去该值(实际上是按90到0来翻转一下)。
自定义事件
为了让我们的自定义控件更加专业,需要控件能够在数值发生变化时以编程方式进行提醒。这就是我们要设置事件的原因。
例如,像这样给角度变化添加一个事件:
public delegate void AngleChangedDelegate(); public event AngleChangedDelegate AngleChanged;
然后,我们要做的就是每次变更Angle属性时,调用AngleChanged()(需要先判断是否为null)。
限制与改进
闪烁
没有闪烁!只需要在制作控件时设置DoubleBuffered属性为true,.NET Framework 2.0会处理剩下的工作,保证控件能流畅的重绘。
尺寸
因为控件使用基于半径(圆)的数学计算方法,因此需要保证控件的长度和宽度相等。
颜色
我是照着Photoshop的样子来的,所以并没包含背景颜色、外圈颜色这些属性。但是,浏览下代码,你会发现改成你喜欢的颜色或者让颜色可以动态修改并不是什么难事。
结论
我建议你下载项目文件(或者至少下载DEMO),这样你可以看到这俩控件用起来很爽。
协议
本文及有关代码、程序均基于CPOL(Codeproject Open License)协议。
更多Photoshop样式的角度和高度选择器控件 相关文章请关注PHP中文网!