Click here to Skip to main content
15,860,972 members
Articles / Desktop Programming / WPF
Article

PolyStar - WPF Polygons and Stars

Rate me:
Please Sign up or sign in to vote.
4.88/5 (32 votes)
27 Sep 2008CPOL4 min read 81.8K   2K   44   13
Control and tool for the creation of various stars and polygons.

Application Image

Introduction

This article is comprised of a Shape class for generating polygons and stars as well as a designer that enables us to preview these objects. This started when I found myself in need of a “Spinny Thingy” to display during a long process. A geometric shape evolved, and a designer application along with it.

Most of this article will cover the implementing of the geometrical transformations to create the stars and polygons (Polystars, henceforth). Along the way, I ran afoul of some oddities in the implementation of the DependencyProperty class. Hopefully, my experience can be of help.

Although understanding is a great and wonderful thing, the PolyStar class and the design application can be used prior to examining their inner workings. You can put a reference to PolygonImageLib in you project, construct the polygon with the Generator tool, and then get the XAML and paste it in, either as a PolyStar or as a Polygon.

Assemblies

The code consists of three assemblies. PolygonBuilder contains the code for displaying the PolyStars, PolygonImageLib contains the classes for the PolyStar, and PointTransformations contains the basic classes for manipulating the points in the polygons. The classes in PointTransformations have mostly come from my last article.

Polygons and Stars

Polygons can be thought of as an equiangular set of points along a circle specified by a radius and a number of points/sides. The code to generate both stars and polygons is in PolyMatrixBuilder.

C#
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 });
    }

Stars are a bit more complicated. One of the ways of defining a star is to think of it as two polygons with the same number of sides and the same origin but different radii, but with the inner one rotated.

Image 2

To create the star, we clones the matrix of points, rotate them, and interleave the two matrices. Note that the inner radius can be negative, which makes an interesting effect, especially when the negative inner radius number is just a little smaller, absolute value wise, than the radius. The interface is set up so that negative values need to be typed rather than come from the slider.

Angular Offset

The angular offset can be thought of as the amount that the inner polygon is rotated away from where it would be in a normal star.

Image 3

Note that it can be either positive or negative. Also, if the offset is greater than 360/(number of sides) in degrees, the geometry changes significantly.

Bevel

A Bevel is usually thought of as lopping off the corners of a shape. Technically, I should speak of chamfers, but that is not common usage. There is another way to think of a bevel. We could make a copy of the set of points, rotate it slightly, and then interleave the two sets of points. This is the approach that I took.

Image 4

We get interesting effects when the bevel angle is negative, or when the angle is larger than 360/(number of sides) degrees for a polygon, or 180/(number of sides) degrees for a star.

Star Generation Code

Combining all of the geometrical notions in the code, we get the following for a star:

C#
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);
}

After this, invoke some overall transformations contained in OverallTransformations to rotate the shape, or flatten it in either the vertical or horizontal directions. This was done to ease maintenance.

Dependency Properties

My motivation for creating the PolyStar was to animate it. That requires that most of the properties be dependency properties. All went well with a few minor irritations. Please note that if you have a double property, you need to set the default to 0.0 rather than 0 for the value of zero. It will not cast it for you, and will also crash in the XAML where the object is created, rather than at the point of the faulty assignment.

Problems occurred when I decided that it would be better if setting IsRotated to false would also set the value of Rotation to 0. Naively, I thought the following code would work:

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

The Rotation is not changed, but IRotated is modified just fine. After a while, I tried the following:

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

This should generate a runtime error, but does not. Apparently, the compiler just ignores everything in the set statement, except SetValue. However, if there is a syntax error, it will fail to compile. If you want to do something when the property is set, you will need to use a callback. Putting code in the set area will not work.

Here is a version of the property that works.

Static Helper Functions

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

Property Definition

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

And Finally, the Callback

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

Conclusion

There are other details about the technical implementation that might be interesting. IValueConverter<t> is worth looking at. MainWindow.xaml uses the VisualBrush in a productive way, something I had doubts that I would ever do. My purpose in writing this article was to make a tool so that people could easily add interesting polygons and stars to their applications. I hope to have succeeded.

History

  • 27th September, 2008: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
Written software for what seems like forever. I'm currenly infatuated with WPF. Hopefully my affections are returned.

Comments and Discussions

 
QuestionThanks! Pin
gardnerp7-Mar-13 9:45
gardnerp7-Mar-13 9:45 
General5 PolyStars! Pin
mikkojay24-Aug-09 11:39
mikkojay24-Aug-09 11:39 
Thanks Ken!
So did you ever animate this? I played around with a version (total hack on my part) where I used a signal generator class I found on the web to oscillate the size of some of the parameters automatically. The end result was much like a kaleidoscope & quite hypnotic! I tore that project down and plan to rebuild it somewhat cleaner. If I come up with anything really off the wall, I'll send you a message.
Thanks again,
Mike
QuestionPossible project? Pin
Noemata4-Aug-09 4:18
Noemata4-Aug-09 4:18 
AnswerRe: Possible project? Pin
KenJohnson4-Aug-09 19:48
KenJohnson4-Aug-09 19:48 
GeneralExcelent! Pin
Dragoljub18-Feb-09 23:59
Dragoljub18-Feb-09 23:59 
GeneralVery pretty :-) Pin
Colin Eberhardt29-Sep-08 0:37
Colin Eberhardt29-Sep-08 0:37 
GeneralAn idea for you Pin
Sacha Barber27-Sep-08 20:56
Sacha Barber27-Sep-08 20:56 
GeneralRe: An idea for you Pin
mtonsager28-Sep-08 2:07
mtonsager28-Sep-08 2:07 
GeneralRe: An idea for you Pin
Leblanc Meneses29-Sep-08 19:02
Leblanc Meneses29-Sep-08 19:02 
GeneralCool Pin
The Cake of Deceit27-Sep-08 9:58
The Cake of Deceit27-Sep-08 9:58 
GeneralVery cool but Pin
Sacha Barber27-Sep-08 9:13
Sacha Barber27-Sep-08 9:13 
AnswerRe: Very cool but Pin
KenJohnson27-Sep-08 16:02
KenJohnson27-Sep-08 16:02 
GeneralRe: Very cool but Pin
Sacha Barber27-Sep-08 20:54
Sacha Barber27-Sep-08 20:54 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.