Introduction
Adobe Photoshop dispose de deux commandes très professionnelles pour définir des effets spéciaux tels que l'ombre portée, le biseau et le relief : l'une est le sélecteur d'angle et l'autre est le sélecteur d'angle et de hauteur (comme indiqué dans l'image ci-dessus).
Cet article amènera les lecteurs à créer deux contrôles personnalisés pour imiter l'apparence et le comportement de ces deux contrôles dans Photoshop.
Connaissances de base - Mathématiques
Théorème de Pythagore
(c'est-à-dire le théorème de Pythagore, pour respecter le texte original, ci-après dénommé théorème de Pythagore. Bien qu'il soit un peu alambiqué. — —Note de Nobi)
En utilisant le théorème de Pythagore, nous pouvons calculer l'hypoténuse (côté le plus long) d'un triangle rectangle. La formule de calcul est . De cette façon, l’hypoténuse c est égale à .
Cercle unité
Étant donné que le prochain travail est lié aux angles et aux cercles, il est utile pour nous de nous familiariser d'abord avec la forme du cercle unité. Le cercle unité est un cercle de centre (0,0) et de rayon 1. Dans une grille régulière (en référence au canevas - note de Nobi), 0 degrés (les coordonnées) part du point (1,0) (à droite) et augmente dans le sens inverse des aiguilles d'une montre. Par conséquent, 90 degrés correspondent à (0,1), 180 degrés correspondent à (-1,0), 270 degrés correspondent à (0,-1) et enfin 360 degrés coïncident avec le point 0.
Fonctions trigonométriques
Ici, il suffit de connaître trois fonctions trigonométriques de base : sin, cos et tan (sinus, cosinus et tangente - Nobi Note). Si on se souvient de SOH-CAH-TOA, on sait que le sinus d'un triangle (rectangle) est égal au rapport du côté opposé à l'hypoténuse, le cosinus est égal au rapport du côté adjacent à l'hypoténuse, et la tangente est égale au rapport du côté opposé au côté adjacent.
De même, nous savons que les fonctions trigonométriques inverses sont utilisées pour calculer des angles inconnus.
Note de traduction :
SOH-CAH-TOA est une formule utilisée par les étrangers pour mémoriser les fonctions trigonométriques. Parmi eux : O est le côté opposé (côté opposé), H est l'hypoténuse (hypoténuse) et A est le côté adjacent.
SOH : Sinus = Opposé ÷ Hypoténuse
CAH : Cosinus = Adjacent ÷ Hypoténuse
TOA : Tangente = Opposé ÷ Adjacent
Fonctions communes
Les deux fonctions (méthodes) importantes suivantes seront utilisées dans les contrôles personnalisés que nous effectuons :
Une fonction reçoit l'angle et le rayon comme paramètres et renvoie l'angle autour d'une certaine origine. Emplacement du point correspondant. (Pour faire simple, il convertit les angles en points)
Une fonction qui complète l'inverse, en prenant les points (X, Y) comme paramètres pour trouver le meilleur angle correspondant.
La première fonction devrait être plus simple :
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; }
Choses à noter en premier nous devons convertir les angles en radians. D'une manière générale, il suffit d'étudier dans le cercle unité :
Cette fonction connaît l'angle et le rayon, à l'aide de fonctions trigonométriques, on calcule les valeurs X et Y, puis dans Ajoutez simplement les coordonnées initiales données de l’origine.
Vous devez également noter que la valeur négative du composant Y est utilisée dans le code de fonction. En effet, la grille sur l'écran de l'ordinateur est à l'envers (vers le bas est positive).
La fonction de la deuxième fonction est de convertir la position du point sur laquelle l'utilisateur clique sur le contrôle en la valeur d'angle correspondante. C'est un peu plus délicat car il faut penser à ajouter quelques éléments. En raison de la longueur limitée de l'article, je poste une partie du code ici :
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; }
Cette fonction vérifie principalement la position de la souris par rapport au point central. Déterminez son quadrant. Une fois que nous connaissons les quadrants, nous pouvons utiliser la fonction trigonométrique (arctangente) pour calculer l'angle.
Si l'angle est supérieur à 180 degrés, soustrayez 360 degrés. C'est la même chose que Photoshop, contrôlant l'angle entre -180 degrés et 180 degrés. Bien entendu, vous n’avez pas besoin d’effectuer cette étape. Le contrôle peut toujours être utilisé sans ajouter cette ligne de code.
Créer des contrôles
Dessiner des contrôles
Les arrière-plans de ces deux contrôles sont les mêmes :
Dessiner l'extérieur avec un stylo d'une largeur de 2 Le cercle
est rempli de blanc à 40% (environ 100) d'opacité
Le centre du champ est un Carré de 3x3 pixels
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函数返回的点连一条直线即可。角度与高度选择器则是在这点上绘制一个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中文网!