## 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 `PolyStar`

s, *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`

.

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.

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.

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.

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:

Matrix innerMat = (Matrix)outerMat.Clone();
innerMat.Multiply(mInnerRadius / mRadius);
PointTransformations.RotationalTransformation rot =
new RotationalTransformation() { Matrix = innerMat };
rot.RotateRadiansAroundOrigin(Math.PI / mSides);
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;
rot.RotateDegreesAroundOrigin(-mBevelOffset / 2);
}

After this, invoke some overall transformations contained in `OverallTransformation`

s 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:

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

The `Rotation`

is not changed, but `IRotated`

is modified just fine. After a while, I tried the following:

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

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

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

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

- 27
^{th} September, 2008: Initial post