TheSwamp

Code Red => .NET => Topic started by: Kerry on December 20, 2011, 08:44:35 PM

Title: AutoCAD - First Plug-in Training
Post by: Kerry on December 20, 2011, 08:44:35 PM

Just noticed this ..
not sure how long it's been available, and haven't had a good look at it yet.

http://usa.autodesk.com/adsk/servlet/index?id=18162650&siteID=123112

Regards
kdub
Title: Re: AutoCAD - First Plug-in Training
Post by: MP on December 20, 2011, 09:05:05 PM
Thanks Kerry, should prove useful for a lot of folks.
Title: Re: AutoCAD - First Plug-in Training
Post by: alanjt on December 20, 2011, 09:53:36 PM
Very nice. Thanks for sharing Kerry.
Title: Re: AutoCAD - First Plug-in Training
Post by: BlackBox on December 20, 2011, 10:27:23 PM
Yes, thanks for sharing this Kerry.

Cheers! :beer:
Title: Re: AutoCAD - First Plug-in Training
Post by: SGP2012 on December 20, 2011, 11:06:53 PM
You found it then. Its only been up for a week or so. I was going to announce it here when it was complete.

Its in Beta right now - the accompanying videos will be ready soon. Feel free to email comments to myfirstplugin@autodesk.com (or discuss it here, of course :-)). All feedback gratefully received - good or bad.

BTW For anyone interested in Revit or Inventor programming, this follows on from the My First Plug-in tutorials for those products. You can find links at www.autodesk.com/developrevit and www.autodesk.com/developinventor.
Title: Re: AutoCAD - First Plug-in Training
Post by: Jeff H on December 20, 2011, 11:16:21 PM
Good stuff!!
 
Thanks a ton Stephen!!!
 
Title: Re: AutoCAD - First Plug-in Training
Post by: MP on December 20, 2011, 11:17:11 PM
Thanks Stephen, quite awesome, and on a number of levels. (http://www.theswamp.org/screens/mp/thumbsup.gif)
Title: Re: AutoCAD - First Plug-in Training
Post by: BlackBox on December 20, 2011, 11:30:48 PM
Can't wait to dig through the .NET tutorial(s) Stephen; many thanks!
Title: Re: AutoCAD - First Plug-in Training
Post by: Kerry on December 21, 2011, 03:49:26 AM

Looks like the samples and tutorials favour VB.Net ...

Stephen,
when the bugs are ironed out can the samples be provided in C# as well as vb to save beginning C# customisers having to translate the code.

If necessary I (or  someone here) can convert the code for members here,  but I believe including the source with your downloads would be better.

Thanks for the iniative.

Regards
kdub.
Title: Re: AutoCAD - First Plug-in Training
Post by: SGP2012 on December 21, 2011, 08:49:29 PM
Sigh! You C# guys and your semi-colon addictions  8-).

C# versions of the projects will be added eventually (after I've received enough feedback to be confident I don't have to edit too much of the VB.NET code), but I may not create a C# version of the webpages. I continue to hold the opinion that VB.NET is more suited to someone learning programming for the first time by working through tutorials like this - the code is that much more readable for mere mortals.
Title: Re: AutoCAD - First Plug-in Training
Post by: Kerry on December 21, 2011, 09:02:25 PM
:)

Sorry, I forgot it was aimed at mortals.   :lmao:

I thought I noticed C# code in either the Inventor or Revit section. 

Title: Re: AutoCAD - First Plug-in Training
Post by: MP on December 21, 2011, 09:46:26 PM
Bring on the VB!! Generally speaking, VB is easier to translate to C# than vice versa.

There, I said it. :laugh:
Title: Re: AutoCAD - First Plug-in Training
Post by: BlackBox on December 22, 2011, 01:02:44 AM
I Jumped (am jumping?) straight from Visual LISP --> VB.NET  :evil:

I still want to learn C# syntax (for personal edification), however I went with VB.NET when I started learning the .NET API for the simple reason that it was easier to read, and was more closely 'written' with the familiar syntax of Visual LISP (ActiveX --> VBA --> VB.NET).

Title: Re: AutoCAD - First Plug-in Training
Post by: Jeff H on December 22, 2011, 11:07:22 AM
If you can code in VB or C#, you can learn the other in a weekend.
 
It is getting to know the framework that takes time.
Title: Re: AutoCAD - First Plug-in Training
Post by: Matt__W on December 22, 2011, 11:13:49 AM
Looks like the samples and tutorials favour VB.Net ...
FINALLY!!!  Right up my alley.  It gets tiring seeing all of the C-base code samples and trying to translate to VB.

Thanks!!
Title: Re: AutoCAD - First Plug-in Training
Post by: Matt__W on December 22, 2011, 11:15:53 AM
If you can code in VB or C#, you can learn the other in a weekend.
My mind automatically translated that to: "If you can dodge a wrench, you can dodge a ball."   :-D
Title: Re: AutoCAD - First Plug-in Training
Post by: Kerry on December 25, 2011, 10:17:15 PM
For anyone manually translating the first lesson :

The C# language is strongly typed.
ie
a Swan is a bird           : can't be converted to Ingredients
a Pidgeon is a bird        : can be converted to Ingredients


you can't do this
Code: [Select]
    Ingredients pieStuff = bird;
because a bird can't be implicitly converted into Ingredients

If you want a  pie you'll need to keep the compiler happy by being explicit about what you want.

Code: [Select]
   Ingredients pieStuff = (Ingredients )bird;
This will only work IF your bird can be converted into Ingredients.
no guarantees that it will taste great, but at least the compiler won't bitch about your language.

If the VB code is marked
Code: [Select]
Option Explicit Off Option Strict Off
the compiler will assume your requirements and this code will be accepted
Code: [Select]
    Dim attRef As AttributeReference = entity
If
Code: [Select]
Option Explicit On Option Strict On
you'll need to write the asignment like this, with an explicit conversion (which I prefer)
Code: [Select]
    Dim attRef As AttributeReference = CType(entity, AttributeReference)

A mind in neutral translation of
Code: [Select]
    Dim attRef As AttributeReference = entityis
Code: [Select]
    AttributeReference attRef = entity;
In C#,  the compiler requires that your code is explicit  ie written like this at the outset:-
Code: [Select]
    AttributeReference attRef = (AttributeReference)entity;

Note 1:
I took some artistic licence with the Swan pie example. :)
Note 2:
This difference in requirements is not necessarily a shortcoming in either language ... it's just the way it is :) 

Title: Re: AutoCAD - First Plug-in Training
Post by: Jeff H on December 26, 2011, 09:02:14 PM
My computer crashed when I was almost done so I got mad and typed this quickly and went from memory which is a very very bad thing. Someone might want to double check it.
First Example Converted
 
In VB you 'Import' the namespace and in C# you 'use' the namespace.
So
Code: [Select]
Imports Autodesk.AutoCAD.Runtime
Imports Autodesk.AutoCAD.DatabaseServices
Imports Autodesk.AutoCAD.Geometry
becomes
Code: [Select]
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
Here is the KeepStraightOverrule class
Code: [Select]
    Public Class KeepStraightOverrule
        Inherits TransformOverrule
        'We want to change how an AttributeReference responds to being
        ' transformed (moved, rotated, etc.), so we override its
        ' standard TransformBy function.
        Public Overrides Sub TransformBy(ByVal entity As Entity,
                                         ByVal transform As Matrix3d)
            'Call the normal TransformBy function for the attribute
            ' reference we're overruling.
            MyBase.TransformBy(entity, transform)
            'We know entity must be an AttributeReference because
            ' that is the only entity we registered the overrule for.
            Dim attRef As AttributeReference = entity
            'Set rotation of attribute reference to 0 (horizontal)
            attRef.Rotation = 0.0
        End Sub
    End Class

In VB when a class inherits from another the first line in the class is 'Inherits' then the the name of the class.
In C# you use ':'
In the TransformOverrule class it has a method TransformBy marked as
Code: [Select]
Public Overridable Sub TransformBy(ByVal entity As Entity, ByVal transform As Matrix3d)
or in C#
Code: [Select]
public virtual void TransformBy(Entity entity, Matrix3d transform)
Overridable = virtual
 
Quote
The virtual keyword is used to modify a method, property, indexer, or event declaration and allow for it to be overridden in a derived class. For example, this method can be overridden by any class that inherits it:
Quote
The Overidable keyword specifies that a property or procedure can be overridden by an identically named property or procedure in a derived class.
A derived class is one that inherits another class.
 
Also a Sub is method that 'does something' and does not return anything so in C# a 'void' method.
 
Some of you might wonder how is entity passed with ByVal(Meaning By Value) and in the body of the method the entity is changed and the original entity is changed. When a custom class is used in an argument it is always passed ByRef(By Reference) meaning its memory address not a copy of the value is passed and any changes made in the method will change the original object.
 
As Kerry mentioned if Option Strict is on then one of the things that will cause a compile error is
Implicit narrowing conversions
Since AttributeReference is derived from Entity and Entity does not contain all the methods as AttributeReference it is considered a narrowing conversion(A conversion with Data loss)
Another example would be
Code: [Select]
            Dim d As Double = 45.25
            Dim i As Integer = d
With option strict On it will throw a error with it off the compiler will do the conversion.
As Kerry mentioned you must explicitly do the conversion in C#
Code: [Select]
AttributeReference attRef = entity as AttributeReference;
MyBase(VB) and base(C#) call the 'base'(The class you are inherting from) class method.
Last couple things as my computer crashed and I am lazy and do not feel like retyping but in VB to make a null check you check if 'Is Nothing', and static = Shared.
Code: [Select]
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
namespace HorizontalAttributesCSharp
{
    public class Commands
    {
        private static KeepStraightOverrule myOverrule;
        [CommandMethod("KeepStraight")]
        public static void ImplementOverrule()
        {
            if (myOverrule == null)
            {
       
                myOverrule = new KeepStraightOverrule();
               
                Overrule.AddOverrule(RXClass.GetClass(typeof(AttributeReference)), myOverrule, false);
            }
           
            Overrule.Overruling = true;
        }
    }
    public class KeepStraightOverrule : TransformOverrule
 {
  public override void TransformBy(Entity entity, Matrix3d transform)
  {
            base.TransformBy(entity, transform);
   AttributeReference attRef = entity as AttributeReference; 
   attRef.Rotation = 0.0;
  }
 }
}

**********************Edit************************************
Also AddOverule first arguement is a System.Type to read more for VB GetType (http://msdn.microsoft.com/query/dev10.query?appId=Dev10IDEF1&l=EN-US&k=k(VB.GETTYPE);k(TargetFrameworkMoniker-%22.NETFRAMEWORK%2cVERSION%3dV4.0%22);k(DevLang-VB)&rd=true)
and for C# typeof (http://msdn.microsoft.com/query/dev10.query?appId=Dev10IDEF1&l=EN-US&k=k(TYPEOF_CSHARPKEYWORD);k(TYPEOF);k(TargetFrameworkMoniker-%22.NETFRAMEWORK%2cVERSION%3dV4.0%22);k(DevLang-CSHARP)&rd=true)
 
 
Title: Re: AutoCAD - First Plug-in Training
Post by: Kerry on December 26, 2011, 09:41:41 PM
< .. >
Someone might want to double check it.
< .. >
I have the same translation.
Either we're both correct or both miserably wrong

:)
Title: Re: AutoCAD - First Plug-in Training
Post by: Jeff H on December 26, 2011, 09:51:45 PM
< .. >
Someone might want to double check it.
< .. >
I have the same translation.
Either we're both correct or both miserably wrong

 :)

If Kerry has the same then looks like I got it correct or what little I cleaned up from here (http://www.developerfusion.com/tools/convert/vb-to-csharp/)

Kerry or Stephen do you a know way to make the Attribute horizontal on 'Insert' using a overrule, maybe with ObjectOverule.
I see the SetAttributes in  DrawableOverrule but I do not think that is what I need.

If you like Overrules and the example is really good reason why to use them then you need to check out Stephen's class
AutoCADŽ .NET: Practical Examples of Customizing AutoCAD Entity Behavior (http://au.autodesk.com/?nd=class&session_id=7560)
Title: Re: AutoCAD - First Plug-in Training
Post by: Kerry on December 26, 2011, 10:01:51 PM

Sorry, haven't played with Overrules at all Jeff.
Recall a couple of mentions on Kean's blog ...
Title: Re: AutoCAD - First Plug-in Training
Post by: Jeff H on December 26, 2011, 10:45:48 PM
I tried this and it makes the Attribute appear horizontal during the Jig of the Insert but crashes with a eOpenforWrite error after the rotation is selected
Code: [Select]
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.GraphicsInterface;
namespace HorizontalAttributesCSharp
{
    public class Commands
    {
        private static KeepStraightOverrule myOverrule;
        private static KeepStraightOverruleOnInsert myOverruleInsert;
        [CommandMethod("KeepStraight")]
        public static void ImplementOverrule()
        {
            if (myOverrule == null)
            {
                myOverrule = new KeepStraightOverrule();
                myOverruleInsert = new KeepStraightOverruleOnInsert();
                Overrule.AddOverrule(RXClass.GetClass(typeof(AttributeReference)), myOverrule, false);
                Overrule.AddOverrule(RXClass.GetClass(typeof(AttributeReference)), myOverruleInsert, false);
            }
            Overrule.Overruling = true;
        }
    }
    public class KeepStraightOverrule : TransformOverrule
    {
        public override void TransformBy(Entity entity, Matrix3d transform)
        {
            base.TransformBy(entity, transform);
            AttributeReference attRef = entity as AttributeReference;
            attRef.Rotation = 0.0;
        }
    }

    public class KeepStraightOverruleOnInsert : DrawableOverrule
    {
        public override int SetAttributes(Drawable drawable, DrawableTraits traits)
        {
            AttributeReference attRef = drawable as AttributeReference;
            attRef.Rotation = 0.0;
            return base.SetAttributes(drawable, traits);
        }

    }
}
When I tried placing it in a OpenClose transaction
I got this error
 
Title: Re: AutoCAD - First Plug-in Training
Post by: Jeff H on December 27, 2011, 12:03:24 AM
Using code from Stephen's last example
Code: [Select]
public class KeepStraightOverruleOnInsert : DrawableOverrule
    {
        public override bool WorldDraw(Drawable drawable, WorldDraw wd)
        {
            return false;
        }
       
        // Called for each viewport so entity can draw itself differently
        //  depending on the view.
        public override void ViewportDraw(Drawable drawable, ViewportDraw vd)
        {
            // Cast drawable to type AttributeReference (we know it's an
            //  AttributeReference because that's the only class we register our
            //  overrule for).
            AttributeReference attRef = drawable as AttributeReference;
            // First calculate the transformation matrix to rotate from the
            //  BlockReference's current orientation to the view.
            Point3d org = attRef.Position;
            Vector3d zVec = vd.Viewport.ViewDirection;
            Vector3d yVec = vd.Viewport.CameraUpVector;
            Vector3d xVec = yVec.CrossProduct(zVec).GetNormal();
            Matrix3d viewmat = Matrix3d.AlignCoordinateSystem(org, Vector3d.XAxis, Vector3d.YAxis, Vector3d.ZAxis, org, xVec, yVec, zVec);
            // Then calculate the additional rotation required to compensate for
            //  the AttributeReference having a rotation.
            Matrix3d rotMat = Matrix3d.Rotation(-attRef.Rotation, attRef.Normal, org);
            // Apply the two transformations
            vd.Geometry.PushModelTransform(viewmat);
            vd.Geometry.PushModelTransform(rotMat);
            //Draw the 'per viewport geometry
            base.ViewportDraw(drawable, vd);
            // Remove the transformation - we don't want any other objects
            //  to draw themselves in the view plane.
            vd.Geometry.PopModelTransform();
            vd.Geometry.PopModelTransform();
        }
 
    }
 
This works using a Insert, but is not persisent and need to somehow make it persistent.
And the other problem I have noticed is that it does not keep AttributeReference in the same location when rotated.
For a pic example the block is a dynamic block and the top BlockReference was rotated with a RotationParameter and the overriden TransformBy method places it correctly, but the bottom reference is done during the Insert which the overriden ViewportDraw handles.
 
Title: Re: AutoCAD - First Plug-in Training
Post by: Jeff H on December 27, 2011, 02:43:56 AM
Overriding the Open Method seems to work but does not show during the Insertion Jig.
Also it only worked when applied to BlockReference's then grabbing the AttributeReferences
 
Code: [Select]
Overrule.AddOverrule(RXClass.GetClass(typeof(BlockReference)), myOverruleInsert, false);

Code: [Select]

 public class KeepStraightOverruleOnInsert : ObjectOverrule
    {
        public override void Open(DBObject dbObject, OpenMode mode)
        {
            if (mode == OpenMode.ForWrite)
            {
                BlockReference blkRef = dbObject as BlockReference;
           
                if (blkRef.AttributeCollection.Count != 0)
                {
                    using (Transaction trx = dbObject.Database.TransactionManager.StartOpenCloseTransaction())
                    {
                        foreach (ObjectId attRefId in blkRef.AttributeCollection)
                        {
                            AttributeReference attRef = trx.GetObject(attRefId, OpenMode.ForWrite) as AttributeReference;
                            attRef.Rotation = 0.0;
                        }
                        trx.Commit();
                    }
                }
            }
            base.Open(dbObject, mode);
        }

    }
 
Title: Re: AutoCAD - First Plug-in Training
Post by: Arizona on December 28, 2011, 11:12:21 AM
Sigh! You C# guys and your semi-colon addictions  8-).

C# versions of the projects will be added eventually (after I've received enough feedback to be confident I don't have to edit too much of the VB.NET code)...
Yes please!
And thank you for the great resource!
Title: Re: AutoCAD - First Plug-in Training
Post by: David Hall on December 28, 2011, 01:09:32 PM
Sigh! You C# guys and your semi-colon addictions  8-).

C# versions of the projects will be added eventually (after I've received enough feedback to be confident.
Add me to that!!
Title: Re: AutoCAD - First Plug-in Training
Post by: gile on December 28, 2011, 02:13:52 PM
Quote
I continue to hold the opinion that VB.NET is more suited to someone learning programming for the first time by working through tutorials like this - the code is that much more readable for mere mortals.

Why !?...
Aren't non English 'mere mortals' ?
Is it more difficult to learn the meaning of a semi-colon than the meaning of "End" ?
Does really implicit casts make easier coding in a strongly typed environment ?
IMO, in the .NET learning curve, the langauge syntax is 'peanuts' comparing to the programmation environment (Framework and API libraries, OOP, and so on...)

Anyway,
Quote
If necessary I (or  someone here) can convert the code for members here
I can give a hand this way too (these days I work on the migration of 20 000 VBA code lines to C#, hope I won't become 'Dim' addict).

I can try a F# translation too. F# isn't so 'unreadable' for those comming from LISP (neither 'Dim' nor semi-colon)...
Title: Re: AutoCAD - First Plug-in Training
Post by: Kerry on December 29, 2011, 03:57:38 AM
< .. >
I can give a hand this way too (these days I work on the migration of 20 000 VBA code lines to C#, hope I won't become 'Dim' addict).

I can try a F# translation too. F# isn't so 'unreadable' for those comming from LISP (neither 'Dim' nor semi-colon)...

I'll be jumped upon if I laugh about the 'Dim' addict quote ,,, so I'll just have a quiet smile to myself. :)

I think F# is a GREAT idea. We probably won't get many new dotNet AutoCAD customisers usinf F#, but it may help them to become familiar with reading it ... thus helping with understanding some of the samples floatong around.
Title: Re: AutoCAD - First Plug-in Training
Post by: Jeff H on December 29, 2011, 05:03:32 AM
hope I won't become 'Dim' addict
Be careful what you ask for
I'll be jumped upon if I laugh about the 'Dim' addict quote ,,, so I'll just have a quiet smile to myself.
Get ready for that smile to change to a cringe.
 
In VB you can always  turn Option Strict & and Explicit OFF and get rid of the Dims
so you can end up with this
Code: [Select]
attRef = entity
&
Code: [Select]
  Private Shared myOverrule

This runs but how much fun do you think it would be to deal with and find bugs with code on a much larger scale but written like this
Code: [Select]
Module Module1
    Sub Main()
        i = 10
        s = "word"
        d = 5.567
        ''answer = i * s ''-----This will compile but CRASH at runtime.
        answer = i * d   ''-----This will compile and work just fine.
        Console.WriteLine(answer)
        Console.ReadKey()
    End Sub
End Module

 
 
The code below compiles just fine
Code: [Select]
Imports Autodesk.AutoCAD.Runtime
Imports Autodesk.AutoCAD.DatabaseServices
Imports Autodesk.AutoCAD.Geometry
Namespace HorizontalAttributes
    Public Class Commands

        Private Shared myOverrule
        <CommandMethod("KeepStraight")>
        Public Shared Sub ImplementOverrule()
            If myOverrule Is Nothing Then
                myOverrule = New KeepStraightOverrule
                Overrule.AddOverrule(
                  RXClass.GetClass(GetType(AttributeReference)),
                  myOverrule, False)
            End If
            Overrule.Overruling = True
        End Sub
    End Class

    Public Class KeepStraightOverrule
        Inherits TransformOverrule
        Public Overrides Sub TransformBy(ByVal entity As Entity,
                                         ByVal transform As Matrix3d)
            MyBase.TransformBy(entity, transform)
            attRef = entity
            attRef.Rotation = 0.0
        End Sub
    End Class
End Namespace
Title: Re: AutoCAD - First Plug-in Training
Post by: Arizona on December 29, 2011, 05:55:24 AM
Get ready for that smile to change to a cringe.
 
In VB you can always  turn Option Strict & and Explicit OFF and get rid of the Dims
so you can end up with this
Code: [Select]
attRef = entity

I even cringed at that one :-)
Title: Re: AutoCAD - First Plug-in Training
Post by: gile on December 29, 2011, 09:22:39 AM
<...>
I think F# is a GREAT idea. We probably won't get many new dotNet AutoCAD customisers usinf F#, but it may help them to become familiar with reading it ... thus helping with understanding some of the samples floatong around.

So, I give a try.
This is certainly not the ultimate F# translation because I'm quite newby with this language, but it was a funny exercise.
Any advice from some F# expert as kaefer (are there others over there ?) is welcome.

I tried to keep the comments in the code when there's no major change so that it should be easier to compare.

KeepAttributesHorizontal - Lesson 1
The major change here is the using of an 'object expression (http://msdn.microsoft.com/en-us/library/dd233237%28v=vs.110%29.aspx)' instead of implementing a new type. I thaught interesting to show this F# ability.
As the overrule instance is created, to check if the overrule is already registered we use a 'global' mutable (http://msdn.microsoft.com/en-us/library/dd233185.aspx) boolean 'variable' to avoid registering it twice (in F# binding values are immutables by default).

Code: [Select]
module HorizontalAttributes

open Autodesk.AutoCAD.DatabaseServices
open Autodesk.AutoCAD.Geometry
open Autodesk.AutoCAD.Runtime

// Mutable value that indicates whether the overrule is registered
let mutable isRegistered = false

// No need to create a new type derived from TransformOverrule,
// just create an 'object expression' based on the TransformOverrule type
// and override the TransformBy method.
let keepStraightOverrule = { new TransformOverrule() with
    override this.TransformBy(entity, transform) =

        // Call the normal TransformBy function for the attribute
        // reference we're overruling.
        base.TransformBy(entity, transform)
        // We know entity must be an AttributeReference because
        // that is the only entity we registered the overrule for.
        let attRef = entity :?> AttributeReference
        // Set rotation of attribute reference to 0 (horizontal)
        attRef.Rotation <- 0.}

[<CommandMethod("KeepStraight")>]
let implementOverrule() =

    // We only want to register our overrule instance once,
    // so we check if it is not already registered
    // (i.e. this may be the 2nd time we've run the command)
    if not isRegistered then
        // Register the overrule
        Overrule.AddOverrule(
            RXClass.GetClass(typeof<AttributeReference>),
            keepStraightOverrule, false)
        isRegistered <- true
    // Make sure overruling is turned on so our overrule works
    Overrule.Overruling <- true


ObjectOrientedSample
'this' is not a F# keyword and could have been replaced by any other word (i.e. x.SetLocation ...)
Notice that with F# the assignation operator for 'variables' (or properties) is : <-

Code: [Select]
module ObjectOrientedSampleFs

open Autodesk.AutoCAD.Runtime

type Point() =
    let mutable xCoord = 0
    let mutable yCoord = 0

    member this.SetLocation x y =
        xCoord <- x
        yCoord <- y

    member this.X
        with get() = xCoord
        and set(value) = xCoord <- value

    member this.Y
        with get() = yCoord
        and set(value) = yCoord <- value

type NewPoint() =
    inherit Point()

    let mutable zCoord = 0

    member this.Z
        with get() = zCoord
        and set(value) = if value >= 0 then zCoord <- value

[<CommandMethod("RunTest")>]
let runTest() =
    let pt1 = new Point()
    // USe Step Into on the next line to step into SetLocation
    pt1.SetLocation 10 10
    let xVal1 = pt1.X
    pt1.X <- 9
    let xVal2 = pt1.X

    let pt2 = new NewPoint()
    // SetLocation, X and Y are inherited from Point class
    pt2.SetLocation 20 20
    let xVal3 = pt2.X
    pt2.X <- 9
    // Z is new to the NewPoint class
    pt2.Z <- 12
    let pt3 = pt2 :> Point
    //  pt3 is variable of type Point, but holds an object of type NewPoint
    ()


KeepAttributesHorizontal - Lesson 5
Using of a comprehension (http://msdn.microsoft.com/en-us/library/dd233214.aspx) (sequence expression) to convert the ObjectIdCollection into an ObjectId array (this avoid the ugly array resizing requiered with the AttributeCollection.CopyTo() method).

Code: [Select]
module HorizontalAttributes

open Autodesk.AutoCAD.ApplicationServices
open Autodesk.AutoCAD.DatabaseServices
open Autodesk.AutoCAD.EditorInput
open Autodesk.AutoCAD.Geometry
open Autodesk.AutoCAD.Runtime

// Mutable value that indicates whether the overrule is registered
let mutable isRegistered = false

// No need to create a new type derived from TransformOverrule,
// just create an 'object expression' based on the TransformOverrule type
// and override the TransformBy method.
let keepStraightOverrule = { new TransformOverrule() with
    override this.TransformBy(entity, transform) =

        // Call the normal TransformBy function for the attribute
        // reference we're overruling.
        base.TransformBy(entity, transform)
        // We know entity must be an AttributeReference because
        // that is the only entity we registered the overrule for.
        let attRef = entity :?> AttributeReference
        // Set rotation of attribute reference to 0 (horizontal)
        attRef.Rotation <- 0.}

[<CommandMethod("KeepStraight")>]
let implementOverrule() =

    let doc = Application.DocumentManager.MdiActiveDocument
    let ed = doc.Editor

    // Select a block reference

    let opts = new PromptEntityOptions("\nSelect a block reference: ")
    opts.SetRejectMessage("\nMust be block reference...")
    opts.AddAllowedClass(typeof<BlockReference>, true)

    let res = ed.GetEntity(opts)

    if res.Status = PromptStatus.OK then
        let db = doc.Database
        use trans = db.TransactionManager.StartTransaction()

        // Open the BlockReference for read.
        // We know its a BlockReference because we set a filter in
        // our PromptEntityOptions
        let blkRef = trans.GetObject(res.ObjectId, OpenMode.ForRead) :?> BlockReference
        let attRefCol = blkRef.AttributeCollection

        // Using a 'comprehension' to convert the ObjectIdCollection into an ObjectId array
        let objIds = [| for id in attRefCol -> id |]

        // We only want to register our overrule instance once,
        // so we check if it is not already registered
        // (i.e. this may be the 2nd time we've run the command)
        if not isRegistered then
            Overrule.AddOverrule(
                RXClass.GetClass(typeof<AttributeReference>),
                keepStraightOverrule, false)
            isRegistered <- true

        // Specify which Attributes will be overruled
        keepStraightOverrule.SetIdFilter(objIds)

        // Make sure overruling is turned on so our overrule works
        Overrule.Overruling <- true


KeepAttributesHorizontal - Lesson 6
We can see here the use of the pipeline operator (|> (http://msdn.microsoft.com/en-us/library/ee340273.aspx)) and the Seq module (http://msdn.microsoft.com/en-us/library/ee353635.aspx) specific functions to iterate through the attributes (the same thing can be done with C# or VB using Linq but it is the 'natural' way with F#). The F# sequence (http://msdn.microsoft.com/en-us/library/dd233209.aspx) type (seq<'T>) is similar to IEnumerable<T> (IEnumerable (Of T) in VB)
Notice the need to 'ignore (http://msdn.microsoft.com/en-us/library/ee340357.aspx)' the value returned by SymbolTable.Add() method.
Notice that functions have to be defined in the code prior they're called.

Code: [Select]
module HorizontalAttributes

open Autodesk.AutoCAD.ApplicationServices
open Autodesk.AutoCAD.DatabaseServices
open Autodesk.AutoCAD.EditorInput
open Autodesk.AutoCAD.Geometry
open Autodesk.AutoCAD.Runtime

// Mutable value that indicates whether the overrule is registered
let mutable isRegistered = false

// Registered Application Id for Xdata
let regAppName = "ADSK_ATTRIBUTE_ZERO_OVERRULE"

// No need to create a new type derived from TransformOverrule,
// just create an 'object expression' based on the TransformOverrule type
// and override the TransformBy method.
let keepStraightOverrule = { new TransformOverrule() with
    override this.TransformBy(entity, transform) =

        // Call the normal TransformBy function for the attribute
        // reference we're overruling.
        base.TransformBy(entity, transform)
        // We know entity must be an AttributeReference because
        // that is the only entity we registered the overrule for.
        let attRef = entity :?> AttributeReference
        // Set rotation of attribute reference to 0 (horizontal)
        attRef.Rotation <- 0.}


let addRegAppId (db:Database) =
    use trans = db.TransactionManager.StartTransaction()

    // First create our RegAppId (if it doesn't already exist)
    let appTbl = trans.GetObject(db.RegAppTableId, OpenMode.ForRead) :?> RegAppTable

    if not (appTbl.Has(regAppName)) then
        let appTblRec = new RegAppTableRecord()
        appTbl.UpgradeOpen()
        appTblRec.Name <- regAppName
        appTbl.Add(appTblRec) |> ignore
        trans.AddNewlyCreatedDBObject(appTblRec, true)
        trans.Commit()


[<CommandMethod("ActivateOverrule")>]
let activateOverrule() =
   
    // We only want to register our overrule instance once,
    // so we check if it is not already registered
    // (i.e. this may be the 2nd time we've run the command)
    if not isRegistered then
        // Register the overrule
        Overrule.AddOverrule(
            RXClass.GetClass(typeof<AttributeReference>),
            keepStraightOverrule, false)
        isRegistered <- true

    // Specify which Attributes will be overruled
    keepStraightOverrule.SetXDataFilter(regAppName)
    // Make sure overruling is turned on so our overrule works
    Overrule.Overruling <- true


[<CommandMethod("KeepStraight")>]
let implementOverrule() =

    let doc = Application.DocumentManager.MdiActiveDocument
    let ed = doc.Editor

    // Select a block reference

    let opts = new PromptEntityOptions("\nSelect a block reference: ")
    opts.SetRejectMessage("\nMust be block reference...")
    opts.AddAllowedClass(typeof<BlockReference>, true)

    let res = ed.GetEntity(opts)

    if res.Status = PromptStatus.OK then
        let db = doc.Database

        addRegAppId(db)

        use trans = db.TransactionManager.StartTransaction()

        // Open the BlockReference for read.
        // We know its a BlockReference because we set a filter in
        // our PromptEntityOptions
        let blkRef = trans.GetObject(res.ObjectId, OpenMode.ForRead) :?> BlockReference
        let attRefCol = blkRef.AttributeCollection
       
        // Iterate through  ObjectIds of all AttributeReferences
        // attached to the BlockReference, opening each
        // AttributeReference and adding xdata to it.
        blkRef.AttributeCollection
        |> Seq.cast<ObjectId>
        |> Seq.map(fun objId -> trans.GetObject(objId, OpenMode.ForWrite))
        |> Seq.iter(fun attRef ->
            // Create new xdata containing the overrule filter name
            use resBuf = new ResultBuffer(new TypedValue(1001, regAppName), new TypedValue(1000, "Dummy text"))
            // Add the xdata
            attRef.XData <- resBuf)

        trans.Commit()

        // Create and register our overrule and turn overruling on.
        activateOverrule()

[<CommandMethod("DontKeepStraight")>]
let removeXdata() =

    let doc = Application.DocumentManager.MdiActiveDocument
    let ed = doc.Editor

    // Select a block reference

    let opts = new PromptEntityOptions("\nSelect a block reference: ")
    opts.SetRejectMessage("\nMust be block reference...")
    opts.AddAllowedClass(typeof<BlockReference>, true)

    let res = ed.GetEntity(opts)

    if res.Status = PromptStatus.OK then
        let db = doc.Database

        addRegAppId(db)

        use trans = db.TransactionManager.StartTransaction()

        // Open the BlockReference for read.
        // We know its a BlockReference because we set a filter in
        // our PromptEntityOptions
        let blkRef = trans.GetObject(res.ObjectId, OpenMode.ForRead) :?> BlockReference
        let attRefCol = blkRef.AttributeCollection
       
        // Iterate through  ObjectIds of all AttributeReferences
        // attached to the BlockReference, opening each
        // AttributeReference and adding xdata to it.
        blkRef.AttributeCollection
        |> Seq.cast<ObjectId>
        |> Seq.map(fun objId -> trans.GetObject(objId, OpenMode.ForWrite))
        |> Seq.iter(fun attRef ->
            // Create new xdata containing the overrule filter name
            use resBuf = new ResultBuffer(new TypedValue(1001, regAppName))
            // Add the xdata.
            // Because we leave this blank except for the regappid,
            // it erases any Xdata we previously added.
            attRef.XData <- resBuf)

        trans.Commit()


BonusPlugin
With some refactorisation
Corrected code so that it works whatever the block insertion plane (see Reply #40)
Corrected code so that it works whatever the attribute text justification (see Reply #62)

Code: [Select]
module HorizontalAttributes

open Autodesk.AutoCAD.ApplicationServices
open Autodesk.AutoCAD.DatabaseServices
open Autodesk.AutoCAD.EditorInput
open Autodesk.AutoCAD.Geometry
open Autodesk.AutoCAD.GraphicsInterface
open Autodesk.AutoCAD.Runtime

// Mutable value that indicates whether the overrule is registered
let mutable isRegistered = false

// Registered Application Id for Xdata
let regAppName = "ADSK_ATTRIBUTE_ZERO_OVERRULE"

// No need to create a new type derived from DrawableOverrule,
// just create an 'object expression' based on the DrawableOverrule type
// and override the WorldDraw, ViewportDraw and SetAttributes methods.
let billboardAttributesOverrule = { new DrawableOverrule() with

    // Returning False from WorldDraw tells AutoCAD this entity has
    // viewport dependent graphics.
    override this.WorldDraw(drawable, wd) = false;

    // Called for each viewport so entity can draw itself differently
    // viewport dependent graphics.
    override this.ViewportDraw(drawable, vd) =
        // Cast drawable to type AttributeReference (we know it's an
        // AttributeReference because that's the only class we register our
        // overrule for).
        let attRef = drawable :?> AttributeReference

        // First calculate the transformation matrix to rotate from the
        // BlockReference's current orientation to the view. 
        let org =
            match attRef.Justify with
            | AttachmentPoint.BaseLeft
            | AttachmentPoint.BaseAlign
            | AttachmentPoint.BaseFit -> attRef.Position
            | _ -> attRef.AlignmentPoint
        let viewMat =
            Matrix3d.PlaneToWorld(new Plane(org, vd.Viewport.ViewDirection)) *
            Matrix3d.WorldToPlane(new Plane(org, attRef.Normal)) *
            Matrix3d.Rotation(-attRef.Rotation, attRef.Normal, org)

        // Apply the transformation
        vd.Geometry.PushModelTransform(viewMat) |> ignore
        // Draw the 'per viewport geometry
        base.ViewportDraw(drawable, vd)
        // Remove the transformation - we don't want any other objects
        // to draw themselves in the view plane.
        vd.Geometry.PopModelTransform() |> ignore

    // This function tells AutoCAD to dynamically update the
    // AttributeReference during view transitions (e.g. 3DORBIT).
    // Comment it out to improve graphic update performance.
    override this.SetAttributes(drawable, traits) =
        base.SetAttributes(drawable, traits) ||| 2048
    }

let addXdata id resBuf (db:Database) =
    use trans = db.TransactionManager.StartTransaction()

    // First create our RegAppId (if it doesn't already exist)
    let appTbl = trans.GetObject(db.RegAppTableId, OpenMode.ForRead) :?> RegAppTable

    if not (appTbl.Has(regAppName)) then
        let appTblRec = new RegAppTableRecord()
        appTbl.UpgradeOpen()
        appTblRec.Name <- regAppName
        appTbl.Add(appTblRec) |> ignore
        trans.AddNewlyCreatedDBObject(appTblRec, true)

    // Open the BlockReference for read.
    // Iterate through  ObjectIds of all AttributeReferences
    // attached to the BlockReference, opening each
    // AttributeReference and adding xdata to it.
    (trans.GetObject(id, OpenMode.ForRead) :?> BlockReference).AttributeCollection
    |> Seq.cast<ObjectId>
    |> Seq.map(fun objId -> trans.GetObject(objId, OpenMode.ForWrite))
    |> Seq.iter(fun attRef -> attRef.XData <- resBuf)

    trans.Commit()

let getBlkRef (ed:Editor) =
    let opts = new PromptEntityOptions("\nSelect a block reference: ")
    opts.SetRejectMessage("\nMust be block reference...")
    opts.AddAllowedClass(typeof<BlockReference>, true)
    ed.GetEntity(opts)


[<CommandMethod("ActivateBillboardOverrule")>]
let activateOverrule() =
   
    // We only want to register our overrule instance once,
    // so we check if it is not already registered
    // (i.e. this may be the 2nd time we've run the command)
    if not isRegistered then
        Overrule.AddOverrule(
            RXClass.GetClass(typeof<AttributeReference>),
            billboardAttributesOverrule, false)
        isRegistered <- true

    // Specify which Attributes will be overruled
    billboardAttributesOverrule.SetXDataFilter(regAppName)
    // Make sure overruling is turned on so our overrule works
    Overrule.Overruling <- true


[<CommandMethod("BillboardAttributes")>]
let implementOverrule() =

    let doc = Application.DocumentManager.MdiActiveDocument

    // Select a block reference
    let res = getBlkRef doc.Editor

    if res.Status = PromptStatus.OK then
        // Create and register our overrule and turn overruling on.
        activateOverrule()

        // Add xdata to all AttributeReferences of the selected block reference
        use resBuf = new ResultBuffer(
                        new TypedValue(1001, regAppName),
                        new TypedValue(1000, "Dummy text"))
        addXdata res.ObjectId resBuf doc.Database

[<CommandMethod("DontBillboardAttributes")>]
let removeXdata() =

    let doc = Application.DocumentManager.MdiActiveDocument

    // Select a block reference
    let res = getBlkRef doc.Editor

    if res.Status = PromptStatus.OK then
        // Erases xdata to all AttributeReferences of the selected block reference.
        use resBuf = new ResultBuffer(new TypedValue(1001, regAppName))
        addXdata res.ObjectId resBuf doc.Database
Title: Re: AutoCAD - First Plug-in Training
Post by: gile on December 31, 2011, 04:36:45 AM
For those who're interested by F#, can have some fun with this video: An Introduction to Microsoft F# (http://channel9.msdn.com/Blogs/pdc2008/TL11)
Title: Re: AutoCAD - First Plug-in Training
Post by: MP on December 31, 2011, 05:48:12 AM
Oh sh!t, why why why did I start watching that? (http://www.theswamp.org/screens/mp/facepalm.gif)
Title: Re: AutoCAD - First Plug-in Training
Post by: MP on December 31, 2011, 06:31:16 AM
That was a well spent 1:18 (despite the fact I should be sleeping). Thanks for posting this Giles (she doesn't know it yet but my wife is going to hate you).
Title: Re: AutoCAD - First Plug-in Training
Post by: SGP2012 on January 03, 2012, 04:02:02 PM
Oh wow! This turned into a long thread. Its my first day back at work today, so my brian is a little mushy, but as far as I can tell the two main feedback items were:

1. My bonus app (lesson #8) is causing attributes to display slightly in the wrong place - so I need to go back and double check my transformation matrices.
2. You don't like my use of implicit casting in the VB.NET code.

I'm ambivalent about #2. I can understand why a C++/C# purist doesn't like it (and that it causes problems when using automatic code translators). But this is a feature of VB (and other languages) for a reason. For starters, its one less thing a beginner has to worry about while they learn the basics - and this tutorial is aimed squarely at beginner programmers (not programmers who are AutoCAD API beginners).

Any other areas I need to look at improving?

And (at the risk of going off on a tangent) any more comments on #2? I'd be interested to know if VB.NET developers dislike implicit casting as much as the C# developers  :evil:.



Title: Re: AutoCAD - First Plug-in Training
Post by: gile on January 03, 2012, 04:57:12 PM
Just some precisions

I didn't wan't to offend anyone with my questions (appologies if I did due to my poor English).

I sincerly ask myself why can we so often hear that VB is easier than C#.
As far as I know, the to main differences are syntax and strictness. in my opinion the syntax is not really an issue (only personnal convinience). Looking at strictness, my question is: is it more or less difficult to learn  stricter programmation language than one which can be more or less strict (and wrtitten different ways) according to the setting of some options (infer, strict, explicit)...
May be some VBers or bi-linguist can reply.

To be clear, I'm not a "programmer who is AutoCAD API beginner". I'm an AutoCAD user and self-educated AutoCAD 'programmer', using LISP since 10 years, and I jumped into the .NET adventure 4 years ago. At this time I dicovered all at the same time: .NET, OOP, C# (and strong static typing).
Title: Re: AutoCAD - First Plug-in Training
Post by: TheMaster on January 04, 2012, 02:04:25 AM

I tried this and it makes the Attribute appear horizontal during the Jig of the Insert but crashes with a eOpenforWrite error after the rotation is selected......


I think that's because this simple example wasn't done correctly to start with (Sorry 'bout that Mr. Preston).

The TransformBy() method takes a Matrix3d and applies the transformation defined by the matrix.

If you want to alter the transformation in some way, you don't supermessage the base type and pass it the original Matrix3d parameter as supplied (which applies the tranformation), and then perform a second transformation, by setting the rotation property. Sorry again, but that is the classic definition of a kludge.

What the author should have done was modify the Matrix3d that was passed into the override of TransformBy() to eliminate any rotation, and then supermessage the base type and pass it the modified matrix.

As far as the errors you may be seeing, the object(s) involved are already open for write when the method is called, but may not always be open in a transaction in certain circumstances.
Title: Re: AutoCAD - First Plug-in Training
Post by: MUQTAR on January 04, 2012, 07:30:09 AM
thanks kerry
Title: Re: AutoCAD - First Plug-in Training
Post by: Jeff H on January 04, 2012, 09:37:42 AM

I tried this and it makes the Attribute appear horizontal during the Jig of the Insert but crashes with a eOpenforWrite error after the rotation is selected......


I think that's because this simple example wasn't done correctly to start with (Sorry 'bout that Mr. Preston).

The TransformBy() method takes a Matrix3d and applies the transformation defined by the matrix.

If you want to alter the transformation in some way, you don't supermessage the base type and pass it the original Matrix3d parameter as supplied (which applies the tranformation), and then perform a second transformation, by setting the rotation property. Sorry again, but that is the classic definition of a kludge.

What the author should have done was modify the Matrix3d that was passed into the override of TransformBy() to eliminate any rotation, and then supermessage the base type and pass it the modified matrix.

As far as the errors you may be seeing, the object(s) involved are already open for write when the method is called, but may not always be open in a transaction in certain circumstances.

Thanks,
and appreciate the concise explanation
Title: Re: AutoCAD - First Plug-in Training
Post by: SGP2012 on January 04, 2012, 03:42:07 PM
Hi TheMaster,

I agree that one shouldn't call a method on an entity from an overrule for that method called for that same entity. For example, if I were to call Attribute.TransformBy from my overrule of Attribute.TransformBy.

However, your description implies that the Attribute.Rotation property 'set' function calls TransformBy in its implementation. It doesn't. If it did, then my code would have crashed the first time it was run as the overruled function was recursively called. That being the case, I really don't consider setting the Rotation property in the overrule as being any different from (say) setting the Color property.

I understand your point about using the transformation matrix, but I remain satisfied that the solution I use efficiently achieves exactly the behavior I wanted to implement in a single line of code (not a kludge). It also serves the additional purpose of avoiding having to present a larger and more complicated chunk of matrix manipulation code in a beginner tutorial. I'm happy to agree to differ on this - working through the transformation matrix is also a good solution if that is your preference.

Also, a note to everyone - Thank you for contributing your energy and expertise in providing feedback for this tutorial. It - and future projects - will be all the better as a result.

Cheers,

Stephen
Title: Re: AutoCAD - First Plug-in Training
Post by: gile on January 04, 2012, 04:03:19 PM
Maybe I've found something to fix the issue with the transformation matrix while block references are inserted in planes non parallel to WCS XY

Heres the C# code for the ViewportDraw method

Code: [Select]
        public override void ViewportDraw(Drawable drawable, ViewportDraw vd)
        {
            // Cast drawable to type AttributeReference (we know it's an
            // AttributeReference because that's the only class we register our
            // overrule for).
            AttributeReference attRef = (AttributeReference)drawable;

            // First calculate the transformation matrix to rotate from the
            // BlockReference's current orientation to the view.
            Point3d org = attRef.Position;
            Vector3d zVec = vd.Viewport.ViewDirection;
            Vector3d yVec = vd.Viewport.CameraUpVector;
            Vector3d xVec = yVec.CrossProduct(zVec).GetNormal();
            Matrix3d viewMat =
                Matrix3d.AlignCoordinateSystem(
                Point3d.Origin, Vector3d.XAxis, Vector3d.YAxis, Vector3d.ZAxis,
                org, xVec, yVec, zVec) *
                Matrix3d.WorldToPlane(new Plane(org, attRef.Normal)) *
                Matrix3d.Rotation(-attRef.Rotation, attRef.Normal, org);

            // Apply the transformation
            vd.Geometry.PushModelTransform(viewMat);
            // Draw the 'per viewport geometry
            base.ViewportDraw(drawable, vd);
            // Remove the transformation - we don't want any other objects
            // to draw themselves in the view plane.
            vd.Geometry.PopModelTransform();
        }

And the VB code:

Code: [Select]
        Public Overrides Sub ViewportDraw(ByVal drawable As Drawable,
                                          ByVal vd As ViewportDraw)
            ' Cast drawable to type AttributeReference (we know it's an
            '  AttributeReference because that's the only class we register our
            '  overrule for).
            Dim attRef As AttributeReference = drawable

            ' First calculate the transformation matrix to rotate from the
            '  BlockReference's current orientation to the view.
            Dim org As Point3d = attRef.Position
            Dim zVec As Vector3d = vd.Viewport.ViewDirection
            Dim yVec As Vector3d = vd.Viewport.CameraUpVector
            Dim xVec As Vector3d = yVec.CrossProduct(zVec).GetNormal
            Dim viewmat As Matrix3d =
                Matrix3d.AlignCoordinateSystem(
                    Point3d.Origin, Vector3d.XAxis, Vector3d.YAxis, Vector3d.ZAxis,
                    org, xVec, yVec, zVec) *
                Matrix3d.WorldToPlane(New Plane(org, attRef.Normal)) *
                Matrix3d.Rotation(-attRef.Rotation, attRef.Normal, org)

            ' Apply the transformation
            vd.Geometry.PushModelTransform(viewmat)
            'Draw the 'per viewport geometry
            MyBase.ViewportDraw(drawable, vd)
            ' Remove the transformation - we don't want any other objects
            '  to draw themselves in the view plane.
            vd.Geometry.PopModelTransform()

        End Sub
Title: Re: AutoCAD - First Plug-in Training
Post by: TheMaster on January 05, 2012, 03:10:30 AM
Hi TheMaster,

I agree that one shouldn't call a method on an entity from an overrule for that method called for that same entity. For example, if I were to call Attribute.TransformBy from my overrule of Attribute.TransformBy. 


Yes, you can't call TransformBy() from the Overrule's TransformBy (at least not without disabling overruling temporarily to prevent reentance).

Quote

However, your description implies that the Attribute.Rotation property 'set' function calls TransformBy in its implementation. It doesn't.


No, I wasn't trying to imply that at all. And of course, it doesn't do that because if it did, that code would fail with a stack overflow. Actually, it's quite the opposite. The implementation of AcDbText::transformBy() is what sets the rotation member along with other members to perform the complete transformation.

Quote

If it did, then my code would have crashed the first time it was run as the overruled function was recursively called. That being the case, I really don't consider setting the Rotation property in the overrule as being any different from (say) setting the Color property.

I understand your point about using the transformation matrix, but I remain satisfied that the solution I use efficiently achieves exactly the behavior I wanted to implement in a single line of code (not a kludge). It also serves the additional purpose of avoiding having to present a larger and more complicated chunk of matrix manipulation code in a beginner tutorial.


Well, if a somewhat advanced topic like overrules are suitable as examples in an entry-level tutorial that targets those with no previous programming experience, then I think it should be done correctly and in the most efficient way (by manipulating the matrix). Perhaps its because I've always held the view that training materials both formal and informal, should always emphasize correctness and should not promote kludges as ways to solve problems, because kludges almost always come back to bite you in the arse.

On that subject, your tutorial purports to assume no prior programming experience. Perhaps you meant no prior object-oriented programming experience? I mention that because while it only very briefly touches on the most-basic of programming concepts such as conditional branching with 'if', variables, etc., It seems to rush the user past all of that and somewhat hastily proceeds to dive head-first into advanced oop-related concepts like inheritance, polymorphism, virtual methods, and so forth. It's almost as if it were trying to squeeze years of learning into one short, informal lesson.

While it certainly is useful for giving an entry level person a hands-on taste of what can be done using the APIs, I would not recommend it as a 'first lesson' for those with no prior programming experience. In fact, introductory training should have nothing to do with AutoCAD development at all, as it only interjects more confusion into something that is already confusing enough :d

I've come to view serious development with .NET and ObjectARX as something that should really be done by professional programmers rather than AutoCAD users that happen to also be weekend programmers. Scripting with LISP is certainly in the domain of the latter, but complex development with any flavor of ObjectARX is something that should be left to skilled professionals, because of the potential damage that inexperienced programmers can do.  I remember one case where an inexperienced LISP programmer was able to corrupt a very large dataset to the extent that repairing it ultimately cost over 1000 times the fee he was paid.

Quote

I'm happy to agree to differ on this - working through the transformation matrix is also a good solution if that is your preference.


Well, doing it via manipulating the matrix also helps to show how one might apply the same kind of constraint to other types of entities like for example, block references that represent annotative elements or symbols containing text, dimensions, leaders, etc., more generally, cases where certain geometry and text should always have the same relative orientation.
Title: Re: AutoCAD - First Plug-in Training
Post by: SEANT on January 05, 2012, 03:38:40 AM
Tony T.?  Is that you?
Title: Re: AutoCAD - First Plug-in Training
Post by: gile on January 05, 2012, 03:53:14 AM
Tony T.?  Is that you?

It looks like, this remind me something:
Quote
I remember one case where an inexperienced LISP programmer was able to corrupt a very large dataset to the extent that repairing it ultimately cost over 1000 times the fee he was paid.

If that's you Tony, I'm glad to see you back. I missed you.
Title: Re: AutoCAD - First Plug-in Training
Post by: Kerry on January 05, 2012, 04:20:40 AM

< .. > this remind me something:
Quote
I remember one case where an inexperienced LISP programmer was able to corrupt a very large dataset to the extent that repairing it ultimately cost over 1000 times the fee he was paid.

< .. >

I thought it sounded familiar to me as well .... but then I've been known to mix up memories :D

Welcome TheMaster .. enjoy.

Regards
kdub.
Title: Re: AutoCAD - First Plug-in Training
Post by: BlackBox on January 05, 2012, 07:18:15 AM
So how does one become a "professional" programmer?  :?

... B.S.C.S. + MS Certifications (MCSD, MCAD, MCPD, etc.)?
Title: Re: AutoCAD - First Plug-in Training
Post by: Kerry on January 05, 2012, 07:23:35 AM
So how does one become a "professional" programmer?  :?

... B.S.C.S. + MS Certifications (MCSD, MCAD, MCPD, etc.)?

nope.

about 50000 lines of code almost gets you there though ...
.. and finding someone to pay for the coffee :)
Title: Re: AutoCAD - First Plug-in Training
Post by: BlackBox on January 05, 2012, 08:34:25 AM
So how does one become a "professional" programmer?  :?

... B.S.C.S. + MS Certifications (MCSD, MCAD, MCPD, etc.)?

nope.

about 50000 lines of code almost gets you there though ...
.. and finding someone to pay for the coffee :)

LoL, obviously (< Education Experience)... I was only asking as a self taught* LISP/.NET guy (+/-2 years since I learned what a defun was), who's going back to resume B.S.C.S. this fall.

*I am fortunate to have received much, much help of some great folks in the forums too of course. :wink:

Despite my experience with CAD, I feel that I have only scratched the surface of my programming abilities, and am interested in pursuing a development career.

Code: [Select]
If Career.CAD_Technician.Salary < Career.Programmer.Salary Then
  Return True
Else
  Return Nothing
End If

Title: Re: AutoCAD - First Plug-in Training
Post by: alanjt on January 05, 2012, 08:37:35 AM
Code: [Select]
If Career.CAD_Technician.Salary < Career.Programmer.Salary Then
  Return True
Else
  Return Nothing
End If
I *seriously* hope money isn't the reason to change a career.
Title: Re: AutoCAD - First Plug-in Training
Post by: BlackBox on January 05, 2012, 08:52:31 AM
I *seriously* hope money isn't the reason to change a career.

I'd be lying if I said it ($) wasn't a factor... but I meant what I said about only scratching the surface of my programming abilities.

I'd be happy to clarify, but that discussion is irrelevant to this thread; feel free to email me offline Alan.

Anyway, I don't mean to derail the thread further - thanks for the response Kerry.

Cheers! :beer:
Title: Re: AutoCAD - First Plug-in Training
Post by: alanjt on January 05, 2012, 08:58:41 AM
I'd be happy to clarify, but that discussion is irrelevant to this thread; feel free to email me offline Alan.
Not necessary. I was just curious about your claims to pursuits of more money.

Please resume your normal broadcasting...
Title: Re: AutoCAD - First Plug-in Training
Post by: TheMaster on January 05, 2012, 04:00:12 PM
So how does one become a "professional" programmer?  :?

... B.S.C.S. + MS Certifications (MCSD, MCAD, MCPD, etc.)?


A professional programmer is someone for whom programming is their primary job function exclusively, and who has the skills and experience gained from doing that for at least 2-3 years, in contrast to AutoCAD power-users or cad managers that might write code occasionally.

They can be self-taught or professionally trained, but because programming is an ongoing learning process that requires one to become proficient with and use new technologies productively as they evolve and come online, it's not simply a matter of one-time training. Acquiring and maintaining the required experience and skill really does require full-time dedication.

That's not to suggest that power users and cadd managers can't or shouldn't use .NET and ObjectARX for occasional scripting and automation, but if you're the one paying their salary, you have to weigh the need to bear the significant cost (and potential pitfalls) of the on-the-job learning curve with the potential benefits. On-the-job training at an employer's expense is certainly possible, provided the employer understands both the risks and the benefits, but unfortunately I fear that many smaller organizations that use CADD don't.

Title: Re: AutoCAD - First Plug-in Training
Post by: dgorsman on January 05, 2012, 05:20:49 PM
Smaller companies can't afford to have somebody dedicated to programming, nor can they afford to hire them in.  Companies around the world have been making use of talented individuals contributing in areas that are not their primary expertise; although some turn out badly thats no reason for a blanket exclusion.

The flip side of this disucssion is those whose primary profession is programming and keeping pace with programming methods & tools as suggested, are not spending the time necessary to stay current with the progam itself (e.g. AutoCAD, Civil3D) or the work processes in use by any given company, client, and/or industry.  This leads to applications which don't mesh well with what the users are actually doing, followed by complaints of "Must have been created by a programmer!".
Title: Re: AutoCAD - First Plug-in Training
Post by: BlackBox on January 05, 2012, 06:04:16 PM
Thanks, guys.

Education isn't everything, but it is something that I personally want to complete - If I don't find a way of making it happen, then it's not getting done. I consider my failing to accomplish something so 'simple' as my education despite the personal challenge to be unacceptable.

As for the balance between CAD/development proficiency; I'm in the gray area at the moment. I am highly proficient at the production side, which has benefited my development activities. I now actually prefer programming to production (for the past couple of years).

I come from a state sized firm that was gobbled up by a growing international firm. They're rate of acquisition propagated the philosophy that 'it's impossible to standardize 11K employees' so I am left with corporate standards that are less than what we had as the former company; critical pathed to a point, then a free for all thereafter. So there seems to be room for improvement - Just need to figure out how to build some momentum beyond the circle of CAD users that already contribute/utilize my development efforts.

I'm a LISPer who's teaching myself .NET now (mainly VB, WPF (XAML), VSTO, but I also want to learn C# among others). I am confident that development is going to remain a significant part of my work; I enjoy programming, constantly learning, and it (programming) solves so many issues either by increasing efficiency by removing redundancy, automating task completion, or providing new functionality. While I cannot code everything that comes to mind at the moment, programming just 'makes sense'.

Again, I really appreciate the feedback, guys.

Cheers! :beer:
Title: Re: AutoCAD - First Plug-in Training
Post by: TheMaster on January 05, 2012, 08:55:41 PM
Smaller companies can't afford to have somebody dedicated to programming, nor can they afford to hire them in. 

I must beg to differ, based largely on my own experiences. I'm a first-hand witness to many real-world cases where high-paid engineers or cadd managers attempted to implement reasonably-complex solutions that ultimately became never-ending projects that went on and on, before they gave up and/or went way over budget (in many cases by 2-3x over), and in some cases, most of the work they did had to be abandoned and/or completely re-done from scratch (in some cases, more than once!) before one could say they were finally working badly.

In most of those cases, I was witness by virtue of the fact that I was hired to help the customer recover from it, and that often meant having to completely redesign and implement the solution, and in almost every case, I was able to do that for a small fraction of what it had cost the customer to produce something that ultimately had to be abandoned. I estimate that roughly 30% of all the work I've done involves taking over existing projects that were floundering or had failed in the hands of in-house people that were not full-time programmers and did not have the experience needed to get the job done.

That's not only because I have years of experience developing AutoCAD solutions, but also because I was able to use my own existing reusable code libraries - the fruit of hundreds of man-hours of design, development, testing, and debugging which I've been working on and refining for many years.

Now, if we just consider that having the ability to make use of a truckload of existing code that's  already been heavily field-tested; debugged and is proven to work (by virtue of the fact that it is currently in use at many sites), the savings of having that (essentially for free when I'm using it contract work - with the caveat that they don't get to own that code outright, they instead get a non-exclusive license to use it), verses the cost of having to write all of the code that provides the equivalent functionality, it should be obvious that people that have been doing what I do on a full-time basis for many years, have a significant advantage over the average part-time, in-house engineer, cadd manager or others who usually must implement functionality offered by existing code libraries from scratch (or, pay significant licensing fees to acquire it from others).

Quote
Companies around the world have been making use of talented individuals contributing in areas that are not their primary expertise; although some turn out badly thats no reason for a blanket exclusion.

Well, I said 'most', which I don't think qualifies as a blanket exclusion.  And, it is places like this that many of those people turn to for help, sometimes without their bosses knowing they were even getting it  :laugh: :laugh:  Too bad most of them haven't donated to help cover the cost of maintaining this site  :oops:

Quote

The flip side of this disucssion is those whose primary profession is programming and keeping pace with programming methods & tools as suggested, are not spending the time necessary to stay current with the progam itself (e.g. AutoCAD, Civil3D) or the work processes in use by any given company, client, and/or industry.  This leads to applications which don't mesh well with what the users are actually doing, followed by complaints of "Must have been created by a programmer!".

Well, by professional programmer, I didn't typically mean some dude with a pony tail, that was working in the IT department of a Wall Street brokerage firm last year, and maybe was working for Yahoo for a few years before that.

Many full-time professional programmers who provide solutions on a consulting basis are people that specialize in industry-specific solutions, and for those that specialize in AutoCAD or an industry that uses it, AutoCAD knowledge is as important if not more important than general programming knowledge. Those kind of programmers/consultants are almost always more intimately familiar with AutoCAD and other tools used by their customers than the customer is.
Title: Re: AutoCAD - First Plug-in Training
Post by: It's Alive! on January 06, 2012, 02:01:37 AM

I tried this and it makes the Attribute appear horizontal during the Jig of the Insert but crashes with a eOpenforWrite error after the rotation is selected......


I think that's because this simple example wasn't done correctly to start with (Sorry 'bout that Mr. Preston).

The TransformBy() method takes a Matrix3d and applies the transformation defined by the matrix.

If you want to alter the transformation in some way, you don't supermessage the base type and pass it the original Matrix3d parameter as supplied (which applies the tranformation), and then perform a second transformation, by setting the rotation property. Sorry again, but that is the classic definition of a kludge.

What the author should have done was modify the Matrix3d that was passed into the override of TransformBy() to eliminate any rotation, and then supermessage the base type and pass it the modified matrix.

As far as the errors you may be seeing, the object(s) involved are already open for write when the method is called, but may not always be open in a transaction in certain circumstances.

I had a supermassage once
Title: Re: AutoCAD - First Plug-in Training
Post by: Jeff H on January 24, 2012, 10:23:35 AM
was modify the Matrix3d that was passed into the override of TransformBy() to eliminate any rotation, and then supermessage the base type and pass it the modified matrix.
Does anyone know how that is accomplished?
 
I am sure it is just some simple nerd math.
Title: Re: AutoCAD - First Plug-in Training
Post by: gile on January 24, 2012, 12:01:19 PM
Does anyone know how that is accomplished?
 
I am sure it is just some simple nerd math.

Doesn't the reply #40 (http://www.theswamp.org/index.php?topic=40471.msg458579#msg458579) accomplish it ?
Title: Re: AutoCAD - First Plug-in Training
Post by: Jeff H on January 24, 2012, 12:14:54 PM
Does anyone know how that is accomplished?
 
I am sure it is just some simple nerd math.

Doesn't the reply #40 (http://www.theswamp.org/index.php?topic=40471.msg458579#msg458579) accomplish it ?
Oops,
I do not know how I missed that.
I was actually hoping you would reply since you always have nice clean solution to transformations etc.....
So Sorry and thanks again!!!!
Title: Re: AutoCAD - First Plug-in Training
Post by: gile on January 28, 2012, 12:07:56 PM
A nicer way (thanks to kaefer to remind me the Viewport.EyeToWorldTransform property)

Replace:
Code - C#: [Select]
  1.                Point3d org = attRef.Position;
  2.                Vector3d zVec = vd.Viewport.ViewDirection;
  3.                Vector3d yVec = vd.Viewport.CameraUpVector;
  4.                Vector3d xVec = yVec.CrossProduct(zVec).GetNormal();
  5.                Matrix3d viewMat =
  6.                    Matrix3d.AlignCoordinateSystem(
  7.                    Point3d.Origin, Vector3d.XAxis, Vector3d.YAxis, Vector3d.ZAxis,
  8.                    org, xVec, yVec, zVec) *
  9.                    Matrix3d.WorldToPlane(new Plane(org, attRef.Normal)) *
  10.                    Matrix3d.Rotation(-attRef.Rotation, attRef.Normal, org);

With:
Code - C#: [Select]
  1.                Point3d org = attRef.Position;
  2.                Matrix3d viewMat =
  3.                    Matrix3d.Displacement(vd.Viewport.CameraTarget.GetVectorTo(org)) *
  4.                    vd.Viewport.EyeToWorldTransform *
  5.                    Matrix3d.WorldToPlane(new Plane(org, attRef.Normal)) *
  6.                    Matrix3d.Rotation(-attRef.Rotation, attRef.Normal, org);
Title: Re: AutoCAD - First Plug-in Training
Post by: Jeff H on February 05, 2012, 08:27:28 AM
A nicer way (thanks to kaefer to remind me the Viewport.EyeToWorldTransform property)

Replace:
Code - C#: [Select]
  1.                Point3d org = attRef.Position;
  2.                Vector3d zVec = vd.Viewport.ViewDirection;
  3.                Vector3d yVec = vd.Viewport.CameraUpVector;
  4.                Vector3d xVec = yVec.CrossProduct(zVec).GetNormal();
  5.                Matrix3d viewMat =
  6.                    Matrix3d.AlignCoordinateSystem(
  7.                    Point3d.Origin, Vector3d.XAxis, Vector3d.YAxis, Vector3d.ZAxis,
  8.                    org, xVec, yVec, zVec) *
  9.                    Matrix3d.WorldToPlane(new Plane(org, attRef.Normal)) *
  10.                    Matrix3d.Rotation(-attRef.Rotation, attRef.Normal, org);

With:
Code - C#: [Select]
  1.                Point3d org = attRef.Position;
  2.                Matrix3d viewMat =
  3.                    Matrix3d.Displacement(vd.Viewport.CameraTarget.GetVectorTo(org)) *
  4.                    vd.Viewport.EyeToWorldTransform *
  5.                    Matrix3d.WorldToPlane(new Plane(org, attRef.Normal)) *
  6.                    Matrix3d.Rotation(-attRef.Rotation, attRef.Normal, org);

I might of had something wrong but the second version made attribute move way off.
 
But for rotating the attribute the same distance from block insertion the first version seems to work very well with one small change if attribute is set to Middle Center.
Just changed
Code - C#: [Select]
  1. Point3d org = attRef.Position;
  2.  
to
Code - C#: [Select]
  1. Point3d org = attRef.AlignmentPoint;
  2.  

This pic at bottom might help explain a little as the "A" stays centered in the rectangle & 'GFI' position.
 
 
 
Title: Re: AutoCAD - First Plug-in Training
Post by: Jeff H on February 05, 2012, 08:53:57 AM
Just wanted to add that with settings mentioned below the only thing I have found so far that throws a little wrench in it is using a dynamic block with rotation paramter, but alignment parameters, dview, perspective on, and much more works great!
 
Thanks again Gile!!!!
Title: Re: AutoCAD - First Plug-in Training
Post by: gile on February 05, 2012, 10:54:11 AM
Hi,

Effectively, it didn't care about the attribute justification.

Quote
the second version made attribute move way off.
Curiously, it needs a kind of regeneration which occurs on starting a 3d orbit.

Anyway, here's a "more traditional" way which seems to work (with attribute justification too):
Code: [Select]
            Point3d org = attRef.Justify == AttachmentPoint.BaseLeft ||
                attRef.Justify == AttachmentPoint.BaseAlign ||
                attRef.Justify == AttachmentPoint.BaseFit ?
                attRef.Position : attRef.AlignmentPoint;
            Matrix3d viewMat =
                Matrix3d.PlaneToWorld(new Plane(org, vd.Viewport.ViewDirection)) *
                Matrix3d.WorldToPlane(new Plane(org, attRef.Normal)) *
                Matrix3d.Rotation(-attRef.Rotation, attRef.Normal, org);
Title: Re: AutoCAD - First Plug-in Training
Post by: Jeff H on February 05, 2012, 11:10:51 AM
I can think of a couple of ways to make the rotation persistant or modify the actual AtrributeReference, but just seeing what ideas anyone had.
 
To sum up what would you think the best approach to modify the AttributeReference so when the drawing is opened with overrule not loaded it appears the same until of course it gets modified?
 
Title: Re: AutoCAD - First Plug-in Training
Post by: gile on February 05, 2012, 12:25:44 PM
Hi,

By my side, I think the overruling route is nice because it doesn't modify the attribute (or whatever it applys to) geometry.

I'd make it "persistant" adding a class which implements IExtensionApplication to the project (assuming the ActivateOverrule is static and defined in the Commands class) and registering the dll so that it's automatically loaded.

Code: [Select]
    public class ExtensionApplication : IExtensionApplication
    {
        public void Initialize()
        {
            Commands.ActivateOverrule();
        }

        public void Terminate() { }
    }
Title: Re: AutoCAD - First Plug-in Training
Post by: Jeff H on February 05, 2012, 12:50:47 PM
If you are wanting to send it to a client who does not have it loaded then all the attributes are every which way.
 
Also noticed that if I changed ModelSpace's Visual Style to anything other 2d Wireframe it would not move or be hidden during the jig, but once set it was placed correctly.
Overriding SetAttributes corrected the issue but had to use most expensive flag
Code - C#: [Select]
  1.  public override int SetAttributes(Drawable drawable, DrawableTraits traits)
  2.         {
  3.             return base.SetAttributes(drawable, traits) + (int)DrawableAttributes.RegenDraw;
  4.         }
  5.