Jump to content
Developer Wiki and Function Reference Links Read more... ×
Sign in to follow this  
PeterT

Document List Handling

Recommended Posts

I am having trouble with a large script I am writing, and the trouble area involves document list handling.

I am trying to process all objects newly created in the script, but these objects occur across multiple layers. I want to exclude processing any objects existing before the script runs.

My thought was to get a handle to LObject at the beginning of the script which would be the last object created before the script run, and work up the object list until I got to LNewObj which would be the last object created during the script execution.

I tried using ForEachObjecrtInList specifying the handle to LObject as the first object in the list, thinking it would work up the object list from there, but this seems to process even the objects created previous to the script running, perhaps because I am starting in the middle of the object list. The documentation for this says to specify a handle the the first item in the list. Does this mean the handle I give it defines the first item in the list, or that I have to start at the first object, FObject?

Then I looked at ForEachObjectInLayer, but I am unclear as to what order the objects are processed using this, and could not figure out how to specify the starting and ending objects, or the subset of newly created objects.

Next I tried setting up nested loops to go up or down through all the layers and work up or down the object list on each layer using NextObj or PrevObj. So I started with LNewObject and tried to work down the list until I got to the LObject. But this seems to fail when I get to a layer that does not contain the LNewObj, and nothing is processed on that layer. There does not seem to be a call for LNewObjInLayer. Also, LObject may, or may not, be on the same layer as LNewObject.

Is there some way I can isolate the list of all new objects created during the script execution which occur on more than one layer?

Is there a separate object list for each layer, or is there only one object list for the whole document?

I could sure use more knowledge in this area. I am stumped. I can't find much documentation or many examples of this process. Can anyone shed some light here?

Share this post


Link to post
Is there some way I can isolate the list of all new objects created during the script execution which occur on more than one layer?

Just store the handles in variables or arrays. You can always ask the handle to the last created object with LNewObj. So when you create an object in the script, store this handle in an array. You can use a dynamic array if you don't know how much objects needs to be stored.

Share this post


Link to post

DWorks,

Thanks for the input, but I am not sure I can do that.

This is just part of a larger script that does a lot of work before it gets to this point. Many of the objects I want to process were created with a mass duplication of selected objects, other objects were created by trimming one or more objects into two or three parts each.

And I am not sure what happens to the handle of an object when trimmed into three parts. Do you get the original handle and two new handles, or is the original handle deleted and you get three new handles, and which of the three parts would be the original handle if it is kept? To complicate it more, multiple objects might be trimmed all at the same time, so if I am trimming three objects into nine objects, how do I get the handles to all these objects?

And if I use LNewObj and PrevObj how do I stop the loop after the newly trimmed objects are found when the last new object before the trim procedure might be one of the objects that was trimmed?

So really, I want to get the handles to these objects after all the other work is done, and it would be very difficult to get their handles along the way.

Also, I only want to select some of these newly created objects, not all of them, so I really need to first access a list of all the new objects, then perform a selection function a on just some of these objects.

Share this post


Link to post

I have not tested this but it seems doable.

You can create a dummy class and assign it to the new objects. Later in the program, use ForEachObject with the class as the criteria. When processing the resulting objects, you can then test further and select only the subset that you want. You can reassign the class for each in the loop or delete the dummy class at the end.

Share this post


Link to post

No real answers I can give, but maybe some suggestions.

Whatever you try I think you should do some testing with a snip of your script. It might be the only way to accurately answer some of your questions. Trim an object and use LNewObj to modify an object....maybe change it's color. You'll soon see what LNew is doing.

I had to do this to use ClipSurface and understand what was the LastNewObj after the clip. It was a long time ago and I can't really remember what the details are, but it was counter-intuitive. Might have been that the LNew was the "clipper" rather than the result of the operation. I think I had to use PrevObj to get to the resultant clipped object.

Miguel's idea sounds promising, but you still have to understand what is the LNew after your operations so that you assign the correct objects to the new class.

For that matter, D's suggestion shouldn't be off the table. There's no reason I know that you can't assign the handles of the new objects to a global array as you process thru the trimming/duplicating.

It'll be much the same as assigning to a class as you go.

Just declare a procedure "loadArray" that stores the handle at the current spot in the array and increments the index after that. Call it anytime you can get a handle to an object that you might want to work on later. After all the clipping, duplicating and loading is done, step thru the array getting handles to newly created objects, test them further and do whatever to those objects.

In both cases you'll need to know how LNew is working in your particular situation.

Or...what about building an array of all the objects that are present before doing anything. Do your manipulations and then select everything that's not in the array? Now you could use ForEachObject with part of the criteria being Sel=True.

BTW I'm not entirely sure what LObject gets. Seems it's affected by stacking order.

Share this post


Link to post

I guess I was hoping there was an easy way to get handles to all the new objects without to that point having to process each object on each layer individually. So far my script has low overhead as everything is done to all objects at once.

Although the objects are all on different layers and all in different classes, with layer options set to show/snap/modify, I can duplicate and move a set of specific objects with one call, deselect all, draw a cutting object, and trim all the objects with one call, and all the resulting objects remain on the appropriate layers, and in the appropriate classes.

At this point I have done the bulk of the work without having to look at each object individually or traverse through any of the layers in the file. Also, to trim the objects, I must use use a DoMenuTextByName call, and there may be a significant number of objects processed, so I definitely do not want to call this more than once.

So this is why I was trying to take the approach of loading the handles at the end of the script rather than along the way. But the Idea of building an array of objects before I do anything is intriguing.

I have never used arrays before in my scripts and should probably look at that. Are there any examples of how I might load objects into an array?

Thanks for the input.

Share this post


Link to post

Take a look at HDuplicate and Split2DObjectByLine. Both accept handles (for example from a for each object routine) and return handles to the resulting objects.

-Josh

Share this post


Link to post

Josh,

Thanks for the idea, but after testing Split2DObjectByLine with ForEachObjectInLayer, there are too many problems to use this. For one thing, it only seems to split polygons, and will not split line or wall objects which make up 75% of my drawing. Even for polygons I get odd results when using CreateDuplicateObject from the resulthandle. The annotated function reference mentions some limitations of this call. I think maybe it should be called Split2DPolyByLine.

Unfortunately, I think I will have to stick with the DMTBN trim call.

Share this post


Link to post

I posted a script here http://techboard.nemetschek.net/ubbthreads/ubbthreads.php?ubb=showflat&Number=113373#Post113373 about half way down. (hope that crazy looking link actually works). It contains a simple function that loads an array with the handles of all selected objects using ForEach, and traverses the array with a While loop operating on the objects therein. The key to that whole exercise is understanding indexes as they apply to arrays and incrementing them accordingly.

In fact, you could basically copy and paste that function and simply change the settings on ForEachObjectInLayer to process all objects on all layers. (After declaring a suitable array variable of course.)

There's lots of good stuff about Doc List Handling and ForEach in there as well, and it's also a great example of how different strategies can be used to the same end.

Too bad about Josh's suggestions. That looked promising.

Edited by ccroft

Share this post


Link to post

If only the objects weren't on more than one layer, then you could put them in a group with BeginGroup and EndGroup. Then get the handle to the group. With this handle you can then search all objects in that group.

But I got the feeling that if we know what you are trying to achief, we can give better help. So please explain exactly what you want to get and what the beginning state is.

Edited by DWorks

Share this post


Link to post

Charles, thanks for the array examples, there is some good info in that thread. A couple of questions though.

Is there a maximum limit to the size of a static array? I may have more than 1000 objects to load.

Does it hurt to set up more storage locations in the array than you actually use? You certainly don't want too few.

Once the array is set up and loaded, how would I go about selecting all objects not in the array? Would I select everything, then traverse the array to deselect those objects?

DWorks, yes, the group thing was my original idea, but the multiple layer issue put a damper on that. I might still be able to do it though, by looping through the layers and grouping on each layer individually, but I think I will try working with arrays first.

Share this post


Link to post

I think Ray says in that post it's 32K, and I tend to believe what he says. Might've changed in the new 64 bit world though.

I'm no computer scientist (obvious...I hope), but I think when you declare a static array you are reserving...whaddaya call em....registers? in memory. So what does 32K registers in a one dimensional array add up to? 32K of Ram? Any scientists out there?

I don't *think* there's much of a downside to setting up an oversize static array. I've done it a lot with no apparent harm. I'll set up a 1,000 place array for what will likely be 100 to 200 objects at most. Actually it was a programmer friend who told me to just go ahead and do it and not worry about it.

However, as you can read in that thread there's very little overhead to declaring a dynamic array. It just says there will be an array and we're going to figure out later how many places are needed. Counting objects with ForEach is the way to go.

I think there's probably a few ways that you could go about acting on objects not in the array, but doing what you said was the first thing that came to mind. If all your object processing is selection based this seems easiest.

Another possible might be to declare a second array NewObjBin. You'd get a handle to each object, test OldObjBin to see it it's there and if not put it in NewObjBin. Sounds like a lot of processing but this stuff is very fast. I have one long and tedious script that has three 2D arrays that get traversed and sorted a few times for each object. I've tested it on maybe 800 objects and there is no lag that I can detect.

If you find a need to speed this kind of thing up you can start doing things like stop traversing the array when you encounter a blank. Then you can take it a step further and if you find a handle that is in OldObjectBin you can delete it and sort the array so the blank goes to the bottom. When you get towards the end of all this comparing you're only traversing a few registers in the array instead of all of them.

I must me crazy cause I kinda enjoy doing this sort of thing. I may just create a drawing with 31,999 objects and count 'em just to see if it takes any time.

That is... if I get any time for such foolishness.

Share this post


Link to post

You know what though. If we keep this thread alive somebody will read it who has a much better idea....

Share this post


Link to post

I have my script generally working at this point, but there are a few areas I still need rewrite for complete functionality. But I am much closer to completion.

As for the array, I was getting "index outside of array limit" errors at 1,000 elements, so I tried 5,000, then 10,000 then 20,000, still getting error. Finally I tried 30,000 and voila, no error. I am testing on only a moderately busy floor plan, so I do not know if 30,000 will be enough for all files. I may have to look into Dynamic Arrays, but I am already at close to 32K with a static array. Maybe someone can clarify the upper limits of these arrays a bit more.

The funny thing is that in VW version 12.5 in the "About VectorWorks" splash it tells you how many objects are in your drawing, and how many on the active layer, but VW 2009 does not show this any more. My test file was converted from a 12.5 file though, and in 12.5 it tells me it has 2,609 objects in file, so I am curious why when loading the file's objects into the array in version 2009 I was getting index over limit errors at an array size of 20,000. I am also slightly skeptical that I have close to 30,000 objects in my file.

Share this post


Link to post

Run a test script that counts objects and messages out the result. Construct it using the same bit of script that's loading your array.

Assuming you're using ForEachInLayer, do you have it set to go inside groups, and containers? Do you really need to do this? A group will count as one object, but if can be made up of many for EG.

A tip for trouble shooting arrays: put a counter in there to count how many times the loop that loads the array executes and message that out after the end of the loop sequence.

Another thought. Did you initialize the index variable? If the array doesn't start loading at 1 you'll have big trouble.

Share this post


Link to post

I am initializing the index, and in the FEOIL I have the traversal options set to 0 (shallow) so that is not it.

But I added Message(index); right after I load the array, and testing on the same file as yesterday, it returns 2,461, which seems more like it. So I reset the size of the array to 2500, and today, I am getting no error message. I do not know what was going on yesterday, but it seems O.K. now, so I have set it to a size of 10,000 which should cover any of our plans and I will lay the array to rest.

Thanks for all the input. Now, to resolve the other remaining issues.

Share this post


Link to post

As for the array, I was getting "index outside of array limit" errors at 1,000 elemen

This is usually a bug in the script that is not easy to recognize if you are using a variable for the index. Any value outside the limits of the array will trigger this error such as negative values, zero, or numbers larger than the upper limit.

I am surprised that not many use the {DEBUG} directive to find errors. If you step over the function that declares the array, the script will stop at the point where the error is and you can then check the value of the index.

A word of caution if you want to use Dynarrays. I have used Dynarray safely if I know beforehand the total size. I do not know if it is my computer or something else but I could not Allocate the Dynarray size more than once without VW crashing. I tested this behavior some time ago so I do not know if this was fixed in current versions of VW.

Share this post


Link to post
As for the array, I was getting "index outside of array limit" errors at 1,000 elemen

A word of caution if you want to use Dynarrays. I have used Dynarray safely if I know beforehand the total size. I do not know if it is my computer or something else but I could not Allocate the Dynarray size more than once without VW crashing. I tested this behavior some time ago so I do not know if this was fixed in current versions of VW.

I never had any issues with Allocating a dynarray more than once, and I do it a lot. I used the versions 12 to 2009 for programming.

Share this post


Link to post

Miguel,

Most of my scripting was in 8 and I'm not sure if we had the debugger back then or not, but in any case I never really learned how to use it very well. My need for new tools since then has been pretty sporadic, and every time I use it I have to re-learn what the controls actually do. It is a powerful tool and has solved some thorny issues.

Call me old-fashioned (or a simpleton), but sometimes message seems easier.

In one script a counter separate from the index value showed an error in loop structure. You can have a situation where a loop executes too many times and where the index isn't equal to loop iteration. I think it was a troublesome sort routine. Maybe that's more of a loop debug than index debug.

So Peter, did you go with selecting all and deselecting everything that's in "OldObjBin"?

Share this post


Link to post

DWorks,

This is an example of reallocation that caused my computer to crash and have avoided since then.

PROCEDURE Test1;

VAR

symTot: INTEGER;

symList: DYNARRAY[] OF HANDLE;

PROCEDURE ProcessSym(symHdl: HANDLE);

BEGIN

symTot:= symTot + 1;

Allocate symList[1..symTot];

symList[symTot]:= symHdl;

END;

BEGIN

symTot:= 0;

ForEachObject(ProcessSym,((S='Symbol-1')));

END;

I could not get past the second iteration of the loop and VW would always crash on Allocate.

Charles,

Before the debugger, I used to do the same thing and write out values. But for checking one value at a time it is faster to use the debugger because you do not have to add the Write statements and remember to delete them after. I only use Write statements where I need to check a sequence of values such as values in a long loop.

Share this post


Link to post

In my experience, reallocation generates a Warning in the Error Output file, but I haven't experienced any crashes.

Unless some of the unexplained, but recurring, crashes that seem to be caused by event-enabled PIOs in ?Compile in EACH invocation? -mode are in addition related to reallocation.

Share this post


Link to post
So Peter, did you go with selecting all and deselecting everything that's in "OldObjBin"?

Charles,

Yes, that is the way I am doing it now, and it works fine. Fortunately, I am only running the script on visible layers, and loaded the array with objects only on these layers, so with Layer Options set to show/snap/modify, a simple SelectAll gets all the objects I am working with.

Share this post


Link to post
DWorks,

This is an example of reallocation that caused my computer to crash and have avoided since then.

PROCEDURE Test1;

VAR

symTot: INTEGER;

symList: DYNARRAY[] OF HANDLE;

PROCEDURE ProcessSym(symHdl: HANDLE);

BEGIN

symTot:= symTot + 1;

Allocate symList[1..symTot];

symList[symTot]:= symHdl;

END;

BEGIN

symTot:= 0;

ForEachObject(ProcessSym,((S='Symbol-1')));

END;

I could not get past the second iteration of the loop and VW would always crash on Allocate.

I ran the script, but VW didn't crash. But ProcessSym will only be activated once because symbols must have a unique name.

Share this post


Link to post

Hi Peter,

???I'd have joined sooner, but I've been out and about this last week; only now catching up.

Is there some way I can isolate the list of all new objects created during the script execution which occur on more than one layer?

???I think your first method should work. FEOIList will start at the handle passed to it and walk to the end of that list, so yes, you can start in the middle of a list.

Is there a separate object list for each layer, or is there only one object list for the whole document?

???There is a separate object list for each layer. This might be the biggest stumbling block in your first attempt. You will have to get a handle to the last object on each layer you are working on and traverse each list from there to the end. You won't need to use LNewObj as each list on each layer will terminate with NIL when you get to the end. It wouldn't work anyway since LNewObj covers the whole document (when it works) and isn't unique for each layer.

???You might consider building a DynArray of "last object" handles for your visible layers and save the last object of each layer in each element. Step through your visible layers and store LActLayer in your array before you create anything new, then step through the array and process all new objects after the stored "last object" handles.

???One other thing, LObject does not get the handle to the last object created or placed, but to last object that would be drawn when all layers and objects are visible. That is, the last (top) object on the last (top) layer. If that layer or object is not visible, LObject will still return the same handle. Think of it as the last object in the stacking order of all layers and placed objects. Since Sheet Layers are above Design Layers, in layer stacking order, if you have sheet layers with objects on them LObject will be coming from the top sheet layer. Very tricky is this document handling stuff.

HTH,

Raymond

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

 

7150 Riverwood Drive, Columbia, Maryland 21046, USA   |   Contact Us:   410-290-5114

 

© 2018 Vectorworks, Inc. All Rights Reserved. Vectorworks, Inc. is part of the Nemetschek Group.

×