tags:

views:

130

answers:

4

I have a collection of Points, stored in a PointCollection.

I need the points in the collection to draw lines. So, for example, if a point collection has four points, that will be two lines, as I use pairs of points in the collection to draw the line. I am looking for a way, preferably using linq, and as few lines of code as possible, to essentially iterate through my PointCollection, extracting the next available pairs of points, and then drawing the line using the pairs of points. Is there a way to do this using linq, lambda expressions, or extension method?

Thanks.

A: 

Assuming you just want sequential pairs (note this doesn't draw them - it just lists them):

using System;
using System.Collections;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media;
static class Program {
    static void Main() {
        PointCollection points = new PointCollection();
        points.Add(new Point(1, 1));
        points.Add(new Point(2, 2));
        points.Add(new Point(3, 3));
        points.Add(new Point(4, 4));
        points.Add(new Point(5, 5));
        points.Add(new Point(6, 6));
        points.Add(new Point(7, 7)); // odd number to test end

        foreach (Tuple<Point, Point> pair in GetPairs(points)) {
            Console.WriteLine("From " + pair.Value1 + " to " + pair.Value2);
        }
    }
    public static IEnumerable<Tuple<Point, Point>>
            GetPairs(PointCollection points) {
        using (IEnumerator<Point> iter = points.GetEnumerator()) {
            while (iter.MoveNext()) {
                Point value1 = (Point)iter.Current;
                if (!iter.MoveNext()) { yield break; }
                yield return new Tuple<Point, Point>(value1, (Point)iter.Current);
            }
        }
    }
}
public struct Tuple<T1, T2> {
    public T1 Value1 { get { return value1; } }
    public T2 Value2 { get { return value2; } }
    private readonly T1 value1;
    private readonly T2 value2;
    public Tuple(T1 value1, T2 value2) {
        this.value1 = value1;
        this.value2 = value2;
    }
}
Marc Gravell
A: 

another approach, using a bit of LINQ and recursion:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace overflow2
{
    class Program
    {
        static void Main(string[] args)
        {
            List<Point> points = new List<Point>();
            points.Add(new Point(1, 1));
            points.Add(new Point(2, 2));
            points.Add(new Point(3, 3));
            points.Add(new Point(4, 4));
            points.Add(new Point(5, 5));
            points.Add(new Point(6, 6));
            points.Add(new Point(7, 7)); // odd number to test end

            foreach (LineSegment<Point> linesegment in points.GetPairs())
                Console.WriteLine("From " + linesegment.Value1.ToString() + " to " + linesegment.Value2.ToString());

            Console.ReadLine();
        }
    }

    public class Point
    {
        public int x { get; set; }
        public int y { get; set; }

        public Point(int _x, int _y)
        {
            x = _x;
            y = _x;
        }

        public override string ToString()
        {
            return string.Format("{0},{1}", x, y);
        }
    }

    public class LineSegment<T>
    {
        public T Value1 { get; private set; }
        public T Value2 { get; private set; }

        public LineSegment(T value1, T value2)
        {
            Value1 = value1;
            Value2 = value2;
        }
    }

    public static class Util
    {
        public static List<LineSegment<Point>> GetPairs(this List<Point> points)
        {
            if (points.Count >= 2)
            {
                var pair = new LineSegment<Point>(points.Take(1).First(), points.Skip(1).Take(1).First());
                var res = new List<LineSegment<Point>>() { pair };
                res.AddRange(points.Skip(2).ToList().GetPairs());  // recursion
                return res;
            }
            else
                return new List<LineSegment<Point>>();
        }
    }
}
Mike Jacobs
+1  A: 

Here's a quick snip of code to get the pairs.

var listOfPairs = points
  .Select((p, i) => new {p, i})
  .GroupBy(x => x.i / 2)
  .Where(g => g.Skip(1).Any())
  .Select(g => g.Select(x => x.p).ToList())
  .ToList();

This is not performant if you have thousands of points, compared to a foreach loop.


Here's the other requested form, using a captured variable.

Point previous = points.FirstOrDefault();

List<Pair> pairs = points
  .Skip(1)
  .Select(p =>
  {
    Pair result = new Pair(p, previous)
    previous = p;
    return result;
  })
  .ToList();
David B
I need to clarify my initial post. The collection of points is used for drawing lines, so, the structure is as follows:If there are four points in the collection, then I would draw the line(s) using the following approach:(1) Point 1 and 2 used for drawing first line(2) Point 2 and 3 used for drawing second line(3) Point 3 and 4 used for drawing third lineSo, even though there are only four points in the collection, those points represent three lines. Is there a way to extract the pairs for the approach that I am taking?
Chris
+1  A: 

Assuming you are not using parallelism on your foreach, you could use:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Drawing;

namespace PointDrawing
{
    class Program
    {
        static void Main(string[] args)
        {
            List<Point> points = new List<Point>();
            points.Add(new Point(1, 1));
            points.Add(new Point(2, 2));
            points.Add(new Point(3, 3));
            points.Add(new Point(4, 4));
            points.Add(new Point(5, 5));
            points.Add(new Point(6, 6));
            points.Add(new Point(7, 7));

            if (points.Count > 0)
            {
                Point src = points[0];
                points.ForEach(p => Draw(ref src, p));
            }
        }

        public static void Draw(ref Point p1, Point p2)
        {
            if (p1 != p2)
            {
                //Draw from p1 to p2 here
            }

            p1 = p2; //assign so that p2 is the next origin
        }
    }
}

That would work with a list of point, but if PointCollection is an IEnumerable you could always add the ever so usefull ForEach extension to your IEnumerables:

public static class Extensions
{
    public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
    {
        foreach (var item in source)
        {
            action(item);
        }
    }
}
Dynami Le Savard