Saturday, September 6, 2008

You say NURB I say Poly Quadratic Bezier curves

A small step forward from the last post, to using multi control segment lines.

Looking at PolyQuadraticBezierSegment you might think it allows multiple control points - unfortunately it should really be called QuadraticBezierPolySegments, its a collection of segments with their own distinct control points rather a one segment with multiple control points.

Having said that it still allows you to create:



(use the "+" key to add new segments)

The XAML is very similar, though I have removed the points as they are created dynamically by the C# code now:


<UserControl x:Class="DragDropMulti.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="400" Height="300">
<Grid x:Name="LayoutRoot" Background="White">
<Canvas x:Name="Container">
<Path StrokeThickness="1" Canvas.ZIndex="-1">
<Path.Stroke>
<LinearGradientBrush SpreadMethod="Pad">
<GradientStop Color="DarkGreen" Offset="0"/>
<GradientStop Color="Black" Offset="0.5"/>
<GradientStop Color="DarkRed" Offset="1"/>
</LinearGradientBrush>
</Path.Stroke>
<Path.Data>
<PathGeometry>
<PathFigure x:Name="Path" StartPoint="100,10">
<PolyQuadraticBezierSegment x:Name="Segment" Points="200,200,300,10" />
</PathFigure>
</PathGeometry>
</Path.Data>
</Path>
</Canvas>
</Grid>
</UserControl>



The class variables have changed to manage a dynamic list of control pointss, and a random generator:

public Page()
{
InitializeComponent();

// create first point for start point
CreateNewPoint();
// create two control points
CreateControlPointSet();

this.KeyUp += new KeyEventHandler(Page_KeyUp);
// make sure the bezier reflects our gui control points
UpdateBezier();
}

private void CreateControlPointSet()
{
CreateNewPoint();
CreateNewPoint();
}

private void CreateNewPoint()
{
// create a new circle
Ellipse ellipse = new Ellipse();
ellipse.Width = RADIUS * 2;
ellipse.Height = RADIUS * 2;

// make it a random colour
ellipse.Fill = new SolidColorBrush(
Color.FromArgb(
255,
GetRandomColour(),
GetRandomColour(),
GetRandomColour()));

// add it to the interface
Container.Children.Add(ellipse);

// put it in a random location
MoveShape(ellipse, new Point(rand.Next((int)Width), rand.Next((int)Height)));

// keep a track of them for control points
ellipses.Add(ellipse);

// let the user move it, and indicate they can
AttachEventHandlers(ellipse);
ellipse.Cursor = Cursors.Hand;
}

private byte GetRandomColour()
{
return (byte)rand.Next(byte.MaxValue);
}

void Page_KeyUp(object sender, KeyEventArgs e)
{
if (e.Key == Key.Add)
{
CreateControlPointSet();
UpdateBezier();
}
}

private void AttachEventHandlers(Ellipse point)
{
point.MouseLeftButtonDown += new MouseButtonEventHandler(Point_MouseLeftButtonDown);
point.MouseLeftButtonUp += new MouseButtonEventHandler(Point_MouseLeftButtonUp);
point.MouseMove += new MouseEventHandler(Point_MouseMove);
}

private void UpdateBezier()
{
this.Path.StartPoint = Offset(GetPoint(ellipses[0]), RADIUS);
PointCollection points = new PointCollection();

this.Segment.Points.Clear();
ellipses.ForEach(ellipse =>
{
// not start point (already set as path StartPoint)
if (ellipse != ellipses[0])
points.Add(Offset(GetPoint(ellipse), RADIUS));
});

// just changing the points in the collection does not work (clear, add)
// appears to be a bug, so we just replace the whole collection of points
this.Segment.Points = points;

}

The rest of the code is the same as before for the utility and event handlers. Note the last comment about a bug (by design?) which means making changes to the points collection in place has no effect. I'm thinking bug rather than feature...

No comments: