Total Length of Selected Objects

Total Length of Selected Objects

Anonymous
Not applicable
7,151 Views
13 Replies
Message 1 of 14

Total Length of Selected Objects

Anonymous
Not applicable

Hey there everyone, 

 

I am brand new to the forum - at least posting wise.  I have found the answer to many a quesiton on here before.  I work primarily in Civil 3D, but am exploring the creation of custom commands in c#.NET.  

I am trying to write a command that when run, asks the user to make a selection on screen, click enter, and it gives the total length of all selected objects.  I can get the basic understanding of c#, but have never worked with the Autodesk API's.  I have a github repository that I have stored my current code in and I am wondering if I could get some guidance on it from you, the community.  Thanks so much for your help! (please be gentle, haha)

 

https://github.com/BenKaiser12/TOTLEN

0 Likes
Accepted solutions (2)
7,152 Views
13 Replies
Replies (13)
Message 2 of 14

Anonymous
Not applicable
Accepted solution

Hey y'all 

 

I got the command working.  The correct version is posted on github.  Feel free to go grab it and give it a try.  If you like it, let me know!  Thanks

 

Total Length Visual Studio Project

0 Likes
Message 3 of 14

_gile
Consultant
Consultant
Accepted solution

Hi,

 

First, thanks for sharing.

 

If I may have some suggestions:

  • You should use a selection filter to get only measurable entities.
  • You do not need to test the existence of an implied selection set, CommandFlags.UsePickSet will do all the magic (including the selection filter).
  • Optionally, you can also use Linq to process the selection set in a more declarative way.
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
using System.Linq;
using AcAp = Autodesk.AutoCAD.ApplicationServices.Application;

[assembly: CommandClass(typeof(TotalLength.Commands))]

namespace TotalLength
{
    public class Commands
    {
        [CommandMethod("TOTLEN", CommandFlags.UsePickSet)]
        public void TotalLength()
        {
            var doc = AcAp.DocumentManager.MdiActiveDocument;
            var db = doc.Database;
            var ed = doc.Editor;

            // build a selection filter to get only measurable entities
            var filter = new SelectionFilter(new[]
            {
                new TypedValue(-4, "<OR"),
                new TypedValue(0, "ARC,CIRCLE,ELLIPSE,LINE,LWPOLYLINE,SPLINE"),
                new TypedValue(-4, "<AND"),
                new TypedValue(0, "POLYLINE"), // polylines 2d or 3d
                new TypedValue(-4, "<NOT"), // but not meshes
                new TypedValue(-4, "&"),
                new TypedValue(70, 112),
                new TypedValue(-4, "NOT>"),
                new TypedValue(-4, "AND>"),
                new TypedValue(-4, "OR>")
            });

            var selection = ed.GetSelection(filter);
            if (selection.Status != PromptStatus.OK)
                return;

            using (var tr = db.TransactionManager.StartTransaction())
            {
                // use Linq queries to get lengths by type in a dictionary
                var lengthes = selection.Value
                    .Cast<SelectedObject>()
                    .Select(so => (Curve)tr.GetObject(so.ObjectId, OpenMode.ForRead))
                    .ToLookup(curve => curve.GetType().Name,                         // <- key selector
                              curve => curve.GetDistanceAtParameter(curve.EndParam)) // <- element selector
                    .ToDictionary(group => group.Key,    // <- key selector
                                  group => group.Sum()); // <- element selector

                // print results
                foreach (var entry in lengthes)
                {
                    ed.WriteMessage($"\n{entry.Key,-12} = {entry.Value,9:0.00}");
                }
                ed.WriteMessage($"\nTotal Length = {lengthes.Values.Sum(),9:0.00}");

                tr.Commit();
            }
            AcAp.DisplayTextScreen = true;
        }
    }
}

 

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 4 of 14

Anonymous
Not applicable

 Hey there Gilles, Thanks for the reply and suggestions! 

Would you mind if I asked a couple of questions?  

1. Why use 'var' instead of the type?  

2. in the selection filter, what do the -4, 0, and 70 mean?

3.  I have never used LINQ, can you point me in the direction of a good resource to learn it? 

 

Thanks for the help!! 

0 Likes
Message 5 of 14

_gile
Consultant
Consultant

1. Because var is shorter to type than most explicit types and give the same result. Furthemore, IMO, it makes the code more readable (avoiding redundancy as: SelectionFilter filter = new SelectionFilter()). Using inferred type is also an F# habit.

 

2. Using the int values of the DxfCode enum in selection filters is a old AutoLISP habit.

3. Getting started with LINQ in C# may be a good start. Within AutoCAD we mostly use LINQ to Objects with AutoCAD collections. By my side, I prefer use the Method Syntax with IEnumerable<T> extension methods and lambda expressions (again my functional programming background with LISP and F#).



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 6 of 14

wang890
Collaborator
Collaborator

hey if you guys are still around. i am trying to put this program in vb.net. the program wont go past the "cast" part.

 

i converted it from c# using online converter, tried several and found one that's decent.

 

Using tr = db.TransactionManager.StartTransaction()
Dim lengths = selection.Value.Cast(Of SelectedObject)().[Select](Function(so) CType(tr.GetObject(so.ObjectId, OpenMode.ForRead), Curve)).ToLookup(Function(curve) curve.[GetType]().Name, Function(curve) curve.GetDistanceAtParameter(curve.EndParam)).ToDictionary(Function(group) group.Key, Function(group) group.Sum())

For Each entry As Object In lengths
ed.WriteMessage($"\n{entry.Key} = {entry.Value}")
Next

ed.WriteMessage($"\nTotal Length = {lengths.Values.Sum()}")
tr.Commit()
End Using

 

i completely don't understand that whole lengths = line. maybe that's the linq : )

 

pretty interesting how you go through all the object types with one line of code. i would normally write it out case by case. 

 

thank you!

Stantec
Dell Precision 5530, Prism M320PU, C3D 14/17/19
0 Likes
Message 7 of 14

_gile
Consultant
Consultant

Hi,

 

I'm not very comfortable with VB but it seems to me the issue is due to the way the converter converted the typedValue array initialisation.

 

try this way (not tested)

Public Sub TotalLength()
    Dim doc = AcAp.DocumentManager.MdiActiveDocument
    Dim db = doc.Database
    Dim ed = doc.Editor
    Dim filter = New SelectionFilter(New TypedValue() { _
	New TypedValue(-4, "<OR"), _ 
	New TypedValue(0, "ARC,CIRCLE,ELLIPSE,LINE,LWPOLYLINE,SPLINE"), _ 
	New TypedValue(-4, "<AND"), _
	New TypedValue(0, "POLYLINE"), _
	New TypedValue(-4, "<NOT"), _
	New TypedValue(-4, "&"), _
	New TypedValue(70, 112), _
	New TypedValue(-4, "NOT>"), _
	New TypedValue(-4, "AND>"), _
	New TypedValue(-4, "OR>") })
    Dim selection = ed.GetSelection(filter)
    If selection.Status <> PromptStatus.OK Then Return

    Using tr = db.TransactionManager.StartTransaction()
        Dim lengthes = selection.Value _
			.Cast(Of SelectedObject)() _
			.Select(Function(so) CType(tr.GetObject(so.ObjectId, OpenMode.ForRead), Curve)) _
			.ToLookup(Function(curve) curve.GetType().Name, Function(curve) curve.GetDistanceAtParameter(curve.EndParam)) _
			.ToDictionary(Function(group) group.Key, Function(group) group.Sum())

        For Each entry In lengthes
            ed.WriteMessage($"\n{entry.Key} = {entry.Value}")
        Next

        ed.WriteMessage($"\nTotal Length = {lengthes.Values.Sum()}")
        tr.Commit()
    End Using

    AcAp.DisplayTextScreen = True
End Sub

Anyway, the use of an online converter and copy / paste without trying to understand the code does is the worst way to learn.

You should re-write the code to suit your coding conventions or habits.



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes
Message 8 of 14

wang890
Collaborator
Collaborator

still getting this:

 

System.MissingMemberException: 'Public member 'Cast' on type 'SelectionSetDelayMarshalled' not found.'

 

is there a way to put a lisp file in vb.net and add that command? i have this request to put in the total length lisp that you can download on the internet and put into another program with a lot of commands.

Stantec
Dell Precision 5530, Prism M320PU, C3D 14/17/19
0 Likes
Message 9 of 14

_gile
Consultant
Consultant

Did you add an 'Import System.Linq' statement to your file?

Mixing LISP and .NET is much more complex and much less reliable than simply make this code work.



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes
Message 10 of 14

_gile
Consultant
Consultant

Working solution (tested with AutoCAD 2017)

The Compile Options settings:

Explicit = On

Strict = On

Infer = On

 

Imports Autodesk.AutoCAD.ApplicationServices
Imports Autodesk.AutoCAD.DatabaseServices
Imports Autodesk.AutoCAD.EditorInput
Imports Autodesk.AutoCAD.Runtime

Public Class Commands
    <CommandMethod("TOTLEN", CommandFlags.UsePickSet)>
    Public Sub TotalLength()
        Dim doc = Application.DocumentManager.MdiActiveDocument
        Dim db = doc.Database
        Dim ed = doc.Editor
        Dim filter = New SelectionFilter(New TypedValue() {
            New TypedValue(-4, "<OR"),
            New TypedValue(0, "ARC,CIRCLE,ELLIPSE,LINE,LWPOLYLINE,SPLINE"),
            New TypedValue(-4, "<AND"),
            New TypedValue(0, "POLYLINE"),
            New TypedValue(-4, "<NOT"),
            New TypedValue(-4, "&"),
            New TypedValue(70, 112),
            New TypedValue(-4, "NOT>"),
            New TypedValue(-4, "AND>"),
            New TypedValue(-4, "OR>")})
        Dim selection = ed.GetSelection(filter)
        If selection.Status <> PromptStatus.OK Then Return

        Using tr = db.TransactionManager.StartTransaction()
            Dim lengthes = selection.Value _
                .Cast(Of SelectedObject)() _
                .Select(Function(so) CType(tr.GetObject(so.ObjectId, OpenMode.ForRead), Curve)) _
                .ToLookup(Function(curve) curve.GetType().Name, Function(curve) curve.GetDistanceAtParameter(curve.EndParam)) _
                .ToDictionary(Function(group) group.Key, Function(group) group.Sum())

            For Each entry In lengthes
                ed.WriteMessage($"{vbLf}{entry.Key} = {entry.Value}")
            Next

            ed.WriteMessage($"{vbLf}Total Length = {lengthes.Values.Sum()}")
            tr.Commit()
        End Using

        Application.DisplayTextScreen = True
    End Sub
End Class


Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 11 of 14

wang890
Collaborator
Collaborator

thank you so much for this. this is it, works perfectly in 2020 too.

 

i turned on the other 2 except explicit. like 160 errors if i turn that on. some lousy declarations and casts there haha. 

Stantec
Dell Precision 5530, Prism M320PU, C3D 14/17/19
0 Likes
Message 12 of 14

wang890
Collaborator
Collaborator

hey

so now i'm asked to have each object type total length listed separately as one line in the message box, then followed by the total of all objects like before.

 

i think that's quite a bit extra work yeah?  have to do a select case somehow?

 

Thank you.

Stantec
Dell Precision 5530, Prism M320PU, C3D 14/17/19
0 Likes
Message 13 of 14

genosyde
Advocate
Advocate

I love method syntax too,

It give us more shorter sentence and data sorting.

 

0 Likes
Message 14 of 14

wang890
Collaborator
Collaborator
Hey, i am revisiting this program again and i want to add in civil 3d feature line. do you think you can point me to how to add that in? after 3 years i still don't know how the whole syntax work on this program. didn't program much but i can still figure out most things except this one : )

so i just want to add civil 3d feature lines together and get a total length from it's lengh property.
Stantec
Dell Precision 5530, Prism M320PU, C3D 14/17/19
0 Likes