Jump to content

vlax-make-safearray Problem


Bill Tillman

Recommended Posts

I'm trying out a new method of using vlax-safearray to assign some values which I want to eventually loop through.

 

First off, the project I'm working on will contain a number of reinforcing members, the quantity of which will all be depending on other input from the users. So my first problem came in trying to assign the number of elements in the array. This did not work for me:

 

(setq riboc_array (vlax-make-safearray vlax-vbInteger '(0 . (- numref 1))))

 

I was hoping to use an equation as the number of elements will always be unknown until the program starts running.

 

And as for filling the array, I wanted to also use a similar method in that the integers that are calculated are not known until they program runs.

 

(repeat (fix _y1)
 (vlax-safearray-fill riboc_array (+ 6.875 _x1 (* count _z1)))
 (vlax-safearray-fill riboc_array (- (distance tpt1 tpt2) 6.875 _x1 (* count _z1)))
 (setq count (1+ count))          
 ); end repeat

 

OK I found a resolution to the first part using cons. The second part is still stumping me?

Link to comment
Share on other sites

I see your comment about finding Cons as part of the solution:

 

(setq riboc_array (vlax-make-safearray vlax-vbInteger (cons 0 (1- numref))))

... However without more information, I am unable to understand what the end goal is here... I don't even know if one can fill a vlax-vbInteger Typed SafeArray with Real numbers, can you? :unsure:

 

Further, methinks an Apply or Mapcar statement may be preferable over Repeat.

Link to comment
Share on other sites

Me thinks me brain must have been fried from another long 10 hours of coding when I hit this challenge. Anyway, I tried changing the type to vbDouble but still hit a problem with this line of code.

 

(vlax-safearray-fill riboc_array (+ 6.875 (/ ribthk 2.)))

 

What I want to accomplish is to have the array filled with numbers which will represent the distance that the centerline of each reinforcing rib will be from a datum point. Each one will not only be a different number but there will be no regular pattern in them other than they will be symetrical about the center of this assembly. Which means I really only have to do 1/2 of them, the others I can either mirror or just change 0 to pi in my code. I will then loop through the array and draw the ribs which consist of three lines, the edges and the hidden web of the channel shape. I have this working in a longer, more manual method, but I wanted to try something like a loop because I have to repeat a similar insertion of blocks in the frontal view of the assembly. And the frontal view centers will be identical to the plan view centers.

Link to comment
Share on other sites

Once again, I prove what too many hours coding in different languages can do to the human mind after 10 hour days. I got it working by using vlax-safearray-put-element. It handles all the formulas and equations well.

 

Now to just figure out how to use mapchar and or lambda to loop through this array. I'm sure it will come but not before a good night's sleep with a little Jack Daniels to ease the mind.

Link to comment
Share on other sites

Glad you got it sorted, Bill... As for the supplementary methodology, try setting up a couple of bench test worthy functions... It may be better performing to simply Repeat a sub-function call on your List(s), otherwise, if going the Lambda route, consider compiling with the use of the Function function for efficiency.

 

Lee, and Irne have done several tests here and at other forums to demonstrate this performance advantage. :thumbsup:

 

Get some (much needed) rest, and then knock this little project out, my friend. :beer:

Link to comment
Share on other sites

Now to just figure out how to use mapchar and or lambda to loop through this array. I'm sure it will come but not before a good night's sleep with a little Jack Daniels to ease the mind.
Nope those don't work on arrays. You'll need an index and then use vlax-safearray-get-element to retrieve each value in turn, unfortunately AutoLisp/VLisp doesn't have built-in functions which work on Arrays the way mapcar / foreach does on lists.

 

BTW, what's the reasoning behind using a SafeArray? I would only use them in case of one of 2 reasons:

 

  1. You need random access (read/write) to items within the "list", so an indexed array becomes more efficient than a linked list.
  2. You need to use a safearray because you need to pass / recieve values from an ActiveX object's properties / methods.

In all other cases lisp is much more efficient when using lists. Especially if you simply want to loop through the list from beginning to end.

 

 

That said, a possible implementation for a mapcar for safearrays (untested):

(defun safearray-mapcar  (func arrays / lbound ubound idx count result)
 (setq lbound (mapcar '(lambda (array) (vlax-safearray-get-l-bound array 1)) arrays)
       ubound (mapcar '(lambda (array) (vlax-safearray-get-u-bound array 1)) arrays)
       count  (apply 'min (mapcar '- ubound lbound))
       idx    -1
       result (vlax-make-safearray vlax-vbVariant (cons 0 (1- count))))
 (while (< (setq idx (1+ idx)) count)
   (vlax-safearray-put-element
     result
     idx
     (apply 'func
            (mapcar '(lambda (array lb) (vlax-safearray-get-element array (- idx lb)))
                    arrays
                    lbound))))
 result)

See how complicated it becomes to use safearrays in lisp? That's why I generally stay away from them - only use them when I can't do something else.

Link to comment
Share on other sites

BTW, what's the reasoning behind using a SafeArray?

 

Basically the program automates the drawing of an assembly with plan, side and front orthographic views. In the plan view there are varying numbers of reinforcing ribs spaced at various on center spacings. In this particular model of assembly the ribs not only are spaced based on the size but certain features which the user may select cause these spacings to run all over the map. But the goal is to make them as uniformly space with 6" oc being the max and 4.5" being the min. There are some very deeply nested if,or,ands which I have constructed to place these in the drawing. And since I have to repeat this in the front view as well, I though making an array with the dimension of each rib from a datum point would work best. I will now loop through this array and use each element in a routine which will draw either the plan view or the front view of these ribs.

 

I've been programming a lot lately in VB.NET and using arrays in that language is really a simple process. And it works great for keep items of similar nature grouped together and for accessing those items.

 

Now as for why I chose vlax-safearray, the search I did for "AutoCAD LISP array" brought up a neat page on AfraLISP where the method was outlined. I gave it a shot and it appears to be what I need. I have the array building now with the correct number of elements. This quantity of elements is selected on the fly each time because again, the quantity is different on each drawing.

 

Any advice or pointers would be appreciated. I'm not married to this function...not yet.

Link to comment
Share on other sites

My advise: I'm not too sure if your explanation warrants an array. Perhaps I'm missing something, but is there any time when you don't "loop through the entire array" but only extract items like numbers: 567, 843 and 1034 discarding / ignoring / skipping all others? If the answer is "Yes", then IMO you've got reason to use the array - otherwise you're making your lisp-life difficult for very little benefit (if any), rather use a list.

 

I agree with you that VB's arrays are much easier to use than AutoLisp's arrays, IMO they're not yet as easy as lisp's lists though. But even in VB.Net/C# I might be inclined to use collections instead of a bare-metal arrays - they tend to be safer and work with threads more cleanly, if not faster as well. It's when you're getting into special scenarios in C++/C where a bare-metal array truly comes to the fore as efficient and highly optimized.

Link to comment
Share on other sites

Even if you are planning to extract items from various indexes in the array, this can be easily accomplished using the AutoLISP nth function. In my opinion, the only situation in which using a safearray may be beneficial is if you are looking to substitute items at specific indexes in the array, since, in AutoLISP this would require iterating over the linked list until the index is reached (subst couldn't be used since duplicate items would also be substituted).

 

To provide a quick example:

 

(defun safearrayshuffle ( l / a n r x )
   (setq n (length l)
         a (vlax-make-safearray vlax-vbvariant (cons 1 n))
   )
   (vlax-safearray-fill a (mapcar 'vlax-make-variant l))
   (repeat n
       (setq r (LM:randrange 1 n)
             x (vlax-safearray-get-element a r)
       )
       (vlax-safearray-put-element a r (vlax-safearray-get-element a n))
       (vlax-safearray-put-element a n x)
       (setq n (1- n))
   )
   (mapcar 'vlax-variant-value (vlax-safearray->list a))
)

;; Rand  -  Lee Mac
;; PRNG implementing a linear congruential generator with
;; parameters derived from the book 'Numerical Recipes'

(defun LM:rand ( / a c m )
   (setq m   4294967296.0
         a   1664525.0
         c   1013904223.0
         $xn (rem (+ c (* a (cond ($xn) ((getvar 'date))))) m)
   )
   (/ $xn m)
)

;; Random in Range  -  Lee Mac
;; Returns a pseudo-random integral number in a given range (inclusive)

(defun LM:randrange ( a b )
   (fix (+ a (* (LM:rand) (- b a -1))))
)

Link to comment
Share on other sites

In this example, the array is sized based on the number of elements which is an unknown quantity. A calculation done inside of an Excel spread sheets sets the quantity and this is transferred to the LISP program. Thus I use all of the elements whenever I access the array.

 

I agree that a list might be easier but for the moment at least, this method is working pretty good. In fact, it is working so well, I broke it out into it's own file and load in on demand from the other automated programs. I am going to have a go at it using lists next. This was another interesting learning event for me in LISP programming.

Link to comment
Share on other sites

In this example, the array is sized based on the number of elements which is an unknown quantity.
That is actually a reason NOT to use an array and to rather use a linked list. Growing / shrinking arrays is a very wasteful operation, whereas growing / shrinking lists is next to simply assigning to a variable.
Link to comment
Share on other sites

You're absolutely right about that. I was toying around with a list method and got that working rather quickly.

 

F.Y.I. this method of using an array was working but when the quantity was an odd number the code I wrote was setting the boundary ok, but the nature of the code was that it would try to assign a value out of the bounds. My fix for that was just to allow the bounds of the array to be a little larger than needed, usually only by one element. And since this program was designed to run low and fast, and once it was completed it was done, the elements of the array would never need to be changed. Still I agree with everyone that the list method is a bit cleaner and easier to work with. In fact I used Find/Replace to change the code and switching from one the other took only a minute or two. I still have this block of code in it's own file and it gets called on demand and only if it's needed.

 

Learning something new each day keeps the mind young.

Link to comment
Share on other sites

My fix for that was just to allow the bounds of the array to be a little larger than needed, usually only by one element.
That's the usual fix for this scenario. All structures like collections use either linked lists, or such "growable" arrays as their base structure. If they use arrays they tend to keep the array slightly (or sometimes a lot) larger than the actual needed size. The most usual method is to grow the array to double its current size when its bounds is reached - this is so the extremely inefficient recreation of the array is not done many times over.

 

Note you need a separate variable to keep track of how many items are in fact used, since the bounds only shows "available-space" not "used-space". Some designs use one of the slots of the array to keep track of such. E.g. a Pascal String is an array of 256 bytes, the 1st of which contains the number of used characters. Another alternative is doing something similar to a C-String, where the last used slot contains a null value to indicate a "stop" - but this only works is null is not a valid normal value.

 

E.g. say the array bounds is (0 . 7), and you've used all 8 items from that. When you add another, you make a new array of (0 . 15), then copy each item from the old array into the new one, then add the new item. Then discard the old array. This is literally what happens with such "growable" arrays. Some implementations "might" be able to grow the array if the physical ram "in-front" of the array is not used by something else - but such happens very rarely.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...