Author Topic: cool extension methods  (Read 11371 times)

0 Members and 1 Guest are viewing this topic.


TonyT

  • Guest
Re: cool extension methods
« Reply #1 on: January 28, 2009, 04:19:59 AM »
Some of my commonly-used extension methods:

Code: [Select]

// Should be self-explainatory:

public static class DoubleExtender
{
   public static string ToDistance( this double value )
   {
      return Converter.DistanceToString( value, DistanceUnitFormat.Current, -1 );
   }

   public static string ToDistance( this double value, DistanceUnitFormat format, int precision )
   {
      return Converter.DistanceToString( value, format, precision );
   }

   // TODO: ToAngle()

}

public static class PolylineHelper
{
   public static Curve GetCurveAt( this Polyline pline, int index )
   {
      Entity ent = pline.GetSubentity(
          new FullSubentityPath(
              new ObjectId[] { pline.ObjectId },
              new SubentityId( SubentityType.Edge, index + 1 ) ) );
      if( ent != null )
      {
         Curve curve = ent as Curve;
         if( curve != null )
            return curve;
         ent.Dispose();
      }
      return null;
   }

   public static Curve GetCurveAt( this Polyline pline, Point3d pointOnSegment )
   {
      return GetCurveAt( pline, (int) Math.Truncate( pline.GetParameterAtPoint( pointOnSegment ) ) );
   }

   public static Curve GetCurveNearestTo( this Polyline pline, Point3d point )
   {
      return GetCurveAt( pline, pline.GetClosestPointTo( point, false ) );
   }

   public static Curve3d GetCurve3dAt( this Polyline pline, int index )
   {
      switch( pline.GetSegmentType( index ) )
      {
         case SegmentType.Arc:
            return pline.GetArcSegmentAt( index );
         case SegmentType.Line:
            return pline.GetLineSegmentAt( index );
         default:
            throw new InvalidOperationException( "Unknown segment type" );
      }
   }

   public static Curve2d GetCurve2dAt( this Polyline pline, int index )
   {
      switch( pline.GetSegmentType( index ) )
      {
         case SegmentType.Arc:
            return pline.GetArcSegment2dAt( index );
         case SegmentType.Line:
            return pline.GetLineSegment2dAt( index );
         default:
            throw new InvalidOperationException( "Unknown segment type" );
      }
   }
}

public static class BlockReferenceExtensionMethods
{
   public static string DefinitionName( this BlockReference target )
   {
      if( target.IsDynamicBlock )
      {
         using( BlockTableRecord obj = target.DynamicBlockTableRecord.Open( OpenMode.ForRead, true, true ) as BlockTableRecord )
            return obj.Name;
      }
      return target.Name;
   }
}

/// This class allows you to tell if a List<T>
/// was modified between two points in time:
///
///  Example:
///
///    List<int> myList = new List<int>();
///    myList.Add( 1 );
///    myList.Add( 2 );
///    myList.Add( 3 );
///
///    int version = myList.GetVersion();
///   
///    SomeVirtualMethod( myList );  // called method may modify the list
///
///    if( myList.IsModified( version ) )
///       Console.WriteLine("SomeVirtualMethod() changed the list");
///

public static class ListExtender
{
   public static int GetVersion<T>( this List<T> list )
   {
      FieldInfo fi = typeof( List<T> ).GetField( "_version", BindingFlags.Instance | BindingFlags.NonPublic );
      if( fi != null )
      {
         return (int) fi.GetValue( list );
      }
      throw new InvalidOperationException();
   }

   public static bool IsModified<T>( this List<T> list, int oldVersion )
   {
      return list.GetVersion<T>() != oldVersion;
   }
}

public static class QueueExtender
{
   public static void Enqueue<T>( this Queue<T> queue, IEnumerable<T> items )
   {
      foreach( T item in items )
         queue.Enqueue( item );
   }

   public static void Enqueue<T>( this Queue<T> queue, IEnumerable items )
   {
      foreach( object item in items )
         queue.Enqueue( (T) item );
   }

}


/// The most useful extension methods I know of, with the
/// caveat that they're probably unsuitable in performance-
/// critical scenarios.
///
///  Example:
///
///    BindingFlags flags = BindingFlags.Public | BindingFlags.InvokeMethod | BindingFlags.Static;
///
///  Instead of doing this:
/// 
///     bool isStatic = ( flags & BindingFlags.Static ) == BindingFlags.Static;
/// 
///  You can instead do this:
///
///     bool isStatic = flags.Includes( BindingFlags.Static );
///
///  Or:
///
///     bool isStatic = flags.And( BindingFlags.Static );

public static class EnumExtender
{
   public static bool Includes( this Enum target, Enum flags )
   {
      if( target.GetType() != flags.GetType() )
         throw new ArgumentException( "Enum type mismatch" );
      long a = Convert.ToInt64( target );
      long b = Convert.ToInt64( flags );
      return (a & b) == b;
   }

   public static bool IncludesAny( this Enum target, Enum flags )
   {
      if( target.GetType() != flags.GetType() )
         throw new ArgumentException( "Enum type mismatch" );
      return ( Convert.ToInt64( target ) | Convert.ToInt64( flags ) ) != 0L;
   }

   public static bool And( this Enum target, Enum flags )
   {
      return Includes( target, flags );
   }

   public static bool Or( this Enum target, Enum flags )
   {
      return IncludesAny( target, flags );
   }

}

« Last Edit: January 28, 2009, 04:24:05 AM by TonyT »

Ken Alexander

  • Newt
  • Posts: 61
Re: cool extension methods
« Reply #2 on: January 28, 2009, 03:26:07 PM »
Hi Tony,

The IsModified extension for List(of T) can be misleading.  I guess one would need to define what ‘Modified’ meant.  If the list contained three integers (1, 2, 3) and after ‘some’ processing, the list still contained three integers (1, 2, 3); has the list been modified?

Code: [Select]

    List<int> MyList = new List<int>();
    MyList.Add(1);
    MyList.Add(2);
    MyList.Add(3);
    int ver = MyList.GetVersion();
   
    //Any of these will cause IsModified to be true
    //Has the list actually been modified.
    MyList(1) = 2;
    MyList.Sort();
    MyList.Reverse();
    MyList.Reverse();
   
    //returns true
    if (MyList.IsModified(ver)) {
        Console.WriteLine("Modified");
    }

Ken Alexander

TonyT

  • Guest
Re: cool extension methods
« Reply #3 on: January 29, 2009, 05:36:18 AM »
Quote

The IsModified extension for List(of T) can be misleading. 


Sorry, I don't agree.

Perhaps it's misleading to you, but I don't think its all that
misleading, given a reasonble understanding of a few very
basic concepts, like for example, the dangers of changing
a collection of items while iterating over its elements in a
loop.

The List<T> class defines what 'modified' means, by virtue
of how it uses the internal 'version' number, and how it
reacts to attempts to modify the list while enumerating its
elements.

Quite simply, modifying a list includes adding/removing or
changing one or more of its existing elements.

Try doing any of those things to a List<> while you are
iterating over its elements within a For/Each/Next loop,
and you'll see exactly what 'modified' means.


Hi Tony,

The IsModified extension for List(of T) can be misleading.  I guess one would need to define what ‘Modified’ meant.  If the list contained three integers (1, 2, 3) and after ‘some’ processing, the list still contained three integers (1, 2, 3); has the list been modified?

Code: [Select]

    List<int> MyList = new List<int>();
    MyList.Add(1);
    MyList.Add(2);
    MyList.Add(3);
    int ver = MyList.GetVersion();
   
    //Any of these will cause IsModified to be true
    //Has the list actually been modified.
    MyList(1) = 2;
    MyList.Sort();
    MyList.Reverse();
    MyList.Reverse();
   
    //returns true
    if (MyList.IsModified(ver)) {
        Console.WriteLine("Modified");
    }


Ken Alexander

  • Newt
  • Posts: 61
Re: cool extension methods
« Reply #4 on: January 29, 2009, 06:44:06 PM »
Please enlighten me on a very basic concept that I must not understand.

With a List of Integers (1, 2, 3) and I call List.Sort, what possible advantage do I have ‘thinking’ that this list has been modified?  The List started out as (1, 2, 3), I sorted it, the List is STILL (1, 2, 3).  What CHANGED? Why would I want to think the list is modified when there is absolutely nothing different about it (other than the internal version number)? That is what is misleading about it.  If the List started out (3, 2, 1) and I call List.Sort, now the List is (1, 2, 3), the List changed; and yes, I might want to know that.

Just like objects that implement INotifyPropertyChanged.  Surely you wouldn’t raise the PropertyChanged event if the value coming is was the same as the current value.

All I am saying is if you flag something as modified and there is nothing really ‘different’ about it, it could be misleading and/or confusing as to why it's flagged modified.



Quote

The IsModified extension for List(of T) can be misleading. 


Sorry, I don't agree.

Perhaps it's misleading to you, but I don't think its all that
misleading, given a reasonble understanding of a few very
basic concepts, like for example, the dangers of changing
a collection of items while iterating over its elements in a
loop.

The List<T> class defines what 'modified' means, by virtue
of how it uses the internal 'version' number, and how it
reacts to attempts to modify the list while enumerating its
elements.

Quite simply, modifying a list includes adding/removing or
changing one or more of its existing elements.

Try doing any of those things to a List<> while you are
iterating over its elements within a For/Each/Next loop,
and you'll see exactly what 'modified' means.


Hi Tony,

The IsModified extension for List(of T) can be misleading.  I guess one would need to define what ‘Modified’ meant.  If the list contained three integers (1, 2, 3) and after ‘some’ processing, the list still contained three integers (1, 2, 3); has the list been modified?

Code: [Select]

    List<int> MyList = new List<int>();
    MyList.Add(1);
    MyList.Add(2);
    MyList.Add(3);
    int ver = MyList.GetVersion();
   
    //Any of these will cause IsModified to be true
    //Has the list actually been modified.
    MyList(1) = 2;
    MyList.Sort();
    MyList.Reverse();
    MyList.Reverse();
   
    //returns true
    if (MyList.IsModified(ver)) {
        Console.WriteLine("Modified");
    }

Ken Alexander

TonyT

  • Guest
Re: cool extension methods
« Reply #5 on: January 30, 2009, 02:39:41 PM »
Ken - You seem to have two concepts confused.

The name of the extension method is 'IsModified'.

The name of the extension method is not 'IsEqual'.

You seem to have misinterpreted my extension method
as answering the question 'is the content of a given list
equal to its previous content', verses 'has any action been
taken that modified the list'. 

IsModified() answers the second question, not the first.

IOW, you can modify a list and end up with the exact
same contents, but the list was nonetheless modified.

Please enlighten me on a very basic concept that I must not understand.

With a List of Integers (1, 2, 3) and I call List.Sort, what possible advantage do I have ‘thinking’ that this list has been modified?  The List started out as (1, 2, 3), I sorted it, the List is STILL (1, 2, 3).  What CHANGED? Why would I want to think the list is modified when there is absolutely nothing different about it (other than the internal version number)? That is what is misleading about it.  If the List started out (3, 2, 1) and I call List.Sort, now the List is (1, 2, 3), the List changed; and yes, I might want to know that.

Just like objects that implement INotifyPropertyChanged.  Surely you wouldn’t raise the PropertyChanged event if the value coming is was the same as the current value.

All I am saying is if you flag something as modified and there is nothing really ‘different’ about it, it could be misleading and/or confusing as to why it's flagged modified.



Quote

The IsModified extension for List(of T) can be misleading. 


Sorry, I don't agree.

Perhaps it's misleading to you, but I don't think its all that
misleading, given a reasonble understanding of a few very
basic concepts, like for example, the dangers of changing
a collection of items while iterating over its elements in a
loop.

The List<T> class defines what 'modified' means, by virtue
of how it uses the internal 'version' number, and how it
reacts to attempts to modify the list while enumerating its
elements.

Quite simply, modifying a list includes adding/removing or
changing one or more of its existing elements.

Try doing any of those things to a List<> while you are
iterating over its elements within a For/Each/Next loop,
and you'll see exactly what 'modified' means.


Hi Tony,

The IsModified extension for List(of T) can be misleading.  I guess one would need to define what ‘Modified’ meant.  If the list contained three integers (1, 2, 3) and after ‘some’ processing, the list still contained three integers (1, 2, 3); has the list been modified?

Code: [Select]

    List<int> MyList = new List<int>();
    MyList.Add(1);
    MyList.Add(2);
    MyList.Add(3);
    int ver = MyList.GetVersion();
   
    //Any of these will cause IsModified to be true
    //Has the list actually been modified.
    MyList(1) = 2;
    MyList.Sort();
    MyList.Reverse();
    MyList.Reverse();
   
    //returns true
    if (MyList.IsModified(ver)) {
        Console.WriteLine("Modified");
    }

« Last Edit: January 30, 2009, 02:55:22 PM by TonyT »

Ken Alexander

  • Newt
  • Posts: 61
Re: cool extension methods
« Reply #6 on: January 30, 2009, 05:43:49 PM »
Ken - You seem to have two concepts confused.

IOW, you can modify a list and end up with the exact
same contents, but the list was nonetheless modified.


You can also modify the elements of a list and the method will return False, indicating that nothing has changed.  The List is not exactly the same anymore, but IsModified returns False.

The only guarantee with this method is whether or not elements have been Added, Removed, or Replaced, but not necessarily ‘changed’.  So a more appropriate method name might be “StructureChanged”. 

However, this in itself can be misleading.  When working with a List of value types, changing one of the elements may or may not be changing its value but the element is replaced.  If you replace the Integer 2 with 3 then you have changed its value and also changed the actual element.  If you replace 2 with 2 you have replaced the element but not its value.  On the other hand if you have a List of Reference types you can make changes to the object itself without replacing the element in the List.  Thus, the List is different but the method indicates that the List has not been modified.

So really the only guarantees we can be sure of is if elements have been Added or Removed.  The end result is IsModified is too broad a term to be used with a Generic List.  You can get the same guarantee by storing the List.Count value and later comparing it for changes.  No Extension Method needed.
Ken Alexander

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8661
  • AKA Daniel
Re: cool extension methods
« Reply #7 on: January 30, 2009, 07:24:08 PM »
Thanks for sharing your extension methods methods Tony, Its great stuff.
I think I went a little overboard with them when I first learned of them.

I was looking for a ThrowIfNull extension, but I think it would make debugging a nightmare
« Last Edit: January 30, 2009, 07:57:45 PM by Daniel »

TonyT

  • Guest
Re: cool extension methods
« Reply #8 on: January 30, 2009, 11:49:45 PM »

You can also modify the elements of a list and the method will return False, indicating that nothing has changed.  The List is not exactly the same anymore, but IsModified returns False.


Ken - Feel free to demonstrate how you can modify
the elements of a list, and the method will return false.

From what I know about the internal workings of the
List<>, that's simply not the case.

Ken Alexander

  • Newt
  • Posts: 61
Re: cool extension methods
« Reply #9 on: January 31, 2009, 01:02:21 AM »

Ken - Feel free to demonstrate how you can modify
the elements of a list, and the method will return false.

From what I know about the internal workings of the
List<>, that's simply not the case.


Sure....The example shows:

Quote

When working with a List of value types, changing one of the elements may or may not be changing its value but the element is replaced.  If you replace the Integer 2 with 3 then you have changed its value and also changed the actual element.  If you replace 2 with 2 you have replaced the element but not its value.  On the other hand if you have a List of Reference types you can make changes to the object itself without replacing the element in the List.  Thus, the List is different but the method indicates that the List has not been modified.



Code: [Select]
Private Sub RunSample()

        'Work with a List of Reference Types
        Dim PersonList As New List(Of Person)

        PersonList.Add(New Person("Ken"))
        PersonList.Add(New Person("Tony"))

        Dim ver As Integer = PersonList.GetVersion()

        PersonList(0).Name = "Tom"
        PersonList(1).Name = "Joe"

        'Returns False
        If PersonList.IsModified(ver) Then
            Console.WriteLine("Modified")
        End If


        'Work with a List of Value Types
        Dim IntegerList As New List(Of Integer)

        IntegerList.Add(1)
        IntegerList.Add(2)

        ver = IntegerList.GetVersion

        IntegerList(0) = 3
        IntegerList(1) = 4

        'Returns True
        If IntegerList.IsModified(ver) Then
            Console.WriteLine("Modified")
        End If

    End Sub

Public Class Person
    Private _Name As String = String.Empty

    Public Property Name() As String
        Get
            Return _Name
        End Get
        Set(ByVal value As String)
            _Name = value
        End Set
    End Property

    Public Sub New(ByVal Name As String)
        _Name = Name
    End Sub
End Class

Ken Alexander

TonyT

  • Guest
Re: cool extension methods
« Reply #10 on: January 31, 2009, 03:30:52 AM »
Ken - Do me a favor please, and do not try to waste my
time any further, because I will have no part of it.

The fact that you can't distinguish between modifying an
object that happens to be referenced by an element in a
list, and modifying the list itself, makes it clear that any
further discussion with you about this, or for that matter,
anything else, is a complete waste of time.

That's it.  I've had it.


Ken - Feel free to demonstrate how you can modify
the elements of a list, and the method will return false.

From what I know about the internal workings of the
List<>, that's simply not the case.


Sure....The example shows:

Quote

When working with a List of value types, changing one of the elements may or may not be changing its value but the element is replaced.  If you replace the Integer 2 with 3 then you have changed its value and also changed the actual element.  If you replace 2 with 2 you have replaced the element but not its value.  On the other hand if you have a List of Reference types you can make changes to the object itself without replacing the element in the List.  Thus, the List is different but the method indicates that the List has not been modified.



Code: [Select]
Private Sub RunSample()

        'Work with a List of Reference Types
        Dim PersonList As New List(Of Person)

        PersonList.Add(New Person("Ken"))
        PersonList.Add(New Person("Tony"))

        Dim ver As Integer = PersonList.GetVersion()

        PersonList(0).Name = "Tom"
        PersonList(1).Name = "Joe"

        'Returns False
        If PersonList.IsModified(ver) Then
            Console.WriteLine("Modified")
        End If


        'Work with a List of Value Types
        Dim IntegerList As New List(Of Integer)

        IntegerList.Add(1)
        IntegerList.Add(2)

        ver = IntegerList.GetVersion

        IntegerList(0) = 3
        IntegerList(1) = 4

        'Returns True
        If IntegerList.IsModified(ver) Then
            Console.WriteLine("Modified")
        End If

    End Sub

Public Class Person
    Private _Name As String = String.Empty

    Public Property Name() As String
        Get
            Return _Name
        End Get
        Set(ByVal value As String)
            _Name = value
        End Set
    End Property

    Public Sub New(ByVal Name As String)
        _Name = Name
    End Sub
End Class



Ken Alexander

  • Newt
  • Posts: 61
Re: cool extension methods
« Reply #11 on: February 02, 2009, 11:17:36 AM »

The fact that you can't distinguish between modifying an
object that happens to be referenced by an element in a
list, and modifying the list itself, makes it clear that any
further discussion with you about this, or for that matter,
anything else, is a complete waste of time.


Uh…I made the distinction very clear here:

Quote

On the other hand if you have a List of Reference types you can make changes to the object itself without replacing the element in the List.  Thus, the List is different but the method indicates that the List has not been modified.


Quote

Ken - Do me a favor please, and do not try to waste my
time any further, because I will have no part of it.



Please extend me the same courtesy.


Ken Alexander

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: cool extension methods
« Reply #12 on: February 12, 2009, 09:48:32 AM »
Hi,

Thanks for sharing, Daniel and Tony.

I'm still trying to learn a little C# and this thread made me think about using extension methods as an issue to the polyline centroid.

Building an extension method to the Polyline Class drives me to build somme others to the Point2d and CircularArc2d classes too and also a little new class, called Triangle2d.

Am I on a good way ? or should I rather stop now ?

Code: [Select]
using System;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

namespace GeometryExtensions
{
    // Extensions
    public static class GeomExt
    {
        // Point2d
        public static Point2d Polar(this Point2d org, double angle, double distance)
        {
            return new Point2d(org.X + distance, org.Y).RotateBy(angle, org);
        }

        // CircularArc2d
        public static double AlgebricArea(this CircularArc2d arc)
        {
            double rad = arc.Radius;
            double ang = arc.IsClockWise ? arc.StartAngle - arc.EndAngle : arc.EndAngle - arc.StartAngle;
            return rad * rad * (ang - Math.Sin(ang)) / 2.0;
        }
        public static Point2d Centroid(this CircularArc2d arc)
        {
            Point2d start = arc.StartPoint;
            Point2d end = arc.EndPoint;
            double area = arc.AlgebricArea();
            double chord = start.GetDistanceTo(end);
            double angle = (end - start).Angle;
            return arc.Center.Polar(angle - (pi / 2.0), (chord * chord * chord) / (12.0 * area));
        }

        // Polyline
        public static Point2d Centroid2d(this Polyline pl)
        {
            Point2d cen = new Point2d();
            Triangle2d tri = new Triangle2d();
            CircularArc2d arc = new CircularArc2d();
            double tmpArea;
            double area = 0.0;
            int last = pl.NumberOfVertices - 1;
            Point2d p0 = pl.GetPoint2dAt(0);
            double bulge = pl.GetBulgeAt(0);

            if (bulge != 0.0)
            {
                arc = pl.GetArcSegment2dAt(0);
                area = arc.AlgebricArea();
                cen = arc.Centroid() * area;
            }
            for (int i = 1; i < last; i++)
            {
                tri.Set(p0, pl.GetPoint2dAt(i), pl.GetPoint2dAt(i + 1));
                tmpArea = tri.AlgebricArea;
                cen += (tri.Centroid * tmpArea).GetAsVector();
                area += tmpArea;
                bulge = pl.GetBulgeAt(i);
                if (bulge != 0.0)
                {
                    arc = pl.GetArcSegment2dAt(i);
                    tmpArea = arc.AlgebricArea();
                    area += tmpArea;
                    cen += (arc.Centroid() * tmpArea).GetAsVector();
                }
            }
            bulge = pl.GetBulgeAt(last);
            if (bulge != 0.0)
            {
                arc = pl.GetArcSegment2dAt(last);
                tmpArea = arc.AlgebricArea();
                area += tmpArea;
                cen += (arc.Centroid() * tmpArea).GetAsVector();
            }
            return cen.DivideBy(area);
        }
        public static Point3d Centroid3d(this Polyline pl)
        {
            Point2d cen = pl.Centroid2d();
            Point3d result = new Point3d(cen.X, cen.Y, pl.Elevation);
            return result.TransformBy(Matrix3d.PlaneToWorld(pl.Normal));
        }

        private const double pi = 3.141592653589793;
    }

    // Class
    public class Triangle2d
    {
        public Triangle2d()
        {
        }
        public Triangle2d(Point2d a, Point2d b, Point2d c)
        {
            p0 = a;
            p1 = b;
            p2 = c;
        }
        public Triangle2d(Point2d[] pts)
        {
            p0 = pts[0];
            p1 = pts[1];
            p2 = pts[2];
        }

        public Point2d Point0
        {
            get { return p0; }
            set { p0 = value; }
        }
        public Point2d Point1
        {
            get { return p1; }
            set { p1 = value; }
        }
        public Point2d Point2
        {
            get { return p2; }
            set { p2 = value; }
        }
        public Point2d Centroid
        {
            get { return (p0 + p1.GetAsVector() + p2.GetAsVector()) / 3.0; }
        }
        public double AlgebricArea
        {
            get { return (((p1.X - p0.X) * (p2.Y - p0.Y)) - ((p2.X - p0.X) * (p1.Y - p0.Y))) / 2.0; }
        }
        public bool IsClockwise
        {
            get { return (this.AlgebricArea < 0.0); }
        }

        public void Set(Point2d a, Point2d b, Point2d c)
        {
            p0 = a;
            p1 = b;
            p2 = c;
        }
        public double GetAngleAt(int index)
        {
            double ang;
            switch (index)
            {
                case 0 :
                    ang = Math.Abs((p0 - p1).Angle - (p0 - p2).Angle);
                    break;
                case 1 :
                    ang = Math.Abs((p1 - p0).Angle - (p1 - p2).Angle);
                    break;
                case 2 :
                    ang = Math.Abs((p2 - p0).Angle - (p2 - p1).Angle);
                    break;
                default :
                    throw new ArgumentOutOfRangeException("L'index doit être 0, 1 ou 2");
            }
            if (ang > pi * 2)
                return pi * 2 - ang;
            else
                return ang;
        }

        private Point2d p0, p1, p2;
        private const double pi = 3.141592653589793;
    }
}
Speaking English as a French Frog

TonyT

  • Guest
Re: cool extension methods
« Reply #13 on: February 12, 2009, 12:32:00 PM »
Nothing wrong with it, except I might do Polar() differently (sin/cos).

Hi,

Thanks for sharing, Daniel and Tony.

I'm still trying to learn a little C# and this thread made me think about using extension methods as an issue to the polyline centroid.

Building an extension method to the Polyline Class drives me to build somme others to the Point2d and CircularArc2d classes too and also a little new class, called Triangle2d.

Am I on a good way ? or should I rather stop now ?

Code: [Select]
using System;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

namespace GeometryExtensions
{
    // Extensions
    public static class GeomExt
    {
        // Point2d
        public static Point2d Polar(this Point2d org, double angle, double distance)
        {
            return new Point2d(org.X + distance, org.Y).RotateBy(angle, org);
        }

        // CircularArc2d
        public static double AlgebricArea(this CircularArc2d arc)
        {
            double rad = arc.Radius;
            double ang = arc.IsClockWise ? arc.StartAngle - arc.EndAngle : arc.EndAngle - arc.StartAngle;
            return rad * rad * (ang - Math.Sin(ang)) / 2.0;
        }
        public static Point2d Centroid(this CircularArc2d arc)
        {
            Point2d start = arc.StartPoint;
            Point2d end = arc.EndPoint;
            double area = arc.AlgebricArea();
            double chord = start.GetDistanceTo(end);
            double angle = (end - start).Angle;
            return arc.Center.Polar(angle - (pi / 2.0), (chord * chord * chord) / (12.0 * area));
        }

        // Polyline
        public static Point2d Centroid2d(this Polyline pl)
        {
            Point2d cen = new Point2d();
            Triangle2d tri = new Triangle2d();
            CircularArc2d arc = new CircularArc2d();
            double tmpArea;
            double area = 0.0;
            int last = pl.NumberOfVertices - 1;
            Point2d p0 = pl.GetPoint2dAt(0);
            double bulge = pl.GetBulgeAt(0);

            if (bulge != 0.0)
            {
                arc = pl.GetArcSegment2dAt(0);
                area = arc.AlgebricArea();
                cen = arc.Centroid() * area;
            }
            for (int i = 1; i < last; i++)
            {
                tri.Set(p0, pl.GetPoint2dAt(i), pl.GetPoint2dAt(i + 1));
                tmpArea = tri.AlgebricArea;
                cen += (tri.Centroid * tmpArea).GetAsVector();
                area += tmpArea;
                bulge = pl.GetBulgeAt(i);
                if (bulge != 0.0)
                {
                    arc = pl.GetArcSegment2dAt(i);
                    tmpArea = arc.AlgebricArea();
                    area += tmpArea;
                    cen += (arc.Centroid() * tmpArea).GetAsVector();
                }
            }
            bulge = pl.GetBulgeAt(last);
            if (bulge != 0.0)
            {
                arc = pl.GetArcSegment2dAt(last);
                tmpArea = arc.AlgebricArea();
                area += tmpArea;
                cen += (arc.Centroid() * tmpArea).GetAsVector();
            }
            return cen.DivideBy(area);
        }
        public static Point3d Centroid3d(this Polyline pl)
        {
            Point2d cen = pl.Centroid2d();
            Point3d result = new Point3d(cen.X, cen.Y, pl.Elevation);
            return result.TransformBy(Matrix3d.PlaneToWorld(pl.Normal));
        }

        private const double pi = 3.141592653589793;
    }

    // Class
    public class Triangle2d
    {
        public Triangle2d()
        {
        }
        public Triangle2d(Point2d a, Point2d b, Point2d c)
        {
            p0 = a;
            p1 = b;
            p2 = c;
        }
        public Triangle2d(Point2d[] pts)
        {
            p0 = pts[0];
            p1 = pts[1];
            p2 = pts[2];
        }

        public Point2d Point0
        {
            get { return p0; }
            set { p0 = value; }
        }
        public Point2d Point1
        {
            get { return p1; }
            set { p1 = value; }
        }
        public Point2d Point2
        {
            get { return p2; }
            set { p2 = value; }
        }
        public Point2d Centroid
        {
            get { return (p0 + p1.GetAsVector() + p2.GetAsVector()) / 3.0; }
        }
        public double AlgebricArea
        {
            get { return (((p1.X - p0.X) * (p2.Y - p0.Y)) - ((p2.X - p0.X) * (p1.Y - p0.Y))) / 2.0; }
        }
        public bool IsClockwise
        {
            get { return (this.AlgebricArea < 0.0); }
        }

        public void Set(Point2d a, Point2d b, Point2d c)
        {
            p0 = a;
            p1 = b;
            p2 = c;
        }
        public double GetAngleAt(int index)
        {
            double ang;
            switch (index)
            {
                case 0 :
                    ang = Math.Abs((p0 - p1).Angle - (p0 - p2).Angle);
                    break;
                case 1 :
                    ang = Math.Abs((p1 - p0).Angle - (p1 - p2).Angle);
                    break;
                case 2 :
                    ang = Math.Abs((p2 - p0).Angle - (p2 - p1).Angle);
                    break;
                default :
                    throw new ArgumentOutOfRangeException("L'index doit être 0, 1 ou 2");
            }
            if (ang > pi * 2)
                return pi * 2 - ang;
            else
                return ang;
        }

        private Point2d p0, p1, p2;
        private const double pi = 3.141592653589793;
    }
}

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: cool extension methods
« Reply #14 on: February 12, 2009, 12:52:27 PM »
Thanks for your reply, Tony.

Quote
I might do Polar() differently (sin/cos)
You meant something like this ?
Code: [Select]
public static Point2d Polar(this Point2d org, double angle, double distance)
        {
            return org + new Vector2d(Math.Cos(angle), Math.Sin(angle)).MultiplyBy(distance);
        }
« Last Edit: February 12, 2009, 12:57:20 PM by gile »
Speaking English as a French Frog