65.9K
CodeProject 正在变化。 阅读更多。
Home

Photoshop 风格的角度和高度选择器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.97/5 (25投票s)

2008年4月26日

CPOL

6分钟阅读

viewsIcon

62052

downloadIcon

1734

具有 Photoshop 角度选择器外观和功能的 C# 自定义控件。

AngleAltitudeControls_Sample

引言

Adobe Photoshop 有两个非常专业的控件,用于“投影”和“斜面和浮雕”等效果:一个是角度选择器控件,另一个是角度和高度选择器控件。

在本文中,我们将通过创建两个 C# 自定义控件来模仿 Photoshop 中控件的外观和行为。

基础 - 需要了解的数学知识

勾股定理

勾股定理可以帮助我们计算直角三角形的斜边(最长的那条边)。公式是 a2 + b2 = c2。因此,斜边 c 始终等于 a2 + b2 的平方根。

单位圆

由于我们将处理角度和圆,因此了解单位圆的格式将非常有帮助。单位圆就是一个以常规网格上的点 (0, 0) 为中心,半径为 1 的圆。角度 0 从点 (1, 0) [右侧] 开始,逆时针增加。因此,角度 90 位于 (0, 1),角度 180 位于 (-1, 0),角度 270 位于 (0, -1),而 360 又回到了 0 的位置。

三角测量

我们只需要三个基本的三角函数:sincostan。如果我们记住 *SOH CAH TOA*,我们就知道在三角形中,正弦等于对边比斜边,余弦等于邻边比斜边,正切等于对边比邻边。

还要记住,反三角函数用于计算未知角度。

常用函数

我们的两个自定义控件将共享两个重要函数

  • 一个函数用于接收角度和半径,并返回围绕指定原点的圆上的相应 Point 位置。(基本上,将角度转换为点。)
  • 另一个函数用于做相反的操作,接收一个点 (X, Y) 并找到最接近的角度。

第一个函数是两者中比较简单的

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;
}

需要注意的是,我们首先需要将角度转换为弧度。(将度转换为弧度。)但是,为了获得总体概念,我们只需要可视化我们的单位圆

unitcircle.gif

该函数有角度和半径,因此使用三角学,我们可以计算出 XY 值,就像它们构成一个三角形一样。为了将值居中到指定原点,我们只需要加上原点的初始坐标。

还请注意,该函数使用角度的相反值作为 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)
    {
      //etc.
    }
  }
  else if (xy.Y > origin.Y)
  {
    //etc.
  }
  if (angle > 180) angle -= 360;
  return (float)angle;
}

该函数的主要思想是检查鼠标位置与控件中心的关系,以确定该点位于单位圆的四个象限中的哪一个。一旦我们知道了象限,我们就可以使用三角学(切线的反函数)来计算角度。

对于这些控件,如果角度大于 180 度,我就减去 360 度。这使得角度保持在 -180180 之间,就像 Photoshop 一样。不过,这是可选的,没有这一行代码控件也能正常工作。

构建控件

绘制控件

两个控件的背景将相同

  • 2 像素宽的 Pen 绘制圆形的轮廓。
  • 使用 40% 不透明度(大约 100 值)的白色填充。
  • 在控件中心绘制一个 3x3 像素的正方形。
protected override void OnPaint(PaintEventArgs e)
{
  //...

  //Draw
  g.SmoothingMode = SmoothingMode.AntiAlias;
  g.DrawEllipse(outline, drawRegion);
  g.FillEllipse(fill, drawRegion);

  //...Marker

  g.SmoothingMode = SmoothingMode.HighSpeed; 
  g.FillRectangle(Brushes.Black, originSquare);

  //...
}

需要注意的重要一点是 SmoothingMode 属性。绘制圆形时将其设置为 AntiAlias,可以使控件看起来平滑且专业。但是,如果我们使用 AntiAlias 绘制正方形,边缘会变得模糊。解决方法是将 SmoothingMode 设置回 HighSpeed,使边缘重新变得清晰。

绘制标记部分取决于控件。简单的角度选择器只需要创建一条从原点到我们 DegreesToXY 函数返回的点的线。而角度和高度选择器将在该点绘制一个 1x1 的矩形,然后绘制四个十字标记。

处理用户点击

感谢我们的 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 中减去,以有效地“翻转”这些值(900 距离处)。

自定义事件

为了使自定义控件更加专业,它们需要能够以编程方式在值发生更改时发出警报。这就是自定义事件的作用。

例如,要创建一个角度更改时的事件,首先定义如下

public delegate void AngleChangedDelegate();
public event AngleChangedDelegate AngleChanged;

然后,我们只需要在自定义控件的 Angle 属性发生更改时调用 AngleChanged()(并确保它不为 null)。

局限性和改进

闪烁

完全没有!.NET Framework 2.0 及更高版本提供了 DoubleBuffer 属性,在构造控件时只需将其设置为 true 即可。Framework 会负责处理,确保控件平滑重绘。

大小

由于两个控件都依赖于假设半径始终相等的数学计算,因此控件的宽度和高度尺寸必须始终相等,这样控件才能正确显示和工作。

颜色

我没有为控件的背景和轮廓颜色包含属性,因为我追求的是 Photoshop 的外观。但是,请查看代码,您会发现更改为您喜欢的颜色,甚至使其动态化,并不会太难。

结论

我建议您下载项目文件(或至少是示例应用程序),以便您可以看到这些控件是如何完美组合在一起的。

至于控件的用途和应用,那就取决于您的想象力了。

© . All rights reserved.