PDA

View Full Version : More efficient way of choosing drawing sheet elements



r.howarth
2007-12-13, 01:01 AM
I'm trying to update shared paramaters on some drawing sheets, and at the moment I'm doing it as follows:
- Loop through all elements
- For each element, check that its category to see if it is "Drawing Sheets"
- if so, loop through its parameters to find the sheet number
- check its the one I want
- loop through parameters again to find parameters I want and set them

This takes like 20 seconds each time, obviously from looping htrough every single element on the file. This happens twice in what i'm trying to do, first time is on the load of the command which lists all the drawing sheets, and then when you want to make the changes it delays as well.

Is there anyway to just go straight to drawing sheets?

GuyR
2007-12-13, 02:25 AM
If you're casting every element to check the category that is VERY slow. The most efficient way is:

ElementIterator elemItor = _CommandData.Application.ActiveDocument.Elements;
while(elemItor.MoveNext())
{
object currElement = elemItor.Current;
if (currElement is Autodesk.Revit.Elements.ViewSheet)
{
ViewSheet currentViewSheet = currElement as ViewSheet;
}
}

The other way, which relies on having a printer view set with all drawings sheets correctly updated is to use the activeDocument.ViewSheetsSets property. Which is blazingly quick.

You also don't need to loop parameters to get the sheet number you can use BuiltInParameter.SHEET_NUMBER to get the parameter directly.And for your shared parameters get the shared parameter definitions once and then get each drawing sheets parameter using the definition.

Doing all this should get it down to < 2secs unless your model is huge.

HTH,

Guy

r.howarth
2007-12-13, 04:02 AM
Thanks mate! You're a legend.

all I did was change
//if (null != elem.Category && elem.Category.Name == "Drawing Sheets")
if (elem is Autodesk.Revit.Elements.ViewSheet)
the commented line to the one you suggested and it's dropped the time right down to an acceptable level.

Thanks for your help once again!

GuyR
2007-12-13, 06:18 AM
I just want to clarify my response and explain further where the delays are occurring.It's also not clear in your reply if you eliminated both bottlenecks. The first is checking for only those objects you really need, in this case FamilyInstance. My naming confused the issue so sorry about that. This:

object currElement = elemItor.Current;
if (currElement is Autodesk.Revit.Elements.ViewSheet)

Should more correctly be written as:

object currObject = elemItor.Current;
if (currObject is Autodesk.Revit.Elements.ViewSheet)

because they're still objects. It's the next line where the cast occurs that you get the second hit:
ViewSheet currentViewSheet = currObject as ViewSheet;

So to summarise:

1..Cast to Elements or Symbols only the objects you require, otherwise leave as objects.
2.. Where possible check object category using Type checking rather using the elements category object.

FWIW, Performance and querying the database is high on the API wishlist so hopefully we'll see some improvements in RAC2009 and beyond.

HTH,

Guy

mmason.65315
2007-12-13, 12:57 PM
I'd have a slight suggestion:

ElementFilterIterator efi = commandData.Application.ActiveDocument.get_Elements( typeof(Autodesk.Revit.Elements.ViewSheet) );

while (efi.MoveNext())
{
Autodesk.Revit.Elements.ViewSheet sheet = efi.current as Autodesk.Revit.Elements.ViewSheet;

// do something with "sheet"
}

using this approach, the ElementFilterIterator is guaranteed to return only the elements of ViewSheet type. While it is not currently faster than doing it the other way, Autodesk has said that this approach in future releases will be SIGNIFICANTLY faster than going through one by one.
And, FWIW, I think it's better to specify what you want up front, so that you don't need another IF layer inside your while block.

--------------------------
Only potential down side to this:
In a few cases I changed my code from your way to this way it didn't work. The reason is that if you were looking for all Views, you might be tempted to look for Autodesk.Revit.Elements.View. This works fine in the code where you cast it - including cases where the element is a subclass of View - such as ViewPlan.

So while this works:

View anyview = elementfilter.current as View;

This:
ElementFilterIterator efi = document.get_Elements( typeof(View) );

does not - it will only find elements that are specifically of the View type, not ViewPlan or View3D or any of the other subclasses.

(Sorry for the confusion - but after struggling with that once, I figured it was important to pass on).

Since you're looking for ViewSheet, the code above will work just fine.

Best Regards,
Matt

GuyR
2007-12-13, 06:33 PM
Matt,

Actually I think you summarised why currently I never recommend using ElementFilterIterator ;-) It's no faster and 9 times out of 10 you need more than one Type.

I also think for the beginner it's slightly confusing in that the Object still needs to be cast. Had it used generics and the line became ViewSheet currSheet = efi.Current; that would have been cool. The API beginner will naturally look at the SDK samples. Unfortunately a lot of them do exactly what you shouldn't do which is cast every element and check the category. So I tend to labour the point about using 'is' to filter Types.

While ElementFilterIterator might get faster in future, the single Type selection is a major limitation so they'll hopefully come up with something better ;-) and we've still got 5 months of RAC2008.

HTH,

Guy

r.howarth
2007-12-14, 12:38 AM
I just want to clarify my response and explain further where the delays are occurring.It's also not clear in your reply if you eliminated both bottlenecks. The first is checking for only those objects you really need, in this case FamilyInstance. My naming confused the issue so sorry about that. This:

object currElement = elemItor.Current;
if (currElement is Autodesk.Revit.Elements.ViewSheet)

Should more correctly be written as:

object currObject = elemItor.Current;
if (currObject is Autodesk.Revit.Elements.ViewSheet)

because they're still objects. It's the next line where the cast occurs that you get the second hit:
ViewSheet currentViewSheet = currObject as ViewSheet;

So to summarise:

1..Cast to Elements or Symbols only the objects you require, otherwise leave as objects.
2.. Where possible check object category using Type checking rather using the elements category object.

FWIW, Performance and querying the database is high on the API wishlist so hopefully we'll see some improvements in RAC2009 and beyond.

HTH,

Guy

I'm trying to work out exactly what you are talking about, I'm still a novice at all of this.
Heres the code I was using, I made a test class which just displays a message box when it finds the right element.

Document document = commandData.Application.ActiveDocument;
Element elem;
ElementIterator iter = document.Elements;
// not sure what this is used for?:
Category sheetCategory = document.Settings.Categories.get_Item(BuiltInCategory.OST_DrawingSheets);

while (iter.MoveNext())
{

Object currElement = iter.Current;
if (currElement is Autodesk.Revit.Elements.ViewSheet)
{
elem = iter.Current as Element;

MessageBox.Show(elem.Category.Name.ToString());
}

}

return IExternalCommand.Result.Succeeded;


that is what you are suggesting correct?

now what I had was somethign along the lines of:

while (iter.MoveNext())
{

elem = iter.Current as Element;
if (elem is Autodesk.Revit.Elements.ViewSheet)
{
MessageBox.Show(elem.Category.Name.ToString());
}

}

so casting each as an element instead of the object. From my tests, both seem to take around 5 seconds to find the first drawing sheet... No noticeable differences.

I'm also not sure what the sheetCategory was for in your original suggestion?

GuyR
2007-12-14, 10:00 AM
Sorry if I'm confusing you.The category definition was superfluous and you'll see I edited that particular post to remove it.

See attached project for you to try, just gets all walls and rooms. To use the command select how many times you want to run each solution and then press Run. The looping just does a bit of averaging to make sure the numbers are as relevant as possible. The usual caveats apply when running these sorts of tests. Don't play mp3's etc while running the tests, and don't start Revit from the debugger ;-) When you run the command for the first time, ignore the first result. So typically I set the loop to 1 and run it for the first time. Then set the loop to 10-15 and run again.

It's a quick hack and taken from a simple command I've used in the past for performance testing different options. I've modified it to give you something relevant to what you're doing. I've also commented the code to try and explain some of it. I typically use the conference project from the training files for tests. It has a bit of everything, a reasonable size without taking 5 minutes to load.

The truth is I am splitting hairs not casting to Elements first, as I do in MyBest ;-) Typically MyBest solution is only about 1% faster than the Better solution. And this is dependent on what you're actually wanting to test. Semantically though it makes more sense to me leaving the current Revit object as an object as long as possible. And it's just as fast to write.

HTH,

Guy

r.howarth
2008-01-11, 06:12 AM
Sorry if I'm confusing you.The category definition was superfluous and you'll see I edited that particular post to remove it.

See attached project for you to try, just gets all walls and rooms. To use the command select how many times you want to run each solution and then press Run. The looping just does a bit of averaging to make sure the numbers are as relevant as possible. The usual caveats apply when running these sorts of tests. Don't play mp3's etc while running the tests, and don't start Revit from the debugger ;-) When you run the command for the first time, ignore the first result. So typically I set the loop to 1 and run it for the first time. Then set the loop to 10-15 and run again.

It's a quick hack and taken from a simple command I've used in the past for performance testing different options. I've modified it to give you something relevant to what you're doing. I've also commented the code to try and explain some of it. I typically use the conference project from the training files for tests. It has a bit of everything, a reasonable size without taking 5 minutes to load.

The truth is I am splitting hairs not casting to Elements first, as I do in MyBest ;-) Typically MyBest solution is only about 1% faster than the Better solution. And this is dependent on what you're actually wanting to test. Semantically though it makes more sense to me leaving the current Revit object as an object as long as possible. And it's just as fast to write.

HTH,

Guy

Sorry I have taken so long to reply - I was on holidays, and then returned home sick :(

Thanks alot for putting so much effort into the reply! A neat little benchmark!
I've got my head around it now, in your worst one, checking the category is definately alot slower than casting the currentElement as the type we are looking for. 'Continue' is neat, I'd never heard of that before (I'd used break, but not continue).

Thanks again, it runs great now :D