SelDup R5 with a twist

Hi I need to select all duplicate objects in rhino, BUT select BOTH objects, not the just the duplicate, then dump them.

I am creating rectangles from a min xy to max xy point3d, in a loop that draws “walls” from these blocks. I then explodes the whole lot and read the seperate lines into a list to look for the lines at the borders (sharing the same two points of two rectangles)

I tried doing this with list comparison with linq, but for the lines although will be selected in rhino as duplicates from command line with seldup, dont detect as duplicates in the list comparison?

Ive tested the linq comparison with integer datasets and it works as its supposed to.

Is the “line” construct made up more than just the two points causing them to have some component unique to each line, causing the comparison in my list to fail?

My next step would have to be extracting boith points from each line, extracting and comparing xyz values on each but that will slow this down significantly. Any help appreciated. You can see original “blocks” in beggining of video.

this is the code that looks for the unique items (ie, not duplicated) and returns them as a list.

public List<Line> BoxCurves(List<Polyline> Boxes)
        {
            List<Line> OutLines = new List<Line>();
            foreach (Polyline Box in Boxes){
                List<Line> individuallines = Box.GetSegments().ToList();
                foreach (Line thisline in individuallines) OutLines.Add(thisline);
            }
            return OutLines.Distinct().ToList();

        }

It still contains every line in the original list, none are considered “distinct”

Hi @ChristopherBotha

A reason for this might be that the == operators for the line Class is implemented in C# as https://github.com/mcneel/rhinocommon/blob/master/dotnet/opennurbs/opennurbs_line.cs#L601, which makes the comparison direction-dependent, while the _SelDup command treats a reversed line as equal to the original version.

So you have two choices: always orient the lines first in some unique way, or make the comparison orientation-independent. Here I’m showing the second option, only using LINQ as you seem to like it (but! if you use LINQ, then don’t accumulate too much, LINQ-based functions accept also arrays).

public IEnumerable<Line> BoxCurves(List<Polyline> pls)
{
    return pls.SelectMany<Polyline, Line>(a => a.GetSegments()).
       Distinct(UnorderedComparer.Instance);
}

  private class UnorderedComparer : IEqualityComparer<Line>
  {
    private UnorderedComparer(){}
    static UnorderedComparer() { g_instance = new UnorderedComparer(); }
    static UnorderedComparer g_instance;
    public static UnorderedComparer Instance { get {return g_instance; }}

    public bool Equals(Line a, Line b)
    {
      //our comparison works both ways
      return (a.From == b.From && a.To == b.To) ||
             (a.From == b.To && a.To == b.From);
    }

    public int GetHashCode(Line line)
    {
      //this hash code is order-independent, as ^ is commutative.
      return line.From.GetHashCode() ^ line.To.GetHashCode(); 
    }
  }

distinct.gh (6.2 KB)

I hope this helps,

Giulio


Giulio Piacentino
for Robert McNeel & Associates
giulio@mcneel.com

1 Like

Hi Guilio

that worked very well to dump the dups, but i need to dump both curves, the dup and the curve its a dup of. I have no particular preference to linq it just seemed easier than iteration loops and comparing them manually.

I gave linq another go, but its simple not possible from what I can tell (not efficiently) so I changed the routine that plots the points for the rectangles, and each set of points that gets added (two points per line segment) I chack for the points against a linq list lookup, if they exist, simply dont draw that line segment in the first place, and while we have that index, delete the existing line at that index.

that fixed it, for large mazes 200x200 blocks it takes ~3s to process. the output can be joined as one single line closed line segment for extrusion in further operations.

Oh, I see. Then your LINQ query could be:

public IEnumerable<Line> BoxCurves(List<Polyline> pls)
{
    A = pl.SelectMany<Polyline, Line>(a => a.GetSegments()).
      GroupBy(l => l, UnorderedComparer.Instance).Where(g => g.Count() == 1).Select(y => y.Key);
}

only-unique.gh (6.2 KB)

Alternatively, you could make a Dictionary<Line,int>, using the value to count repetitions. Then, just iterate the dictionary and see where the pair.Value == 1.

I hope this helps,

Giulio


Giulio Piacentino
for Robert McNeel & Associates
giulio@mcneel.com

Many thanks. Ill measure performance on both methods. Your help most appreciated.

Linq method was the fastest from 0.03s for a 19x19 grid to 17.3 seconds for a 299x299 grid. (89401 cells!)

while that sounds slow, considering its building a single polyline from all those segments, then “simplifying” it to minimum number of points et, its pretty fast.

again, many thanks for the help.

If you are desperate for performance, I think the non-LINQ methods of Dictionary<Line,int> (or HashTable<Line,int> ) mentioned above would possibly be faster.

The target is for unique line of maze jewellry (each one a different maze with a captured spherical gemstone). Max I will use it for is 15x75 grid. 0.011s. Ample!

1 Like

@piac

many thanks that linq routine pulled my ass out the fire on this too.

1 Like