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

PolyStar - WPF 多边形和星形

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.88/5 (30投票s)

2008年9月27日

CPOL

4分钟阅读

viewsIcon

84907

downloadIcon

2003

用于创建各种星形和多边形的控件和工具。

Application Image

引言

本文包含一个用于生成多边形和星形的 Shape 类,以及一个允许我们预览这些对象的编辑器。这始于我发现需要一个“旋转的小东西”来在漫长的过程中显示。一个几何形状演变而来,随之而来的是一个设计应用程序。

本文的大部分内容将介绍用于创建星形和多边形(下称“多星形”)的几何变换的实现。在此过程中,我遇到了 DependencyProperty 类实现中的一些奇怪之处。希望我的经验能有所帮助。

虽然理解是一个很棒的事情,但在检查其内部工作原理之前,您可以使用 PolyStar 类和设计应用程序。您可以将 PolygonImageLib 的引用添加到您的项目中,使用 Generator 工具构建多边形,然后获取 XAML 并将其粘贴进去,无论是作为 PolyStar 还是 Polygon

Assemblies

代码包含三个程序集。PolygonBuilder 包含显示 PolyStars 的代码,PolygonImageLib 包含 PolyStar 的类,PointTransformations 包含处理多边形中点的基本类。PointTransformations 中的类大多来自我上一篇文章。

多边形和星形

多边形可以被认为是沿着由半径和点/边数指定的圆的等角点集。生成星形和多边形的代码在 PolyMatrixBuilder 中。

if (mShapeType == EShapeType.Polygon)
{
    Matrix mat = new Matrix(2);
    for (int i = 0; i < mSides; i++)
    {
        double x = mRadius * Math.Cos((2 * Math.PI / mSides) * i);
        double y = mRadius * Math.Sin((2 * Math.PI / mSides) * i);
        mat.AddRow(new Double[] { x, y });
    }

星形要复杂一些。定义星形的一种方法是将其视为两个具有相同边数、相同原点但半径不同的多边形,但内侧多边形是旋转过的。

为了创建星形,我们克隆点矩阵,旋转它们,然后将两个矩阵交错。请注意,内半径可以为负数,这会产生有趣的效果,特别是当负内半径绝对值略小于半径时。界面设置为负值需要手动输入,而不是从滑块获取。

角度偏移

角度偏移可以被认为是内侧多边形相对于正常星形位置的旋转量。

请注意,它可以是正数或负数。另外,如果偏移量大于 360/(边数) 度,则几何形状会发生显著变化。

斜角

斜角通常被认为是削平形状的角。严格来说,我应该说倒角,但这不常用。还有另一种思考斜角的方式。我们可以复制点集,稍微旋转它,然后交错两个点集。这就是我采用的方法。

当斜角为负数,或者对于多边形角度大于 360/(边数) 度,或者对于星形角度大于 180/(边数) 度时,我们会得到有趣的效果。

星形生成代码

将代码中的所有几何概念结合起来,我们得到星形如下:

Matrix innerMat = (Matrix)outerMat.Clone();
// Will work because centered about origin
innerMat.Multiply(mInnerRadius / mRadius); 
PointTransformations.RotationalTransformation rot = 
    new RotationalTransformation() { Matrix = innerMat };
rot.RotateRadiansAroundOrigin(Math.PI / mSides); //Circle is 2PI radians 
if (mIsAngularlyOffset)
{
    rot.RotateDegreesAroundOrigin(mAngularOffset);
}
outerMat.Interleave(innerMat, false);

if (mIsBeveled )
{
    Matrix bevelmat = (Matrix)outerMat.Clone();
    rot.Matrix = bevelmat;
    rot.RotateDegreesAroundOrigin(mBevelOffset);
    outerMat.Interleave(bevelmat, false);
    rot.Matrix = outerMat;
    //Remove appearance of rotation 
    rot.RotateDegreesAroundOrigin(-mBevelOffset / 2);
}

之后,调用 OverallTransformations 中的一些总体变换来旋转形状,或在垂直或水平方向上将其压扁。这样做是为了方便维护。

依赖属性

我创建 PolyStar 的动机是动画化它。这要求大多数属性都成为依赖属性。一切都进展顺利,只有一些小麻烦。请注意,如果您有一个 double 属性,您需要将零值设置为 0.0 而不是 0。它不会自动为您转换,并且在创建对象的 XAML 中会崩溃,而不是在错误赋值点崩溃。

当我决定当 IsRotated 设置为 false 时,也将 Rotation 的值设置为 0 会更好时,出现了问题。我天真地认为以下代码会起作用:

set
{
    SetValue(IsRotatedProperty, value);
    Rotation= 0;
}

Rotation 没有改变,但 IRotated 得到了很好的修改。过了一段时间,我尝试了以下方法:

set
{
    double[] x = { 0 };
    double y = x[4];
    SetValue(IsRotatedProperty, value);
    double z = x[4];
}

这应该会生成一个运行时错误,但并没有。显然,编译器只是忽略了 set 语句中的所有内容,除了 SetValue。但是,如果存在语法错误,它将无法编译。如果您想在属性设置时执行某些操作,则需要使用回调。在 set 区域中放置代码将不起作用。

这是一个有效的属性版本。

静态辅助函数

private static DependencyProperty DependencyProp
    (string name, System.Type type,object defaultValue){
    FrameworkPropertyMetadata fpm = new FrameworkPropertyMetadata
    ( defaultValue, FrameworkPropertyMetadataOptions.AffectsRender);
    DependencyProperty dp = DependencyProperty.Register
    (name, type , typeof(PolyStar), fpm);
    return dp;
}

private static DependencyProperty DependencyProp
    (string name, System.Type type, object defaultValue,
    PropertyChangedCallback callback)
{
    FrameworkPropertyMetadata fpm = new FrameworkPropertyMetadata
       (defaultValue, FrameworkPropertyMetadataOptions.AffectsRender,callback);
    DependencyProperty dp = DependencyProperty.Register
        (name, type, typeof(PolyStar), fpm );
    return dp;
}

属性定义

public static DependencyProperty IsRotatedProperty = 
       DependencyProp("IsRotated", typeof(bool), false, IsRotatedCallBack);
public bool IsRotated
{
    get { return (bool)GetValue(IsRotatedProperty ); }
    set
    {
        SetValue(IsRotatedProperty, value);
    }
}

最后是回调

static void IsRotatedCallBack(DependencyObject property, 
       DependencyPropertyChangedEventArgs args)
{
    if (!(bool)args.NewValue)
    {
        PolyStar pTemp = (PolyStar)property;
        pTemp.Rotation  = 0;
    }
}

结论

还有其他关于技术实现的细节可能很有趣。IValueConverter<t> 值得一看。MainWindow.xaml 以一种有益的方式使用了 VisualBrush,这是我一直怀疑自己是否会做到的事情。我写这篇文章的目的是创建一个工具,让人们能够轻松地将有趣的多边形和星形添加到他们的应用程序中。我希望我成功了。

历史

  • 2008 年 9 月 27 日:初次发布
© . All rights reserved.