Friday, September 5, 2008

Silverlight and bezier curves

Starting to learn Silverlight, so I implemented a simple interface to allow the control points for a Quadratic bezier segment to be controlled via drag-and-drop.


(note it works in Googles chrome, just does not redraw properly)



<UserControl x:Class="DragDrop.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>
<Ellipse x:Name="PointStart" Canvas.Left="100" Canvas.Top="10" Width="20" Height="20" Fill="DarkGreen" Cursor="Hand"/>
<Ellipse x:Name="PointMiddle" Canvas.Left="200" Canvas.Top="100" Width="20" Height="20" Fill="Black" Cursor="Hand"/>
<Ellipse x:Name="PointEnd" Canvas.Left="300" Canvas.Top="10" Width="20" Height="20" Fill="DarkRed" Cursor="Hand"/>

<Path StrokeThickness="10" Canvas.ZIndex="-1" StrokeDashArray="1,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">
<QuadraticBezierSegment x:Name="Segment" Point1="200,100" Point2="300,10" />
</PathFigure>
</PathGeometry>
</Path.Data>
</Path>
</Canvas>
</Grid>
</UserControl>


with C# behind:

namespace DragDrop
{
public partial class Page : UserControl
{
bool isDragging = false;

public Page()
{
InitializeComponent();

AttachEventHandlers(PointStart);
AttachEventHandlers(PointMiddle);
AttachEventHandlers(PointEnd);

// make sure the bezier reflects our gui control points
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()
{
double radius = this.PointStart.Width / 2;

Path.StartPoint = Offset(GetPoint(PointStart), radius);
Segment.Point1 = Offset(GetPoint(PointMiddle), radius);
Segment.Point2 = Offset(GetPoint(PointEnd), radius);
}

private Point Offset(Point inpoint, double offset)
{
Point point = new Point(inpoint.X + offset, inpoint.Y + offset);

return point;
}

private static Point GetPoint(DependencyObject sobj)
{
Point point = new Point((double)sobj.GetValue(Canvas.LeftProperty), (double)sobj.GetValue(Canvas.TopProperty));

return point;
}

void Point_MouseMove(object sender, MouseEventArgs e)
{
if (!isDragging)
return;

Ellipse point = sender as Ellipse;
Point newLocation = e.GetPosition(this);

MoveShape(point, newLocation);

UpdateBezier();
}

private static void MoveShape(DependencyObject shape, Point newLocation)
{
shape.SetValue(Canvas.LeftProperty, newLocation.X);
shape.SetValue(Canvas.TopProperty, newLocation.Y);
}

void Point_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (!isDragging)
return;

isDragging = false;
Ellipse point = sender as Ellipse;
point.ReleaseMouseCapture();
}

void Point_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Ellipse point = sender as Ellipse;
isDragging = true;
point.CaptureMouse();
}
}
}

No comments: