DistinctBy or similar with multiple properties doesn't work. (Distinct, does)

DistinctBy or similar with multiple properties doesn't work. (Distinct, does)

juancalvoferrandiz
Enthusiast Enthusiast
2,680 Views
6 Replies
Message 1 of 7

DistinctBy or similar with multiple properties doesn't work. (Distinct, does)

juancalvoferrandiz
Enthusiast
Enthusiast

Hello everybody,

 

OBJECTIVE:

I want to use with 2 exact exterior wall faces:

a)the DistinctBy function from MoreLinq 

b) GroupBy, Select and First

or

c) Implement Comparable

 

Reference code:

https://www.codeproject.com/Articles/535374/www.codeproject.com/Articles/477519/Return-Anonymous-typ...

 

CONTEXT:

I'm able to compile but the code dosen't do the job. The Distinct() method works (returns 1 face). My problem is that with the Distinct() method I can't select the properties with which I do the distinction:

 

DistinctBy3.JPG

 

 

 

 

 

 

 

 

 

  

 

 

The Implement  Comparable: 

DistinctBy4.JPG

 

 

 

 

 

 

 

 

  

 

 

 

 

 

The 2 faces:

DistinctBy5.JPG

 

 

 

 

 

 

 

 

 I have tried the code with a Console App with positive results:


DistinctBy2.JPG

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  

QUESTION:

Any keys/tips for what I can do?

 

Thanks for any comment.

 

Good day for everyone.

0 Likes
2,681 Views
6 Replies
Replies (6)
Message 2 of 7

jeremytammik
Autodesk
Autodesk

I am interested in your problem and would generally recommend implementing a proper compare function.

 

However, you do not explain what your task or goal is, what data you have, what you want to compare and how.

 

I cannot read your screen snapshots.

 

Please write down your question in text form.

 

Cheers,

 

Jeremy

 



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

0 Likes
Message 3 of 7

juancalvoferrandiz
Enthusiast
Enthusiast

Hello Jeremy,


Thank you for your interest. I appreciated.

 

Ok. I complete the initial information:

 

GOAL: My goal is to being able to create a Method which distincts faces with LINQ, selecting the properties of the faces that will generate the distinction. The return of the method will be the faces that don't have the same properties, leaving just a copy of repeated faces.

 

DATA I HAVE: I got faces provenient from different situations, but principaly from two scenarios: Exterior selected faces from walls, roofs, floors and faces from solids I create with GeometryCreationUtilities. 

EXTRA: My principal frustation its to don't understand why the LINQ Distinct() (not being able to choose the properties that distinct) method works but my the other 3 options, compile but dosen't do the job.

CODE: I update my code from my screenshots to 4 methods with return  and the class with the IEqualityComparer implemented. As example I choose the XVector and YVector properties from the derived PlanarFace Class, which are easy to see that the values are the same.

 

I hope this update helps. 


Cheers, Juan.

 

//With Distinct() works perfectly, but I'm no able to choose the properties that distinct.
public static List<Face> DistinctWithDistinctMethod(List<Face> currentColorFaces)
        {
            var currentColorPlanarFaces = currentColorFaces.Cast<PlanarFace>().ToList();

            var distinctColorPlanarFaces = currentColorPlanarFaces.Distinct().ToList();

            return new List<Face>(distinctColorPlanarFaces.ToList());
        }

// Option A: DistinctBy from MoreLinq Library. (https://code.google.com/archive/p/morelinq/)
public static List<Face> DistinctWithDistinctByMethod(List<Face> currentColorFaces)
        {
            var currentColorPlanarFaces = currentColorFaces.Cast<PlanarFace>().ToList();

            var distinctColorPlanarFaces = currentColorPlanarFaces.DistinctBy(x => new { x.XVector, x.YVector }).ToList();

            return new List<Face>(distinctColorPlanarFaces.ToList());
        }

// Option B: GroupBy, Select and First.
public static List<Face> DistinctWithGroupByMethod(List<Face> currentColorFaces)
        {
            var currentColorPlanarFaces = currentColorFaces.Cast<PlanarFace>().ToList();

            var distinctColorPlanarFaces = currentColorPlanarFaces
                .AsEnumerable()
                .GroupBy(x => new { x.XVector, x.YVector })
                .Select(g => g.First())
                .ToList();

            return new List<Face>(distinctColorPlanarFaces.ToList());
        }

// Option C: Implement Comparable.
public static List<Face> DistinctWithImplementComparable(List<Face> currentColorFaces)
        {
            var currentColorPlanarFaces = currentColorFaces.Cast<PlanarFace>().ToList();

            var distinctColorPlanarFaces = currentColorPlanarFaces.Distinct(new PlDistinct(x => new { x.XVector, x.YVector })).ToList();

            return new List<Face>(distinctColorPlanarFaces.ToList());
        }

//The class with the IequalityCOmparer implemented
public class PlDistinct : IEqualityComparer<PlanarFace>
    {
        private Func<PlanarFace, object> _funcDistinct;

        public PlDistinct(Func<PlanarFace, object> funcDistinct)
        {
            _funcDistinct = funcDistinct;
        }

        public bool Equals(PlanarFace x, PlanarFace y)
        {
            return _funcDistinct(x).Equals(_funcDistinct(y));
        }

        public int GetHashCode(PlanarFace obj)
        {
            return _funcDistinct(obj).GetHashCode();
        }
    }

 

0 Likes
Message 4 of 7

jeremytammik
Autodesk
Autodesk

I would not use the X and Y vectors at all for comparison purposes.

 

To just compare the underlying unbounded planes, all you need are the normal vector and the distance of the plane from the origin.

  

If you want to be really picky, a Revit plane can also be considered different if it its embedded X and Y axes is rotated.

 

Here is a comparison operator for Revit planes:

 

https://github.com/jeremytammik/the_building_coder_samples/blob/master/BuildingCoder/BuildingCoder/U...

 

    public static int Compare( Plane a, Plane b )
    {
      int d = Compare( a.Normal, b.Normal );

      if( 0 == d )
      {
        d = Compare( a.SignedDistanceTo( XYZ.Zero ),
          b.SignedDistanceTo( XYZ.Zero ) );

        if( 0 == d )
        {
          d = Compare( a.XVec.AngleOnPlaneTo(
            b.XVec, b.Normal ), 0 );
        }
      }
      return d;
    }

  

As you can see, it also makes use of a compare method for XYZ objects and one for real numbers, plus a signed distance to the origin method.

 

Please refer to The Building Coder samples Util class for those:

 

https://github.com/jeremytammik/the_building_coder_samples

 

Comparison of planar faces and their boundaries is more complex, but doable.

 

What you have implemented does not compare planar faces, or the underlying planes, in any reliable way.

 

If it works for your needs, then your needs are totally unclear to me.

 

Please specify how you want to compare the planar faces.

 

Maybe it will help if you explain the purpose of this exercise.

 

Cheers,

 

Jeremy

 



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

0 Likes
Message 5 of 7

juancalvoferrandiz
Enthusiast
Enthusiast

Thanks for your quick answer Jeremy.

 

Yes, I agree. I choose XVector and YVector as examples because they where easy to compare visualy and my purpose is to make a method that could accept any number of properties without worrying about their type , but for my final objective. In the short term, they are not useful.

My purpose, in the short term,  is to compare faces in the model by position, orientation and area, so I suppose I will need to use Origin, FacerNormal and Area. I can't use just Distinct()(the option that at this moment works), because I got faces created in different moments with different ID but same position, normal and area.

Thanks for your time.

0 Likes
Message 6 of 7

juancalvoferrandiz
Enthusiast
Enthusiast

When I have time I will try your code Jeremy. In the meantime, I have been able to continue by converting the properties in string:

 

// Option A: DistinctBy from MoreLinq Library. (https://code.google.com/archive/p/morelinq/)
public static List<Face> DistinctWithDistinctByMethod(List<Face> currentColorFaces)
        {
            var currentColorPlanarFaces = currentColorFaces.Cast<PlanarFace>().ToList();

            var distinctColorPlanarFaces = currentColorPlanarFaces.DistinctBy(x => $"{x.XVector} {x.YVector }").ToList();

            return new List<Face>(distinctColorPlanarFaces.ToList());
        }

// Option B: GroupBy, Select and First.
public static List<Face> DistinctWithGroupByMethod(List<Face> currentColorFaces)
        {
            var currentColorPlanarFaces = currentColorFaces.Cast<PlanarFace>().ToList();

            var distinctColorPlanarFaces = currentColorPlanarFaces
                .AsEnumerable()
                .GroupBy(x => $"{x.XVector} {x.YVector }")
                .Select(g => g.First())
                .ToList();

            return new List<Face>(distinctColorPlanarFaces.ToList());
        }

// Option C: Implement Comparable. 
// Haven't try yet.

Thanks again for your help Jeremy.

0 Likes
Message 7 of 7

jeremytammik
Autodesk
Autodesk

Congratulations on implementing a satisfactory solution.

 

The problem remains that if you want to distinguish different planes, their X and Y vectors are not really the best place to start, mathematically.

 

I hope you have implemented a comprehensive test suite to ensure that all your use cases are satisfied and correctly handled, so you do not run into surprises later on.

 

Cheers,

 

Jeremy

 



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

0 Likes