开发者

Intersection point in silverlight/wpf

Given a path like this one :

<Path Stretch="Fill" Stroke="#FFD69436" Data="M 0,20 L 22.3,20 L 34,0 L 44.7,20 L 68,20 L 55.8,40 L 68,60 L 44.7,60 L 34,80 L 22.3开发者_JAVA技巧,60 L 0,60 L 11.16,40 L 0,20 Z">
            <Path.Fill>
                <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
                    <LinearGradientBrush.GradientStops>
                        <GradientStop Color="#FFFFFF" Offset="0" />
                        <GradientStop Color="Orange" Offset="1" />
                    </LinearGradientBrush.GradientStops>
                </LinearGradientBrush>
            </Path.Fill>
        </Path>

How can I get the point that is at the edge of this figure at any angle? Lets say that I want the intersection point between the edge of this figure and a line passing through the center of this figure, at the 30 degrees angle with the OX axe?

thanks.


There are quite a few different ways to do this, depending on the performance and accuracy you need and how much geometric code you're willing to write.

1. Using FillContains

One way is to construct successive line geometries and use path.Data.FillContains(geometry) to determine whether they intersect the figure. Something along these lines:

Transform rotation = new RotateTransform { Angle = 30 };

double max = path.Width + path.Height;
double current = 0;
for(double delta = max/2; delta > 0.25; delta = delta/2)
{
  var line = new LineGeometry(
                  new Point(centerX + current, centerY),
                  new Point(centerX + max, centerY),
                  rotation);
  if(path.Data.FillContains(line))
    current += delta;
}
var intersectPoint = rotation.Transform(new Point(current, 0));

2. Using GetFlattenedPathGeometry

Another way is to use GetFlattenedPathGeometry:

var flattened = path.Data.GetFlattenedPathGeometry();
var segment = pg.Figures[0].Segments[0] as PolyLineSegment;
Point[] points = segment.Points;
for(int i=0; i<points.Count-1; i++)
{
  ... check for intersection with the line from points[i] to points[i+1] ...
}

This can be faster because the geometry is only processed once but requires you to code your own line intersection algorithm (which is very simple).

3. Using PathGeometry.CreateFromGeometry

The most efficient way of all is to convert the given geometry into a PathGeometry and then manually iterate through the Figures and Segments in the geometry:

var geo = PathGeometry.CreateFromGeometry(path.Data); foreach(var figure in geo.Figures) foreach(var segment in figure.Segments) if(segment is LineSegment) ... else if(segment is ArcSegment) ... else if(segment is BezierSegment) ... else if(segment is QuadraticBezierSegment) ... else if(segment is PolyLineSegment) ... else if(segment is PolyBezierSegment) ... else if(segment is PolyQuadraticBezierSegment) ...

This approach requires quite a lot of geometric analysis code but is extremely fast because WPF doesn't have to construct a flattened geometry or do repeated intersections. This is the technique I generally use when I need my code to run very fast.

Note on GetWidenedPathGeometry

Everything I've said so far will give you intersections between the geometric path data and your intersecting line: It does not take into account line width, end caps, etc. To take these into account as well you will need to use GetWidenedPathGeometry as follows:

var widenedData = path.Data.GetWidenedPathGeometry(new Pen { ... });

Where the Pen parameters are set from path.Stroke, path.StrokeWidth, etc.

After doing this, use one of the above techniques replacing "path.Data" with "widenedData".


Do you guys know what the equivalent to this would be in Silverlight 3? I'm trying to do this for WP7, and it looks like it's Paths.Data is a plain Geometry object, which doesn't expose Figures or the methods you mention.

Great answer by the way, this really helped me, I'm trying to get the Point collection from a Path, and this is it, it just doesnn't work for mobile :)

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜