Author Topic: TypedValue using DXF Code with multiple values  (Read 8215 times)

0 Members and 1 Guest are viewing this topic.

jabowabo

  • Mosquito
  • Posts: 20
TypedValue using DXF Code with multiple values
« on: March 28, 2013, 09:07:25 PM »
Hi all, I'm new to The Swamp and .NET!  I've manage to do some really cool, if basic, things already but I am really hung up on this one.  I am using Autodesk CADmep which allows users to assign data to DXF code 300.  I'm trying to create a filter that checks the data there but can't seem to figure out how.  All the other DXF codes I have seen only have one value whereas this one can have many dotted pairs.  When I try to use the code below on any other codes it works as expected, but on 300 I get nothing in my selection set.

Code: [Select]
TypedValue[] acTypValAr = new TypedValue[1];
acTypValAr.SetValue(new TypedValue((int) 300, "myvalue"), 0);


Using the LISP (entget (car (entsel))) I get back something like this:

Quote
((-1 . <Entity name: 7ffff81d470>) (0 . "MAPS_SOLID") ...<snipped>... (300 . "Bar Joists - K Series") (300 . "Design: Structural") (300 . "") (300 . "13.891") (300 . "0.000")...

Is there any way to access the dotted pairs as typed values individually or at least to write the list to a variable or array? Any tips or nudges toward the right direction would be greatly appreciated!

TheMaster

  • Guest
Re: TypedValue using DXF Code with multiple values
« Reply #1 on: March 28, 2013, 10:17:40 PM »
Hi all, I'm new to The Swamp and .NET!  I've manage to do some really cool, if basic, things already but I am really hung up on this one.  I am using Autodesk CADmep which allows users to assign data to DXF code 300.  I'm trying to create a filter that checks the data there but can't seem to figure out how.  All the other DXF codes I have seen only have one value whereas this one can have many dotted pairs.  When I try to use the code below on any other codes it works as expected, but on 300 I get nothing in my selection set.

Code: [Select]
TypedValue[] acTypValAr = new TypedValue[1];
acTypValAr.SetValue(new TypedValue((int) 300, "myvalue"), 0);


Using the LISP (entget (car (entsel))) I get back something like this:

Quote
((-1 . <Entity name: 7ffff81d470>) (0 . "MAPS_SOLID") ...<snipped>... (300 . "Bar Joists - K Series") (300 . "Design: Structural") (300 . "") (300 . "13.891") (300 . "0.000")...

Is there any way to access the dotted pairs as typed values individually or at least to write the list to a variable or array? Any tips or nudges toward the right direction would be greatly appreciated!

Your question is a bit vague, do you mean a selection filter?  If so, have you tried the filtering with LISP's (ssget "x" '(...)) to see if it will select something? If that comes up empty, then the data is not supported (generally, repeatable group codes aren't).

I'm not sure what to make of your last question. What 'dotted pairs' do you mean?  In .NET, a LISP list containing dotted pairs translates to an array of TypedValue[], and to get the values out of the array you use the array indexer, and the Value property, as in acTypeValAr[<index>].Value.

Also, we don't generally use SetValue() and GetValue() on arrays, we use the array indexer, like this:  myArray[index] = value;  or value = myArray[index].  The GetValue() and SetValue() methods take a system.Object as their arguments and that means there must be a boxing/unboxing conversion which is very slow. 

jabowabo

  • Mosquito
  • Posts: 20
Re: TypedValue using DXF Code with multiple values
« Reply #2 on: March 28, 2013, 11:36:45 PM »
Sorry for not being clear, I'm still learning the terminology.  :oops:
This is the code I am trying use to filter (adapted from a Kean Walmsley post http://bit.ly/9KFypk):
Code: [Select]
  [CommandMethod("FSS")]
        public static void FilterSelectionSet()
        {
            // Get the current document editor
            Editor acDocEd = Application.DocumentManager.MdiActiveDocument.Editor;
            Document acDoc = Application.DocumentManager.MdiActiveDocument;

            PromptStringOptions pStrOpts1 = new PromptStringOptions("\nEnter DXF Code number: ");
            pStrOpts1.AllowSpaces = true;
            PromptResult pStrRes1 = acDoc.Editor.GetString(pStrOpts1);
            string dcnumstr = pStrRes1.StringResult;
            int dcnum = Convert.ToInt16(dcnumstr);
           
            PromptStringOptions pStrOpts2 = new PromptStringOptions("\nEnter DXF Code value: ");
            pStrOpts2.AllowSpaces = true;
            PromptResult pStrRes2 = acDoc.Editor.GetString(pStrOpts2);
            string dcval = pStrRes2.StringResult;
             
            // Create a TypedValue array to define the filter criteria
            TypedValue[] acTypValAr = new TypedValue[1];
            acTypValAr.SetValue(new TypedValue((int) dcnum, dcval), 0);
           
            // Assign the filter criteria to a SelectionFilter object
            SelectionFilter acSelFtr = new SelectionFilter(acTypValAr);

            // Request for objects to be selected in the drawing area
            PromptSelectionResult acSSPrompt;
            acSSPrompt = acDocEd.GetSelection(acSelFtr);

            // If the prompt status is OK, objects were selected
            if (acSSPrompt.Status == PromptStatus.OK)
            {
                SelectionSet acSSet = acSSPrompt.Value;

                Application.ShowAlertDialog("Number of objects selected: " + acSSet.Count.ToString());
            }
            else
            {
                Application.ShowAlertDialog("Number of objects selected: 0");
            }
        }
I get back "nil" with (ssget "x" (list (cons 300 "*joist*"))) - with or without wildcards.  So it looks like this is probably not going to work:
Quote
If that comes up empty, then the data is not supported (generally, repeatable group codes aren't).

 Dotted pairs was a bad choice of terms.  The LISP output I quoted before showed the DXF codes and their values separated by a period.  I thought maybe there was some way to get those into an array. 

I can use the Entity.List() method to show the list at the command line but I'm still trying to find a way to get it into my .net program.  Sounds like I need to look into this?

Quote
In .NET, a LISP list containing dotted pairs translates to an array of TypedValue[], and to get the values out of the array you use the array indexer, and the Value property, as in acTypeValAr[<index>].Value.

Thanks for your help (and patience)!

TheMaster

  • Guest
Re: TypedValue using DXF Code with multiple values
« Reply #3 on: March 29, 2013, 12:49:41 AM »
Sorry for not being clear, I'm still learning the terminology.  :oops:
This is the code I am trying use to filter (adapted from a Kean Walmsley post http://bit.ly/9KFypk):
Code: [Select]
  [CommandMethod("FSS")]
        public static void FilterSelectionSet()
        {
            // Get the current document editor
            Editor acDocEd = Application.DocumentManager.MdiActiveDocument.Editor;
            Document acDoc = Application.DocumentManager.MdiActiveDocument;

            PromptStringOptions pStrOpts1 = new PromptStringOptions("\nEnter DXF Code number: ");
            pStrOpts1.AllowSpaces = true;
            PromptResult pStrRes1 = acDoc.Editor.GetString(pStrOpts1);
            string dcnumstr = pStrRes1.StringResult;
            int dcnum = Convert.ToInt16(dcnumstr);
           
            PromptStringOptions pStrOpts2 = new PromptStringOptions("\nEnter DXF Code value: ");
            pStrOpts2.AllowSpaces = true;
            PromptResult pStrRes2 = acDoc.Editor.GetString(pStrOpts2);
            string dcval = pStrRes2.StringResult;
             
            // Create a TypedValue array to define the filter criteria
            TypedValue[] acTypValAr = new TypedValue[1];
            acTypValAr.SetValue(new TypedValue((int) dcnum, dcval), 0);
           
            // Assign the filter criteria to a SelectionFilter object
            SelectionFilter acSelFtr = new SelectionFilter(acTypValAr);

            // Request for objects to be selected in the drawing area
            PromptSelectionResult acSSPrompt;
            acSSPrompt = acDocEd.GetSelection(acSelFtr);

            // If the prompt status is OK, objects were selected
            if (acSSPrompt.Status == PromptStatus.OK)
            {
                SelectionSet acSSet = acSSPrompt.Value;

                Application.ShowAlertDialog("Number of objects selected: " + acSSet.Count.ToString());
            }
            else
            {
                Application.ShowAlertDialog("Number of objects selected: 0");
            }
        }
I get back "nil" with (ssget "x" (list (cons 300 "*joist*"))) - with or without wildcards.  So it looks like this is probably not going to work:
Quote
If that comes up empty, then the data is not supported (generally, repeatable group codes aren't).

 Dotted pairs was a bad choice of terms.  The LISP output I quoted before showed the DXF codes and their values separated by a period.  I thought maybe there was some way to get those into an array. 

I can use the Entity.List() method to show the list at the command line but I'm still trying to find a way to get it into my .net program.  Sounds like I need to look into this?

Quote
In .NET, a LISP list containing dotted pairs translates to an array of TypedValue[], and to get the values out of the array you use the array indexer, and the Value property, as in acTypeValAr[<index>].Value.

Thanks for your help (and patience)!

Ok, I understand you now, thanks for clarifying.

The last part, about getting the data into an array, depends on a few things.

If this is an entity/object from AutoCAD MEP, then you are supposed to use that
vertical product's managed API to access the object and its data.

Out of curiosity, can you tell us where you got the idea of using GetValue() to set the value of an array, as you show in your OP ?
« Last Edit: March 29, 2013, 12:55:26 AM by TT »

jabowabo

  • Mosquito
  • Posts: 20
Re: TypedValue using DXF Code with multiple values
« Reply #4 on: March 29, 2013, 01:42:20 AM »
The entitiesI am trying to get data from are not from Acad MEP, they are from a product that Autodesk bought last year called CADmep.  There is no API for it except for a scripting interface.  I can do quite a bit with that but I think I can do better if I can just figure a few things out.  If you're curious about what I'm working on you can watch the 2 minute video demo here:  http://www.youtube.com/watch?v=QxjKepUHAa8
The app has been a real time-saver for me an I've given it away to a several dozen other users of the software an received a lot of positive feedback.  If I can get access to the list data for the entities I think I can make it much more efficient.

The SetValue part was lifted directly from the Wamsley post I referenced in the previous post.  I'm still cutting and pasting a lot of the stuff I find difficult to understand and then kinda trying to figure out what it does and why.

Thanks again for your help!

TheMaster

  • Guest
Re: TypedValue using DXF Code with multiple values
« Reply #5 on: March 29, 2013, 05:12:10 AM »
The entitiesI am trying to get data from are not from Acad MEP, they are from a product that Autodesk bought last year called CADmep.  There is no API for it except for a scripting interface.  I can do quite a bit with that but I think I can do better if I can just figure a few things out.  If you're curious about what I'm working on you can watch the 2 minute video demo here:  http://www.youtube.com/watch?v=QxjKepUHAa8
The app has been a real time-saver for me an I've given it away to a several dozen other users of the software an received a lot of positive feedback.  If I can get access to the list data for the entities I think I can make it much more efficient.

In that case, your only option is to P/Invoke acdbEntGet(), which is the native ObjectARX counterpart to LISP's (entget) function.

You can search this group for 'acdbEntGet()' and you will find code that shows how to do it, but you will need to also P/Invoke acdbGetAdsName(), and need to be careful with the EntryPoint= part of that method's [DllImport] attribute, because it is platform-specific (there are different entry point symbols for 32 and 64 bit platforms), and you will need to use the entry point from the major release you're working with (e.g., R17, R18, or R19).  You can use Depends.exe to find the entry point symbol for acdbGetAdsName().

Quote
The SetValue part was lifted directly from the Wamsley post I referenced in the previous post.  I'm still cutting and pasting a lot of the stuff I find difficult to understand and then kinda trying to figure out what it does and why.

The reason I asked about that, is because, I had made some comments in another thread regarding Kean's blog and the fact opinion that I didn't think it was the best way to learn about C# programming, and this is a good example case in point.

My advice is to try to spend as much time learning about the language and the .NET framework, as you spend trying to do things with the AutoCAD managed API. While it may be possible for some to learn the language 'on the fly' while trying to wrestle with a very complicated API, that's not true for most, and IMO, isn't the best way to learn a programming language and framework.
« Last Edit: March 29, 2013, 11:57:19 PM by TT »

jabowabo

  • Mosquito
  • Posts: 20
Re: TypedValue using DXF Code with multiple values
« Reply #6 on: March 29, 2013, 08:16:16 AM »
Thanks for pointing me in the right direction - in more ways than one.:)

TheMaster

  • Guest
Re: TypedValue using DXF Code with multiple values
« Reply #7 on: March 29, 2013, 11:59:49 PM »
Thanks for pointing me in the right direction - in more ways than one.:)

If P/Invoke'ing acdbEntGet() is a bit of a hassle, you could also write a Lisp-callable function (LispFunction) that can be passed the result of a call to LISP's (entget) function as well, which would give you what you need in the ResultBuffer argument.

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8722
  • AKA Daniel
Re: TypedValue using DXF Code with multiple values
« Reply #8 on: March 30, 2013, 10:02:51 AM »
Did you look for COM interop for said program? That’s the first place I’d look if no .net API is available  :-)

jabowabo

  • Mosquito
  • Posts: 20
Re: TypedValue using DXF Code with multiple values
« Reply #9 on: March 30, 2013, 12:27:26 PM »
Did you look for COM interop for said program? That’s the first place I’d look if no .net API is available  :-)
I'm not sure where to start to look for COM interop but I'll poke around. I managed to get TT's P/Invoke acdbEntGet() to return exactly what I need.  Before I commit to that method I am also testing his LISP function suggestion which is slow going so far but I'm making progress.  I would rather go the latter route to keep things simple. 

Entity.List() will dump all the data I need to the command line - seems like it ought to be a simple thing to write it to a variable instead but I guess if it was simple everyone would be doing it  :-D

Thanks!

TheMaster

  • Guest
Re: TypedValue using DXF Code with multiple values
« Reply #10 on: March 30, 2013, 12:56:05 PM »
Before I commit to that method I am also testing his LISP function suggestion which is slow going so far but I'm making progress.  I would rather go the latter route to keep things simple.

Taking the data from LISP is pretty simple.

Code - C#: [Select]
  1. public static class MyLispFunctions
  2. {
  3.    [LispFunction( "SETENTGETDATA" )]
  4.    public static void SetEntGetData( ResultBuffer rb )
  5.    {
  6.       if( rb == null )
  7.          return;
  8.       Document doc = Application.DocumentManager.MdiActiveDocument;
  9.       var ed = doc.Editor;
  10.       TypedValue[] array = rb.AsArray();
  11.       for(int i = 0; i < array.Length; i++ )
  12.       {
  13.          TypedValue tv = array[i];
  14.          ed.WriteMessage( "\n[{0}]: TypeCode = {1}  Value = {2}",
  15.             i, tv.TypeCode, tv.Value );
  16.       }
  17.    }
  18. }
  19.  

From lisp, you just do  (setentgetdata (entget <ename>))


jabowabo

  • Mosquito
  • Posts: 20
Re: TypedValue using DXF Code with multiple values
« Reply #11 on: March 30, 2013, 03:23:42 PM »
Quote
Taking the data from LISP is pretty simple.
Ha! You make it look simple - that is awesome!   I'm going to try to modify it to to get a filtered selection set instead of just one entity and store the values to a datatable.  Once I get that done, the rest is gravy!

Thanks again TT, you rock! :mrgreen:

jabowabo

  • Mosquito
  • Posts: 20
Re: TypedValue using DXF Code with multiple values
« Reply #12 on: April 07, 2013, 06:41:04 PM »
A follow-up question if you will indulge me:
I am able to get the data I need from a selection set but only by calling the lisp function (see below) on each item and processing a result buffer for each one individually one at a time.  I can't help but think it would be faster to get a result buffer for all the items at once.  Unfortunately, I haven't been able to make this work by iterating the entities within a LISP.   Is this even possible and is it reasonable to think this is a faster method? 

One more by-product I'd like to eliminate is the command line advances a blank line each time the lisp I am using is run.  (edit: looks like NOMUTT will do this) Thank you for any help!

Code: [Select]
acDoc.SendStringToExecute("(GETMAPLISTDATA (entget (handent \"" + objHand + "\"))) ", false, false, false);
  where objHand = entity handle
« Last Edit: April 07, 2013, 07:11:05 PM by jabowabo »

TheMaster

  • Guest
Re: TypedValue using DXF Code with multiple values
« Reply #13 on: April 08, 2013, 02:05:36 AM »
A follow-up question if you will indulge me:
I am able to get the data I need from a selection set but only by calling the lisp function (see below) on each item and processing a result buffer for each one individually one at a time.  I can't help but think it would be faster to get a result buffer for all the items at once.  Unfortunately, I haven't been able to make this work by iterating the entities within a LISP.   Is this even possible and is it reasonable to think this is a faster method? 

One more by-product I'd like to eliminate is the command line advances a blank line each time the lisp I am using is run.  (edit: looks like NOMUTT will do this) Thank you for any help!

Code: [Select]
acDoc.SendStringToExecute("(GETMAPLISTDATA (entget (handent \"" + objHand + "\"))) ", false, false, false);
  where objHand = entity handle

Sorry, not sure what GETMAPLISTDATA is.  Is that a LISP function that you defined  managed code (using the LispFunction attribute) ?

Also, SendStringToExecute() isn't really the best way to solve the problem, but I really don't know very much about the problem to start with, so I can't tell you which of several other ways that are generally more reliable would be best, but here's a better way to call the LISP (entget) function from managed code:

Code - C#: [Select]
  1.     /// <summary>
  2.     ///
  3.     /// Managed wrapper for LISP (entget) function, via
  4.     /// Application.Invoke (acedInvoke()).
  5.     ///
  6.     /// Requires (vl-acad-defun 'entget) to be called in
  7.     /// in every document (e.g., placed in acaddoc.lsp).
  8.     ///
  9.     /// Cannot be used from application context.
  10.     ///
  11.     /// </summary>
  12.  
  13.     static readonly TypedValue[] entgetArgs = new TypedValue[]{
  14.         new TypedValue((int) LispDataType.Text, "entget"),
  15.         new TypedValue((int) LispDataType.Nil)};
  16.  
  17.     public static ResultBuffer EntGet( ObjectId id )
  18.     {
  19.         entgetArgs[1] = new TypedValue(
  20.             (int) LispDataType.ObjectId, id );
  21.         return Application.Invoke( new ResultBuffer( entgetArgs ) );
  22.     }
  23.  
  24.     [CommandMethod( "CALLENTGET" )]
  25.     public static void CallEntGetSample()
  26.     {
  27.         var doc = Application.DocumentManager.MdiActiveDocument;
  28.         var ed = doc.Editor;
  29.         var peo = new PromptEntityOptions( "\nPick an entity: " );
  30.         var per = ed.GetEntity( peo );
  31.         if( per.Status != PromptStatus.OK )
  32.             return;
  33.         ResultBuffer rb = EntGet( per.ObjectId );
  34.         if( rb != null )
  35.         {
  36.             foreach( TypedValue tv in rb )
  37.                 ed.WriteMessage( "\n{0}", rb.ToString() );
  38.         }
  39.     }
  40.  
« Last Edit: April 08, 2013, 02:28:40 AM by TT »

jabowabo

  • Mosquito
  • Posts: 20
Re: TypedValue using DXF Code with multiple values
« Reply #14 on: April 08, 2013, 10:40:21 AM »
This looks like a much better method than what I have currently.  TT, I really appreciate the time you've taken to share your expertise and help me out!

BTW, GETMAPLISTDATA is the LISP function.