Silverlight - how to draw a line with an arc? - silverlight

I am developing a small silverlight application (using siverlight 4 and c#). In my application I need to draw coordinates based on their X,Y. Then, I need to draw lines between some of the points, based on the connections between them. Since there can be several lines, and I cannot have them all intersecting each other (as it will turn this into a mess), I need to draw some of my lines with an arch.
So, What would be the best way to approach this issue?
Create my own x,y system - position elements in points and draw lines - If so How can I draw a line with an arch?
Use a ready control that provides similar capabilities? If so, what control?
Thank You!
Attached is a small image to illustrate my need (I am no big painter, sorry).

Take a look at drawing Bezier curves (MSDN Link) and learn about the different geometry types (MSDN Link)
Below is a code sample to get you started that will produce the following image:
<Canvas x:Name="LayoutRoot" Background="White">
<Path Stroke="Blue" StrokeThickness="2" >
<Path.Data>
<PathGeometry>
<PathGeometry.Figures>
<PathFigureCollection>
<PathFigure StartPoint="50,50">
<PathFigure.Segments>
<PathSegmentCollection>
<BezierSegment
Point1="50,20"
Point2="120,170"
Point3="350,150"
/>
</PathSegmentCollection>
</PathFigure.Segments>
</PathFigure>
</PathFigureCollection>
</PathGeometry.Figures>
</PathGeometry>
</Path.Data>
</Path>
<Path Fill="Gold" Stroke="Black" StrokeThickness="1">
<Path.Data>
<EllipseGeometry Center="50,50" RadiusX="20" RadiusY="20" />
</Path.Data>
</Path>
<Path Fill="Gold" Stroke="Black" StrokeThickness="1">
<Path.Data>
<EllipseGeometry Center="350,150" RadiusX="20" RadiusY="20" />
</Path.Data>
</Path>
</Canvas>

Related

Can you style individual segments of a Path?

I'm showing a series of data points where the first segment in the series is colored differently than all the other segments (image 1).
At the moment the first segment is a separate object than the other segments in the series, which are all a single Path comprised by PolyLineSegments.
You can see in the images that the mating surfaces between the first segment and the second (where the color changes) is not very clean, however it is rendered much nicer in the remaining cases (third image example).
Is it possible to alter the colors for individual segments? If so I could merge the standalone line into the PathSegmentCollection and I think that would resolve the rendering issue. But the various attributes such as Stroke are only available on the Path tag, not the subsidiary objects.
<Line
X1="{Binding LeadingCoordinate.Screen.X}"
Y1="{Binding LeadingCoordinate.Screen.Y}"
X2="{Binding Coordinates[0].Screen.X}"
Y2="{Binding Coordinates[0].Screen.Y}"
Stroke="Red"
StrokeThickness="5"
Opacity="0.25"
StrokeStartLineCap="Round"
StrokeEndLineCap="Flat"
/>
<Path
Stroke="Blue"
StrokeThickness="5"
Opacity="0.25"
StrokeStartLineCap="Round"
StrokeEndLineCap="Round"
StrokeLineJoin="Round"
>
<Path.Data>
<PathGeometry>
<PathGeometry.Figures>
<PathFigureCollection>
<PathFigure StartPoint="{Binding ScreenPoints[0]}">
<PathFigure.Segments>
<PathSegmentCollection>
<PolyLineSegment Points="{Binding ScreenPoints}"/>
</PathSegmentCollection>
</PathFigure.Segments>
</PathFigure>
</PathFigureCollection>
</PathGeometry.Figures>
</PathGeometry>
</Path.Data>
</Path>

How to make a reverse Path

I have this code:
<Path Stroke="Black" Margin="15">
<Path.Data>
<PathGeometry>
<PathFigure StartPoint="150,100">
<ArcSegment Size="50,50" Point="50,50" />
</PathFigure>
</PathGeometry>
</Path.Data>
</Path>
And the resulting Path is this:
I want to make the reverse Path:
But I want to make this without turn the StartPoint and the ArcSegment Point, because I need this Path to make a DoubleAnimationUsingPath (If I set StartPoint to 50,50 and the ArcSegmentPoint to 150,100, the figure is ok, but the animation goes in reverse mode).
The fix for this particular case is quite easy, simply set SweepDirection="Clockwise" on your ArcSegment. If this was just an example and not your actual use-case you might want to include more code (e.g. the Animation).
<Path Stroke="Black" Margin="15">
<Path.Data>
<PathGeometry>
<PathFigure StartPoint="150,100">
<ArcSegment SweepDirection="Clockwise" Size="50,50" Point="50,50" />
</PathFigure>
</PathGeometry>
</Path.Data>
</Path>
Just in case you need it, there is also a IsLargeArc setting that you could toggle once you reach a certain threshold (depends on your use-case).
Example Result:

How do I draw with coordinates in [0;1] instead of pixels in WPF?

My coordinates are relative to the control size, in the 0 to 1 range. I currently draw on my control using manual scaling by RenderSize, which works fine, but is surely the wrong way.
How can I draw directly in 0-1 coordinates instead?
You may use Path controls and scale their Data by applying an appropriate transform to the Geometry.Transform property, like in the trivial example shown below. This way you would scale the drawn shapes, but not their stroke thicknesses.
<Grid>
<Canvas>
<Canvas.Resources>
<ScaleTransform x:Key="transform"
ScaleX="{Binding Value, ElementName=scaleSlider}"
ScaleY="{Binding Value, ElementName=scaleSlider}"/>
</Canvas.Resources>
<Path Stretch="None" Stroke="Blue" StrokeThickness="2">
<Path.Data>
<RectangleGeometry Rect="0.1,0.1,0.8,0.4"
Transform="{StaticResource transform}"/>
</Path.Data>
</Path>
<Path Stretch="None" Stroke="Red" StrokeThickness="2">
<Path.Data>
<EllipseGeometry Center="0.6,0.5" RadiusX="0.3" RadiusY="0.3"
Transform="{StaticResource transform}"/>
</Path.Data>
</Path>
</Canvas>
<Slider x:Name="scaleSlider" HorizontalAlignment="Center" VerticalAlignment="Bottom"
Width="100" Minimum="100" Maximum="500"/>
</Grid>

WPF - Path Geometry...Is there a way to bind the Data Property?

I have a ControlTemplate that serves as a "Bubble" popup on the AdornerLayer of a given control.
It works fine but I need to be able to calculate where it should display (middle/ bottom).
Instead of:
<Path Stroke="Black" Fill="Black" Data="M 15 20 L 15 0 33 20" Margin="0 1 0 0"/>
I am looking for (obviously this won't work but it illustrates what I'm trying to accomplish:
<Path Stroke="Black" Fill="Black" Data="M {TemplateBinding Left} 20 L 15 0 33 20"/>
Can this be done with a ValueConverter? I just can't visualize a solution for some reason. I'm also open to alternatives.
Thanks for reading and if I can provide more info please just ask.
If you want a value converter that you can use to convert a string into the path data, you might like to try the universal value converter I wrote a while back.
Alternatively, to bind to a single property, you will have to expand your geometry by adding the various geometry objects into your XAML, rather than using the string shorthand. For example ...
<Path Stroke="Black" StrokeThickness="1">
<Path.Data>
<PathGeometry>
<PathGeometry.Figures>
<PathFigureCollection>
<PathFigure IsClosed="True" StartPoint="10,100">
<PathFigure.Segments>
<PathSegmentCollection>
<LineSegment Point="{Binding MyPropertyPath}" />
<LineSegment Point="100,50" />
</PathSegmentCollection>
</PathFigure.Segments>
</PathFigure>
</PathFigureCollection>
</PathGeometry.Figures>
</PathGeometry>
</Path.Data>
</Path>

How can I check whether 2 WPF Paths (with MatrixTransform applied) overlap

In my WPF multitouch application, I want to check whether a Path is overlapping another Path.
How can I do this? I have found different solutions on the Internet, but none is working, because of the combination of requirements:
2 Paths of irregular shape (so Bounds are not working: no Rect)
On the Paths a MatrixTransform is applied (because of Manipulation events)
The Paths can be rotated and scaled (using the MatrixTransform)
The RenderTransformOrigin is 0.5, 0.5
How can I do this?
Example: in this example the blue and red star are overlapping, the red and the green are not.
<Window x:Class="WPFTest.OtherWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="OtherWindow" Height="300" Width="300" >
<Canvas >
<Path x:Name="path1" Data="M17,0 L23,11 34,13 27,21 28,32 18,24 8,30 9,19 0,11 13,11 z" Fill="#99FF0000" Stroke="Black" RenderTransformOrigin="0.5,0.5">
<Path.RenderTransform>
<MatrixTransform Matrix="1,0,0,1,50,50"/>
</Path.RenderTransform>
</Path>
<Path x:Name="path2" Data="M17,0 L23,11 34,13 27,21 28,32 18,24 8,30 9,19 0,11 13,11 z" Fill="#990000FF" Stroke="Black" RenderTransformOrigin="0.5,0.5">
<Path.RenderTransform>
<MatrixTransform Matrix="0.777817459305202,0.777817459305202,-0.777817459305202,0.777817459305202,35,45"/>
</Path.RenderTransform>
</Path>
<Path x:Name="path3" Data="M17,0 L23,11 34,13 27,21 28,32 18,24 8,30 9,19 0,11 13,11 z" Fill="#9900FF00" Stroke="Black" RenderTransformOrigin="0.5,0.5">
<Path.RenderTransform>
<MatrixTransform Matrix="0.777817459305202,0.777817459305202,-0.777817459305202,0.777817459305202,82,55"/>
</Path.RenderTransform>
</Path>
</Canvas>
How can I check this from code behind?
Thanks,
Jim
If it is acceptable, you may use transformed geometries for Path.Data instead of (render-)transformed Paths (with the drawback of course that their stroke won't be scaled).
Once you use such transformed geometries, you can determine their mutual intersection by Geometry.FillContainsWithDetail, which gives you an enum value for the kind of intersection.
Your XAML might look like the following. Note that because Geometry does not have something like a transform origin, I modified your geometry so that the coordinate origin lies in its center. I also added the resulting offset to the MatrixTransform offset values.
<Canvas>
<Path Name="redStar" Fill="#99FF0000" Stroke="Black">
<Path.Data>
<PathGeometry Figures="M0,-16 L6,-5 17,-3 10,5 11,16 1,8 -9,14 -8,3 -17,-5 -4,-5 z">
<PathGeometry.Transform>
<MatrixTransform Matrix="1,0,0,1,67,66"/>
</PathGeometry.Transform>
</PathGeometry>
</Path.Data>
</Path>
<Path Name="blueStar" Fill="#990000FF" Stroke="Black">
<Path.Data>
<PathGeometry Figures="M0,-16 L6,-5 17,-3 10,5 11,16 1,8 -9,14 -8,3 -17,-5 -4,-5 z">
<PathGeometry.Transform>
<MatrixTransform Matrix="0.777817459305202,0.777817459305202,-0.777817459305202,0.777817459305202,52,61"/>
</PathGeometry.Transform>
</PathGeometry>
</Path.Data>
</Path>
<Path Name="greenStar" Fill="#9900FF00" Stroke="Black">
<Path.Data>
<PathGeometry Figures="M0,-16 L6,-5 17,-3 10,5 11,16 1,8 -9,14 -8,3 -17,-5 -4,-5 z">
<PathGeometry.Transform>
<MatrixTransform Matrix="0.777817459305202,0.777817459305202,-0.777817459305202,0.777817459305202,99,71"/>
</PathGeometry.Transform>
</PathGeometry>
</Path.Data>
</Path>
</Canvas>
The following code will determine the intersections:
Geometry redGeometry = redStar.Data;
Geometry blueGeometry = blueStar.Data;
Geometry greenGeometry = greenStar.Data;
Trace.TraceInformation("red/blue intersection: {0}", redGeometry.FillContainsWithDetail(blueGeometry));
Trace.TraceInformation("blue/green intersection: {0}", blueGeometry.FillContainsWithDetail(greenGeometry));
Trace.TraceInformation("green/red intersection: {0}", greenGeometry.FillContainsWithDetail(redGeometry));
redGeometry.Transform = new MatrixTransform(1, 0, 0, 1, 70, 90);
Trace.TraceInformation("red/blue intersection: {0}", redGeometry.FillContainsWithDetail(blueGeometry));

Resources