TheSwamp

Code Red => .NET => Topic started by: Bryco on May 22, 2007, 12:00:02 AM

Title: Overloading types.
Post by: Bryco on May 22, 2007, 12:00:02 AM
This overloading is you beaut, but it seems like sometimes you need a few extra choices.

Code: [Select]
       
 static public Arc Arc(Point3d Cen,Vector3d V, double Rad,double startAng,double endAng,string sOwner) 
            //standard arc
 
        static public Arc Arc(Center Center, Point3d StartP, Point3d EndP, string sOwner)
        //3 point arc with center
   
        static public Arc Arc( Point3d StartP,Point3d MidP, Point3d EndP, string sOwner)
        //3 point arc
   


Both the 3 point arc with center and the 3 point arc  have the same arguments so I was messing with making Center a class just to make it different than a Point3d, I would almost rather it was an enum but I can't figure that out.
Any ideas?
Title: Re: Overloading types.
Post by: sinc on May 22, 2007, 12:30:41 AM
This is a really heavy-handed reason for using a class, or even a struct.  For a case like that, I would tend to lean toward creating another method called Arc3P() or something like that.

If you want to use an enum, you could do that too, but it would require adding another argument to your command, which gets kind of messy:

Code: [Select]
public enum ArcCreationMethod
{
    CenterAndEndpoints,
    ThreePointCurve
}

static public Arc Arc(ArcCreationMethod method, Point3d param1, Point3d param2, Point3d param3, string sOwner)
Title: Re: Overloading types.
Post by: Bryco on May 22, 2007, 12:47:01 AM
I'm giving this a go, thanks.
Title: Re: Overloading types.
Post by: Bryco on May 22, 2007, 01:36:42 AM
So far it doesn't seem to work,
 Error   1   Type 'Util.AddEnt' already defines a member called 'Arc' with the same parameter types.
But I do like your Arc3P() idea.
string sOwner is a way to figure out if the item is to be added to Paperspace,modelspace,Currentspace or block.
This is where I started thinking about enums and while they work perfect for the first 3, I can't figure out how to pass either a stringname or an id for the fourth.   
Title: Re: Overloading types.
Post by: MickD on May 22, 2007, 02:13:00 AM
You are overloading your constructor correct?
If so, create a few constructors as you have but make sure you have a default ctor that takes no arg's. This way you can create a few arc creation methods that can return a new arc as a return type (as your ctor is really doing) and return a new arc to the 'null' arc you defined like-

Arc myarc;
myarc = Arc.Arc2pntAng(...);

In fact, you could simply call your arc create methods inside your ctors passing the param's as required that way you still have both choices to assign on definition or after you declare -

public Arc Arc(p1,p2,angle)
{
    return = Arc2pntsAng(p1,p2,ang);
}

// the method
public Arc Arc2pntsAng(p1,p2,angle)
{
    Arc tmparc = new Arc();
    tmparc = Arc2pntsAng(p1,p2,ang);
    return tmparc;
}

<edit> added ctor and method

you may have to make the class methods static though and you will still have only limited ctor's but at least you still have a way to create different methods. Perhaps not create ctor's at all and just enforce the creation??
It's only food for thought though, it's been a while with c# :)

hth.
Title: Re: Overloading types.
Post by: Glenn R on May 22, 2007, 02:19:09 AM
Instead of 'sOwner', why not pass in the ObjectId of the block table record to add it to...? (ie. the current space for example)
Title: Re: Overloading types.
Post by: Bryco on May 22, 2007, 02:43:23 AM
Mick, I think if I do that I still have to start with 2 ctors  e.g public Arc Arc(p1,p2,p3) and public Arc Arc(p1,Cen,p2) that have the same parameter list.

Glenn, since there is virtually no globals I have a sub that is the full monty way of adding an ent. Otherwise I was finding C# was way more verbose than vba. With as BlockTableRecord btr here and a BlockTableRecord btr there etc.
 
Code: [Select]
static private void AddEntTtoDb(Entity Ent, string sOwner)
        {
            BlockTable bt;
            BlockTableRecord btr;
            Database db = HostApplicationServices.WorkingDatabase;
            using (Transaction trans = db.TransactionManager.StartTransaction())
            {
                bt = (BlockTable)trans.GetObject(db.BlockTableId, OpenMode.ForWrite);
                switch (sOwner)
                {
                    case "Ms":
                        btr = (BlockTableRecord)trans.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite);
                        break;
                    case "Ps":
                        btr = (BlockTableRecord)trans.GetObject(bt[BlockTableRecord.PaperSpace], OpenMode.ForWrite);
                        break;
                    case "Cs":
                        btr = (BlockTableRecord)trans.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
                        break;
                    default:
                        if ((bt.Has(sOwner)))
                        {
                            btr = (BlockTableRecord)trans.GetObject(bt[sOwner], OpenMode.ForWrite);
                            Util.GetandPrint.Print("The block " + btr.Name + " exists");
                        }
                        else
                        {

                            btr = new BlockTableRecord();
                            btr.Name = sOwner;
                            bt.Add(btr);
                            trans.AddNewlyCreatedDBObject(btr, true);

                        }

                        break;
                }

                btr.AppendEntity(Ent);
                trans.AddNewlyCreatedDBObject(Ent, true);

       
            trans.Commit();
            }

So I don't need to know the id's but I already know the block name.
Perhaps the best way is a simple Constant
        public const string Ms = "ModelSpace";
        public const string Ps = "PaperSpace";
        public const string Cs = "CurrentSpace";
While this is not as nice as an enum it is available in intellisense.

Title: Re: Overloading types.
Post by: Kerry on May 22, 2007, 04:34:33 AM

Bryco, did you know these were equivalent ?

Have a look at the Fields .Modelspace and  .Paperspace of the BlockTableRecord .. The fields are String Constants, which you could pass in in you wanted to ..
Code: [Select]
              BlockTableRecord modelSpaceRecord1 = (BlockTableRecord)tr.GetObject(
                  bt[BlockTableRecord.ModelSpace],
                  OpenMode.ForWrite);   

              BlockTableRecord modelSpaceRecord2 = (BlockTableRecord)tr.GetObject(
                  bt["*MODEL_SPACE"],
                  OpenMode.ForWrite);

.... but I'd personally pass the ObjectID of the container I wanted the Ent added to .. as Glenn mentioned.
Title: Re: Overloading types.
Post by: It's Alive! on May 22, 2007, 06:09:07 AM
.... but I'd personally pass the ObjectID of the container I wanted the Ent added to .. as Glenn mentioned.

Sorry to ask, but may we have an example?
Title: Re: Overloading types.
Post by: sinc on May 22, 2007, 08:15:28 AM
I had started getting the impression you are trying to define a Util.AddEnt class, and these were methods that created arcs in your utility class.

If they are constructors for an Arc class, they should not have a return value.

Code: [Select]
public class Arc
{
    ; this is a constructor
    public Arc()
   {
   }
}
Code: [Select]
using SomethingWithAnArcClass;
namespace Util;
{
    public class AddEnt()
    {
        ; this is the constructor for the AddEnt class
        public AddEnt()
        {
        }

        ; this is NOT a constructor
        public Arc Arc()
       {
       }
    }
}
In any case, the only way to use method overloading is to have arguments with distinct types.  That's how overloading works.  But overloading is simply a convenience for the programmer; it is not a goal.  You are gaining nothing good by trying to FORCE your code to use overloading.  All overloading lets you do is avoid having a lot of methods like ArcFromStartAndEndAngles(), ArcFromCenterAndTwoPoints(), and Arc3P().  Overloading lets you use the same name for the first two.  But because the arguments are the same, you can't use operator overloading for the last one.  Instead, you can use the names Arc(), Arc(), and Arc3P(), and the compiler will be able to tell the difference between the first two methods by looking at the arguments.
Title: Re: Overloading types.
Post by: LE on May 22, 2007, 10:25:39 AM
I do not know what you are doing...

Quote
static private void AddEntTtoDb(Entity Ent, string sOwner)

If you have a chance, look for the "MgdDbg" project by Jim Awe from Adesk, and read the "SymTbl.cs"

http://discussion.autodesk.com/thread.jspa?messageID=4907654

hth
Title: Re: Overloading types.
Post by: It's Alive! on May 22, 2007, 11:46:30 AM
I do not know what you are doing...

Quote
static private void AddEntTtoDb(Entity Ent, string sOwner)

If you have a chance, look for the "MgdDbg" project by Jim Awe from Adesk, and read the "SymTbl.cs"

http://discussion.autodesk.com/thread.jspa?messageID=4907654

hth


That's it!
Thanks Luis
Title: Re: Overloading types.
Post by: Bryco on May 23, 2007, 12:07:00 AM
Kerry,*MODEL_SPACE point taken.
sinc, good advice again.
Le, that's opened plenty of doors. Great find.

Title: Re: Overloading types.
Post by: TonyT on May 26, 2007, 03:44:32 AM
Glenn, since there is virtually no globals I have a sub that is the full monty way of adding an ent. Otherwise I was finding C# was way more verbose than vba. With as BlockTableRecord btr here and a BlockTableRecord btr there etc.

Well, if I may say so myself, I've found that trying to invent
your own API that offers the 'full monty' way of doing things,
will ultimately create more problems than it solves.

API design is an art, and is not as simple as it often seems.

The problem with your approach to a 'full monty' way of
adding an ent to a block, is that if the code is used in a
context where the name of the block is coming from user
input, and the user intended to give the name of an
existing block, but mistyped it, your code just creates the
block and doesn't return an error.

The point here is that you really can't avoid the 'verbose'
nature of ObjectARX programming, and this is an excellent
example of why.

If the code that calls that sub doesn't do what it should to
check to see if a block exists (e.g., the verbose code that
your full monty sub is supposed to avoid) when it knows or
can assume that the block should exist, and rather, blindly
passes the name of the block to your full monty sub, you
will quickly find your code riddled with hard to find bugs.

You also haven't learned about the problem with the default
indexer and the Has() method yet (they return erased entries),
and that means that to find out if a block exists, you must
test the IsErased property of the object id returned by the
default indexer, and if it is erased, you must iterate over the
entire table to find another non-erased entry with the same
name.

So, while your sub may seem seem like a great convenience
at its conception, I think after using it for real, you will quickly
learn that it has the potential to be more of a problem than a
solution.  I'm not trying to knock it down for the sake of doing
that, I'm trying to show why someone that is just learniing to
use an API is not yet able to see every potential problem in
their code until the one using it is unceremoneously confronted
with an exception dialog,

It's better bite the bullet and contend with the verbose nature
of the API, and focus on learning to use it, rather than trying
to come up with ways of hiding or insulating yourself from it,
as this sub appears to be a case of.

It's hard to learn an API if you're trying to hide from it :)

Title: Re: Overloading types.
Post by: Bobby C. Jones on May 26, 2007, 11:28:59 AM
Glenn, since there is virtually no globals I have a sub that is the full monty way of adding an ent. Otherwise I was finding C# was way more verbose than vba. With as BlockTableRecord btr here and a BlockTableRecord btr there etc.

Well, if I may say so myself, I've found that trying to invent
your own API that offers the 'full monty' way of doing things,
will ultimately create more problems than it solves.

<snip>
I traversed this same path, asked myself the same questions you have asked yourself, and lamented all the extra work involved in using this API.  But in the end, I agree with Tony.

We all know however that we are lazy (http://bobbycjones.spaces.live.com/blog/cns!2A36783BF92E8178!167.entry) and typing is not how we want to spend our days.  If you're using Visual Studio as your IDE, I suggest Code Snippets (http://bobbycjones.spaces.live.com/blog/cns!2A36783BF92E8178!170.entry).  You can create snippets that encapsulate a logical action, like adding an entity to a database, although most of mine aren't quite that chunky, and modify the code after it's inserted. 
Title: Re: Overloading types.
Post by: Bryco on May 26, 2007, 06:19:11 PM
Great stuff.
Thanks Tony and Bobby (on my favourites).
It seemed like a good idea at the time but I see the danger now.
I'll try catch up on this this weekend.
Title: Re: Overloading types.
Post by: TonyT on May 26, 2007, 07:31:06 PM
Code snippits are great for inserting simple frequently used constructs,
like try/finally try/catch and so on. But, I don't know if I would use it
to replicate more complex, commonly used constructs rather than
abstracting that into a reusable form, which eliminates the need for
replication (replication of code is something I try to avoid as much as
possible).

As an example, I found that writing very simple editing commands in
.NET was extremely tedious compared to LISP, where it can be done
in typically less than half a dozen lines of code. In .NET, there was
much more code needed, to build simple commands that had no UI
and used the command line (e.g., like the 'ERASE' command).

To make it easier for me to quickly build those kind of commands in
.NET (where the pattern is to prompt for a selection set of objects,
possibly filtered, and then perform some action on each selected
object), I wrote a class that I can use to easily implement those kind
of commands, which factors out 95% of the 'grunt work' needed and
reduces the actual amount of code required to the absolute minimum.

The code in the class is consumed/reused by deriving new classes
from it, typically one for each editing command to be built. Below
are a few examples of how the resuable code in the class is consumed.

Note that most of the grunt work typically required by commands
that prompt for and then operate on a user selection, is delegated
to the base classes, e.g., things like document locking; transaction
management; object selection with filtering; iterating through the
selection; and even opening the objects.

The derived class merely needs to override a virtual method (called
ProcessEntity), that is called once for each each selected object,
that's passed as the parameter and is already open, and there it
can do what it needs to the object and bada bing, bada boom... done.

Of course, there's some performance overhead, but my experience
shows it to be neglegible, and certainly worth the productivtity gain.


Code: [Select]

namespace CaddZone.AutoCAD.EditorServices
{

   ////////////////////////////////////////////////////////////////////
   // A class that implements a command to reset the color
   // of selected block references to BYLAYER:
  
   public class BlockRefPropertyReset : SelectionEditor<BlockReference>
   {
      protected override void ProcessEntity(BlockReference blockref)
      {
          blockref.ColorIndex = 256;
          return EditorStatus.Continue;
      }
     
      [CommandMethod("BLOCKCOLORTOBYLAYER", CommandFlags.UsePickSet)]
      public override void CommandHandler()
      {
         Execute();
      }
   }

   ///////////////////////////////////////////////////////////
   /// CIRCLEOFFSET command:
   ///
   /// This specialization of SelectionEditor implements
   /// an editor that prompts you to select one or more
   /// circles, and enter a distance. It then offsets each
   /// selected circle's radius by the specified distance.
   ///
   /// The larger thing which the trained eye will notice
   /// about this, is that we have successfuly factored
   /// out 95% of the 'grunt work' that must typically
   /// be done in order to build relatively simple custom
   /// AutoCAD commands, using the .NET API.
   ///

   public class CircleOffsetEditor : SelectionEditor<Circle>
   {
      // Called after objects are selected, to
      // obtain additional input:

      protected override PromptStatus EndEditorInput( Editor ed )
      {
         PromptDistanceOptions ops = new PromptDistanceOptions( "" );
         ops.Message = "\nCircle radius offset: ";
         ops.AllowZero = false;
         ops.DefaultValue = offset;
         PromptDoubleResult res = ed.GetDistance( ops );
         if( res.Status == PromptStatus.OK )
            offset = res.Value;    // store the value for use in ProcessObject()
         return res.Status;
      }

      /// <summary>
      ///  Override this to gain access to the PromptSelectionOptions
      ///  that's used to get the selection of objects. Here we just
      ///  set the prompts to "Select/remove circles: "
      /// </summary>

      protected override void BeginSelect( PromptSelectionOptions options )
      {
         base.BeginSelect( options );
         options.MessageForAdding = "Select circles: ";
         options.MessageForRemoval = "Remove circles: ";
      }

      /// <summary>
      /// This override is called once for each selected
      /// object, which is passed in as the parameter.
      ///
      /// The passed in object is already open for Write,
      /// and cast to the Type of the generic parmeter
      /// argument to this class (a Circle in this case).
      ///
      /// This is where each object is Modified.
      ///
      /// </summary>

      protected override EditorStatus ProcessEntity( Circle circle )
      {
         if( circle.Radius + offset > 0.0 )
            circle.Radius += offset;

         return EditorStatus.Continue;  // continue processing objects
      }

      // user-supplied offset distance:
      private double offset = 1.0;

      // Implement a command for this editor
      [CommandMethod("CIRCLEOFFSET", CommandFlags.UsePickSet)]
      public override void CommandHandler()
      {
         Execute();
      }

   }

   ////////////////////////////////////////////////////////////////////
   // A class that implements the 'LENGTH' command, which
   // measures the length of selected curves:
  
   public class CurveLengthReporter : SelectionViewer<Curve>
   {
      protected override void Initialize()
      {
         totalLength = 0.0;
         counted = 0;
         base.Initialize();
      }

      protected override EditorStatus ProcessEntity( Curve curve )
      {
         try           // Ignore eNotApplicable returned by rays/xlines
         {
            totalLength += curve.GetDistanceAtParameter( curve.EndParam );
            ++counted;
         }
         catch (Autodesk.AutoCAD.Runtime.Exception ex)
         {
             if( ex.ErrorStatus != ErrorStatus.NotApplicable )
                throw ex;
         }
         return EditorStatus.Continue;
      }

      protected override string GetReportMessage()
      {
         if( counted > 0 )
         {
            return string.Format(
               "Total length of {0} curves: {1}, average length: {2}",
                     counted, totalLength, totalLength / counted );
         }
         else
            return "No curves were processed";
      }

      [CommandMethod( "LENGTH", CommandFlags.UsePickSet )]
      public override void CommandHandler()
      {
          Execute();
      }

      private Distance totalLength = 0.0;
      private int counted = 0;
   }

   //////////////////////////////////////////////////////////////
   /// The SCALEWIDTH command
   /// <summary>
   ///
   /// The SCALEWIDTH command applies a scale factor to the
   /// constant widths of a selection of polyLines.
   ///
   /// Custom filtering:
   ///
   /// This class also utilizes custom selection filtering, where
   /// an overridden method of the base class examines each
   /// object selected by the user, and acccepts or rejects it.

   /// Custom filtering is enabled by overriding the FilterObject()
   /// method. At runtime the SelectionEditor class will detect if
   /// this method has been overridden in a derived class, and if
   /// so, it will invoke the method to allow it to filter each object
   /// selected by the user.

   public class PolylineWidthScaler : SelectionEditor<Polyline>
   {
      // Get the scale factor to apply to each Polyline's width:
      protected override PromptStatus EndEditorInput( Editor editor )
      {
         PromptDoubleOptions ops = new PromptDoubleOptions( "\nWidth scale factor: " );
         ops.AllowNegative = false;
         ops.AllowZero = false;
         ops.DefaultValue = scaleFactor;
         PromptDoubleResult res = editor.GetDouble( ops );
         if( res.Status != PromptStatus.OK )
            return res.Status;
         if( Math.Abs( res.Value - 1.0 ) < 1.0e-6 )
         {
            Prompt( "\nScale factor cannot be 1.0," );
            return PromptStatus.Other;
         }
         scaleFactor = res.Value;
         return res.Status;
      }

      // Filter out polyLines whose constant width is nominally 0
      protected override bool FilterEntity( Polyline polyline )
      {
         return polyline.ConstantWidth > 1.0e-6;
      }

      // Operate on each Polyline
      protected override EditorStatus ProcessEntity( Polyline polyline )
      {
         polyline.ConstantWidth *= scaleFactor;
         return EditorStatus.Continue;
      }

      [CommandMethod( "SCALEWIDTH", CommandFlags.UsePickSet )]
      public override void CommandHandler()
      {
         base.CommandHandler();
      }

      private double scaleFactor = 1.0;
   }

   // only to show how rediculously simple it is to build commands
   // with SelectionEditor<>, this example prompts for a selection set
   // of circles, and changes their color to RED, and does it in only 4
   // lines of code:

   public class MakeCirclesRed : SelectionEditor<Circle>
   {
      protected override EditorStatus ProcessEntity(Circle circle)
      {
          circle.ColorIndex = 1;
          return EditorStatus.Continue;
      }
   }

   // To invoke the command, just use:  new MakeCirclesRed().Execute();
}


So, while there's nothing wrong with snippets for reducing typing,
I think they can also be misused to replicate larger code constructs
that possibly shouldn't be replicated if that can be avoided through
carefully designed modular, reusable classes.

Glenn, since there is virtually no globals I have a sub that is the full monty way of adding an ent. Otherwise I was finding C# was way more verbose than vba. With as BlockTableRecord btr here and a BlockTableRecord btr there etc.

Well, if I may say so myself, I've found that trying to invent
your own API that offers the 'full monty' way of doing things,
will ultimately create more problems than it solves.

<snip>
I traversed this same path, asked myself the same questions you have asked yourself, and lamented all the extra work involved in using this API.  But in the end, I agree with Tony.

We all know however that we are lazy (http://bobbycjones.spaces.live.com/blog/cns!2A36783BF92E8178!167.entry) and typing is not how we want to spend our days.  If you're using Visual Studio as your IDE, I suggest Code Snippets (http://bobbycjones.spaces.live.com/blog/cns!2A36783BF92E8178!170.entry).  You can create snippets that encapsulate a logical action, like adding an entity to a database, although most of mine aren't quite that chunky, and modify the code after it's inserted. 
Title: Re: Overloading types.
Post by: Kerry on May 26, 2007, 08:12:42 PM
Code snippits are great for inserting simple frequently used constructs,
like try/finally try/catch and so on. But, I don't know if I would use it
to replicate more complex, commonly used constructs rather than
abstracting that into a reusable form, which eliminates the need for
replication (replication of code is something I try to avoid as much as
possible).

.............>>> <<<

So, while there's nothing wrong with snippets for reducing typing,
I think they can also be misused to replicate larger code constructs
that possibly shouldn't be replicated if that can be avoided through
carefully designed modular, reusable classes.


I found my head nodding in agreemant as I read this. I've played a little with snippets .. { you'll find a couple of threads in this forum discussing snippets and XML editors } .. and had grandiose visions of writing large tracts of code with a few keystrokes ; untill I slapped myself up the side of my head and came back to reality. The name 'snippets' is a perfect description of how I use them now. 

Thanks for the comment Tony. Just as importantly, thanks for the code example. There is a lot to be learnt from that seemingly simple example.
Title: Re: Overloading types.
Post by: Kerry on May 26, 2007, 09:29:37 PM
Bryco,
To come back to the Overloading ..
Here's a couple of examples of Method Overloading you may find interesting.

 
        // class Autodesk.AutoCAD.Runtime.Converter
        // enum Autodesk.AutoCAD.Runtime.DistanceUnitFormat
        // struct Autodesk.AutoCAD.Geometry.Point3d
        //--------
        public static string PtToStr(Point2d pt)
        {
            return PtToStr(pt, DistanceUnitFormat.Current, -1);
        }
        //--------
        public static string PtToStr(Point3d pt)
        {
            return PtToStr(pt, DistanceUnitFormat.Current, -1);
        }
        //--------
        public static string PtToStr(Point2d pt, DistanceUnitFormat unitType, int prec)
        {           
            return string.Format("({0}, {1})",
                Converter.DistanceToString(pt.X, unitType, prec),
                Converter.DistanceToString(pt.Y, unitType, prec));
        }
        /// <summary>
        ///
        /// </summary>
        /// <param name="pt"></param>
        /// <param name="unitType"></param>
        /// <param name="prec"></param>
        /// <returns></returns>
        public static string PtToStr(Point3d pt, DistanceUnitFormat unitType, int prec)
        {
            return string.Format("({0}, {1}, {2})",
                Converter.DistanceToString(pt.X, unitType, prec),
                Converter.DistanceToString(pt.Y, unitType, prec),
                Converter.DistanceToString(pt.Z, unitType, prec));
        }
 

 
        //
        //--------
        public static void Rotate(Entity ent, Point3d basePoint, double rotationAngle)
        {
            Rotate(ent.ObjectId, basePoint, rotationAngle);
        }
        /// <summary>
        ///
        /// </summary>
        /// <param name="id"></param>
        /// <param name="basePoint"></param>
        /// <param name="rotationAngleInRadians"></param>
        public static void Rotate(ObjectId id, Point3d basePoint, double rotationAngleInRadians)
        {
            // note: System.Math.PI is const 3.1415926535897931
            // struct Autodesk.AutoCAD.Geometry.Matrix3d
            // struct Autodesk.AutoCAD.Geometry.Vector3d
            // Matrix3d transform = Matrix3d.Rotation(((rotationAngleInDegrees * Math.PI) / 180,
            //
            Matrix3d transform = Matrix3d.Rotation(
                rotationAngleInRadians, 
                new Vector3d(0, 0, 1),
                basePoint);
            using (Transaction tr =
                AcadApp.DocumentManager.MdiActiveDocument.Database.TransactionManager.StartTransaction())
            {
                ((Entity)tr.GetObject(id, OpenMode.ForWrite, true)).TransformBy(transform);
                tr.Commit();
            }
        }

 
        //--------
        //--------       
        public static void Move(Entity ent, Point3d fromPoint, Point3d toPoint)
        {
            Move(ent.ObjectId, fromPoint, toPoint);
        }
        //--------
        /// <summary>
        ///
        /// </summary>
        /// <param name="id"></param>
        /// <param name="fromPoint"></param>
        /// <param name="toPoint"></param>
        public static void Move(ObjectId id, Point3d fromPoint, Point3d toPoint)
        {
            Matrix3d transform = Matrix3d.Displacement(fromPoint.GetVectorTo(toPoint));
            using (Transaction tr =
                AcadApp.DocumentManager.MdiActiveDocument.Database.TransactionManager.StartTransaction())
            {
                ((Entity)tr.GetObject(id, OpenMode.ForWrite, true)).TransformBy(transform);
                tr.Commit();
            }
        }

 
        //--------
        //--------
         public static bool SelectPoint01(string Message, out Point3d pt )
        {
            return SelectPoint01(Message,  out pt, new Point3d(), false );
        }   
        /// <summary>
        ///
        /// </summary>
        /// <param name="Message"></param>
        /// <param name="pt"></param>
        /// <param name="defVal"></param>
        /// <param name="useDefVal"></param>
        /// <returns></returns>
        public static bool SelectPoint01(string Message,  out Point3d pt,Point3d defVal, bool useDefVal  )
        {
            Editor ed = AcadApp.DocumentManager.MdiActiveDocument.Editor;
            bool result = false;
            PromptPointOptions ppo = new PromptPointOptions(Message);
            if (useDefVal)
            {
                ppo.BasePoint = defVal;
                ppo.UseDashedLine = true;
                ppo.UseBasePoint = true;
                ppo.AllowNone = true;
            }

            PromptPointResult ppr = ed.GetPoint(ppo);
            switch (ppr.Status)
            {
                case PromptStatus.Cancel:
                    ed.WriteMessage("\nUser canceled.");                   
                    break ;
                case PromptStatus.Error:
                    ed.WriteMessage("\nUser input error.");                   
                    break;
                case PromptStatus.OK:
                    result = true;
                    break;
            }
            pt = ppr.Value;
            return result;
        }

... and a quick tester
 
        //--------
        //--------
        [CommandMethod("TestPoints")]
        public static void TestPoints()
        {
            Editor ed = AcadApp.DocumentManager.MdiActiveDocument.Editor;
            Point3d pt = new Point3d(0, 0, 0);
            bool result = KdubUtils.Utility.SelectPoint01("Select a Point ", out pt);
            if (result)
            {
                MessageBox.Show("TestPoint_01 Result :" + KdubUtils.Utility.PtToStr(pt));
            }
            bool result2 = KdubUtils.Utility.SelectPoint01("Select a Point ", out pt, pt, true);
            if (result2)
            {
                MessageBox.Show("TestPoint_02 Result :" + KdubUtils.Utility.PtToStr(pt));
            }
        }


Title: Re: Overloading types.
Post by: Kerry on May 27, 2007, 02:07:00 AM
.. and a tester for the Move and Rotate Methods

 
        [CommandMethod("TestMove")]
        public static void TestMove()
        {
            Editor ed = AcadApp.DocumentManager.MdiActiveDocument.Editor;
            PromptEntityResult peResult = ed.GetEntity("\nSelect an entity to Move: ");
            Point3d pt1 = ed.GetPoint("\nMove From: ").Value;
            Point3d pt2 = ed.GetPoint("\nMove To: ").Value;
            KdubUtils.Utility.Move(peResult.ObjectId, pt1, pt2);
        }
        //------------
        [CommandMethod("TestRotate")]
        public static void TestRotate()
        {
            Editor ed = AcadApp.DocumentManager.MdiActiveDocument.Editor;
            PromptEntityResult peResult = ed.GetEntity("\nSelect an entity to Rotate: ");
            Point3d pt1 = ed.GetPoint("\nRotate From: ").Value;
            PromptAngleOptions paOptions = new PromptAngleOptions("\nEnter or Select an Angle: ");
            paOptions.AllowZero = true;
            paOptions.AllowNone = false;
            paOptions.DefaultValue = Math.PI * 45.0 / 180; // in Radians
            paOptions.UseDefaultValue = true;
            paOptions.BasePoint = pt1;
            paOptions.UseBasePoint = true;
            double ang1 = ed.GetAngle(paOptions).Value;
            // MessageBox.Show("Angle is :" + Converter.AngleToString(ang1));
            // ang1 is expressed in radians           
            KdubUtils.Utility.Rotate(peResult.ObjectId, pt1, ang1);
        }
Title: Re: Overloading types.
Post by: TonyT on May 27, 2007, 03:55:06 AM
In addition, here's something that might be useful for
those who want to perform conversions of things like
points, angles, and distances in a way that conforms
to the .NET component model and therefore supports
controls like PropertyGrid and DataGridView:

  http://www.caddzone.com/ComponentModel.cs (http://www.caddzone.com/ComponentModel.cs)

Bryco,
To come back to the Overloading ..
Here's a couple of examples of Method Overloading you may find interesting.

 
        // class Autodesk.AutoCAD.Runtime.Converter
        // enum Autodesk.AutoCAD.Runtime.DistanceUnitFormat
        // struct Autodesk.AutoCAD.Geometry.Point3d
        //--------
        public static string PtToStr(Point2d pt)
        {
            return PtToStr(pt, DistanceUnitFormat.Current, -1);
        }
        //--------



Title: Re: Overloading types.
Post by: TonyT on May 27, 2007, 09:31:14 PM
 

        public static void Rotate(Entity ent, Point3d basePoint, double rotationAngle)
        {
            Rotate(ent.ObjectId, basePoint, rotationAngle);
        }

        public static void Rotate(ObjectId id, Point3d basePoint, double rotationAngleInRadians)
        {
            Matrix3d transform = Matrix3d.Rotation(
                rotationAngleInRadians, 
                new Vector3d(0, 0, 1),
                basePoint);
            using (Transaction tr =
                AcadApp.DocumentManager.MdiActiveDocument.Database.TransactionManager.StartTransaction())
            {
                ((Entity)tr.GetObject(id, OpenMode.ForWrite, true)).TransformBy(transform);
                tr.Commit();
            }
        }



Well, without assuming anything about the application, I would
have done that a bit differently.

First I would have designed the code to implicitly operate on
an array of ObjectId[] rather than just one (and overloaded
for the case where only one object is being operated on).

Second, I prefer to avoid using the active document when I
can get the database the objects are in from the Objects
themselves, allowing the code to used in any database.

Code: [Select]

     // for one
     public static void Rotate(ObjectId id, Point3d basePoint, double rotationAngleInRadians)
     {
         Rotate( new ObjectId[] { id }, basePoint, rotationAngleInRadians);
     }

     // for many
     public static void Rotate(ObjectId[] ids, Point3d basePoint, double rotationAngleInRadians)
     {
          Matrix3d transform = Matrix3d.Rotation(
                rotationAngleInRadians, 
                new Vector3d(0, 0, 1),
                basePoint);
          Database db = ids[0].Database;
          using(Transaction tr = db.TransactionManager.StartTransaction())
          {
              foreach( ObjectId id in ids )
              {
                   Entity ent = tr.GetObject(id, OpenMode.ForWrite) as Entity;
                   if( ent != null )
                        ent.TransformBy(transform);
              }
              tr.Commit();
          }
     }


Title: Re: Overloading types.
Post by: Bryco on May 28, 2007, 11:20:16 AM
I've got the feeling I'll be coming back to this thread quite a few times for pointers, it's a beauty.
So far I've found the distance function to be a perfect example of overloading. Now I can feed it 1 2dpoint,2 3dpoints etc etc.
It does seem like there's a lot of preliminary work to do to make coding in cad more efficient.
That's one of the reasons I tried to start with a full monty approach, get it to work then I wont be typing it wrong the next time.
using(Transaction tr = db.TransactionManager.StartTransaction())- this line is one I'll look at as a snippet.
One of the advantages I see in this is being able to post code without searching for 30 included functions.
The commands look they are going to take a little time. E.g. Matrix3d transform = Matrix3d.Rotation, this might take the odd side step to figure out. (The hook). In reality I need to finish reading my C+ book first, but it sure is nice to mix it up a bit.