Retrieving Newly Created Elements

The day before yesterday, I demonstrated the use of the

Mirror method
.
Henrik Bengtsson of

Lindab

immediately came back with a question on this.
Funnily enough, the reason I quickly posted the article the day before yesterday was because someone completely different asked the exact same two questions as Henrik in the exact same sequence.
There seems to be some kind of

morphogenetic resonance

going on among Revit developers.
Anyway, here is Henrik’s question:

Question:
The mirror command works all right according to the description.
I now face another issue that I had not thought of before.
How can I get a reference to the newly created mirrored objects?
Those are actually the objects that I want to continue working with.

Answer:
Because this question was already asked once this morning, I had some time to meditate deeply on how to retrieve the newly created elements generated by the mirroring operation.
My first idea was to grab them by parsing the tail of the journal file, but unfortunately they are not listed there.
My second idea works, however:

  • Ask the document for all its elements before the mirroring operation and remember the total number n.
  • Call the Mirror method, generating a number of new elements.
  • Ask the document for all its elements again, and retrieve the ones whose index exceeds n.

I make use of two helper methods for this:

  • GetElementCount determines the total number of document elements before the mirroring operation.
  • GetElementsAfter returns a list of all document elements whose index exceeds a given number.

Here is the implementation of these two methods:


int GetElementCount( Document doc )
{
  int count = 0;
  ElementIterator it = doc.Elements;
  while( it.MoveNext() )
  {
    ++count;
  }
  return count;
}
 
List<Element> GetElementsAfter( int n, Document doc )
{
  List<Element> a = new List<Element>( n );
  ElementIterator it = doc.Elements;
  int i = 0;
 
  while( it.MoveNext() )
  {
    ++i;
 
    if( n < i )
    {
      a.Add( it.Current as Element );
    }
  }
  return a;
}

Here is the code for the new external command implementing this.
It is a simple extension of the

mirroring command CmdMirror

presented on Wednesday:


Application app = commandData.Application;
Document doc = app.ActiveDocument;
 
ElementSet els = doc.Selection.Elements;
 
Line line = app.Create.NewLine(
  XYZ.Zero, XYZ.BasisX, true );
 
int n = GetElementCount( doc );
 
doc.Mirror( els, line );
 
List<Element> a = GetElementsAfter( n, doc );
 
string s = "The following elements were mirrored:rn";
 
foreach( Element e in a )
{
  s += string.Format( "rn  {0}",
    Util.ElementDescription( e ) );
}
Util.InfoMsg( s );
 
return CmdResult.Succeeded;

Here is
version 1.1.0.41
of the complete Visual Studio solution including the mirroring command CmdMirror and the new command CmdMirrorListAdded.

Disclaimer:
Please note that there is nothing in the Revit API documentation stating that the iterator returned by doc.Elements will traverse the elements in the order they were added to the database.
That happens to be the case today and has been so in the past.
This behaviour may change without notice in the future.


Comments

6 responses to “Retrieving Newly Created Elements”

  1. Henrik Bengtsson Avatar
    Henrik Bengtsson

    Thanks for the answer!
    It works alright, but it felt a bit dangerous and like a time consuming solution (lots of iteration) when i first looked at it.
    But after some investigation regarding alternatives this is probably the best solution available at the moment…
    I made some time analyzis comparing this method with another alternative, and this was faster for those type of model files that will be the case when working with our add-in.
    The alternative method was to use a Shared parameter, set its value and use a parameter-filter to retrieve the elements. It feels 100 % safe in all cases but it wasn’t the fastest unfortunately…
    /Henrik

  2. Dear Henrik,
    I like it a lot that you think it seems a bit dangerous. I fully agree with that. Time consuming also, maybe. I very much like your idea for a safer approach making use of a shared parameter, what a shame that it is slower. How did you test that? Did you perform the following steps or something different?
    1. Define a shared parameter for all element categories that are of interest.
    2. Set it to a predefined value meaning ‘before’ on all elements.
    3. Perform the mirroring operation.
    4. Retrieve all elements with a value differing from ‘before’.
    Thank you very much for benchmarking, that is really valuable information.
    Cheers, Jeremy.

  3. Henrik Bengtsson Avatar
    Henrik Bengtsson

    Ciao!
    I more or less did according to your description… My steps are:
    1. Define the shared parameter
    2. Send in the instance as well as the coordinates for the plane which I want to use for the mirroring operation in the function.
    3. The predefined value for the shared parameter was an empty string, instead of a string value. I would like to store as little data in the model as possible. Perhaps it doesn’t affect the ‘weight’ that much but that was the reason. So I simply set the parameter value for the element that I want to transform just before the mirroring operation.
    3. Perform the mirroring operation
    4. Delete the source object, since my reason for running the mirroring operation is simple transforming.
    5. Retrieve the element with the use of a parameter filter. Since I work with one single element, I would only get one element in that operation.
    6. I reset its parameter value, since I don’t want this to end up in my element list next time I perform the mirroring function.
    7. Return the new instance and continue to work with that.
    So, actually no big differences… ;-)
    For simple time analysis, I logged the time before the operation and the time after the operation. What was important was the size of the model. What is actually happening behind the scenes in the filter functions is hard to tell, but I suppose that both iteration and comparison is done, which in that case will be more time consuming then a simple iteration as you described Jeremy…
    /Henrik

  4. Dear Henrik,
    Thank you very much for the description, it definitely all makes sense and may become a valuable solution in the future if the iterator stops returning the elements in their creation sequence.
    Cheers, Jeremy.

  5. Jeremy,
    I’m away from my API docs right now – but in terms of finding the newest elements- isn’t there a “ReverseIterator” available? It might be faster to count from the back until you find an identifiable “last element” from before?
    -Matt

  6. Dear Matt,
    Thanks for the good idea, but no cigar in sight, I believe.
    Some of the Revit API classes do provide reverse iterators, but the document Elements property that I am using above returns an ElementIterator instance and nothing but.
    One might possibly try to use one of the filtered access methods to the document elements, I just don’t know whether any of those is guaranteed to return all elements and also return them in sequential order.
    Cheers, Jeremy.

Leave a Reply

Discover more from Autodesk Developer Blog

Subscribe now to keep reading and get access to the full archive.

Continue reading