This was a deep dive down a rabbit hole for sure but I like this tutorial because you are figuring it out and explaining code as you go. I have been watching 30 minutes at a time before work. Thanks for this
Very true! It's really just me coding opposed to a tutorial. I didn't have enough time to really rehearse or plan beyond the basic idea of the videos - not ideal but I think better than no new videos at all. I tried something completely new with the grid numbering system (I previously just used hard-coded lists) and luckily it worked out well. The attribute and dynamic block property features are what I really wanted to show people. Once you get a nice little sub function there's so much you can do. My goal is to create tutorials (or just videos) where the end result is something a person can actually use. I'm learning C# and .NET right now. TONS of great tutorials, but the code they create isn't something you can really use and play around with. It's hard to expand further from the lessons if you have something that's a dead end.
Hi, let me point that I've gone through all the 5 lessons. What a great tool, lots of learning, it saves us a lot of time when placing the grid bubbles, also gives us the possibility to choose between various options. So, thank you very much for this! Two issue though: 1st, in the command window I cannot change the alignment, it gives me center (no matter what I choose) and 2nd (the old one with the lowerCaseLetter) with not being able two jump from "z" to "a". Thanks again! Can wait for future VL learning lessons.
1st issue - could be something wrong with the values entered in the Lookup table Top (0/0.125) Right (0.125/0) Bottom (0/-0.125) and Left (-0.125/0)??? I've no more ideas! :-)
I fixed it! Inside the getAttTextValue defun there was last flag1 was written LowerCaseLetter istead (wrong) of lowerCaseLetter (correct). Not it works just fine (except the 1st issue)
@@CoolsimexRO My Grid Bubble block was actually missing the "Center" in the Lookup Table, do you think it could be that? I'm going to post the code either here (if I can post something that long) or I'll post it on a website somewhere. Looks like it worked... just check my reply below. The code also works. Added a few lines and a few notes.
'(lambda (x) (cond ( (< x 48) (setq flag1 "badValue") ; Bad Value Range ) ( (and (> x 47) (< x 58)) ; ASCII Character Codes for numbers 1 to 9. (setq flag1 "numeric") ) ( (and (> x 57) (< x 65)) ; Bad Value Range (setq flag1 "badValue") ) ( (and (> x 64) (< x 91)) ; ASCII Codes for Capital Letters (setq flag1 "capitalLetter") (if (> (strlen userInput) 1) (setq flag1 "badValue")) ) ( (and (> x 90) (< x 97)) ; Bad Value Range (setq flag1 "badValue") ) ( (and (> x 96) (< x 123)) (setq flag1 "lowerCaseLetter") ; ASCII Codes for Lower Case Letters (if (> (strlen userInput) 1) (setq flag1 "badValue")) ) ( (> x 122) (setq flag1 "badValue") ; Bad Value Range ) ) ; end cond
); end lambda (vl-string->list userInput)
);end mapcar (if (/= flag1 "badValue") (setq flag2 T) (prompt " Invalid Input, please enter a valid value.")) (cond ( (= flag1 "numeric") (setq kCs-gVar-GridBubbleTextValueType "numeric") ) ( (= flag1 "lowerCaseLetter") (setq kCs-gVar-GridBubbleTextValueType "lowerCase") ) ( (= flag1 "capitalLetter") (setq kCs-gVar-GridBubbleTextValueType "upperCase") ) ( (and (/= flag1 "numeric") (= flag1 "lowerCaseLetter") (= flag1 "capitalLetter")) ; **** ADDED THIS CASE TO PREVENT ERROR MESSAGE PRIOR TO TEXT BEING SET (setq kCs-gVar-GridBubbleTextValueType "none") )
) ; end cond ) ; end while (setq kCs-gVar-GridBubbleText userInput) (princ) ) ; end defun getAttTextValue______________________________________________________________________________ (defun getAlignment () (if (= nil kCs-gVar-GridBubbleAlignment) (setq kCs-gVar-GridBubbleAlignment "Center")) ;*** changed both here and below (initget "Center Top Right Bottom Left") (setq kCs-gVar-GridBubbleAlignment (getkword (strcat " Enter value for alignment [Center/Top/Right/Bottom/Left] : "))) ) ; end defun getAlignment __________________________________________________________________________________________________________ (defun setAlignment ( _obj ) (if (or (= kCs-gVar-GridBubbleAlignment "Center") (= kCs-gVar-GridBubbleAlignment "Top") (= kCs-gVar-GridBubbleAlignment "Right") (= kCs-gVar-GridBubbleAlignment "Bottom") (= kCs-gVar-GridBubbleAlignment "Left")) (kCs-SetDynamicBlockProperty _obj "Alignment" kCs-gVar-GridBubbleAlignment) ); end if ) ; end defun setAlignment _________________________________________________________________________________________________________ (defun gridBubbleTextIncrement () (if (= kCs-gVar-GridBubbleTextValueType "numeric") (setq kCs-gVar-GridBubbleText (itoa (1+ (atoi kCs-gVar-GridBubbleText)))) ) ; end if (if (= kCs-gVar-GridBubbleTextValueType "upperCase") (cond ( (= kCs-gVar-GridBubbleText "Z") (setq kCs-gVar-GridBubbleText "A") ) ( (/= kCs-gVar-GridBubbleText "Z") ; ascii codes = 65 to 90 (vl-string-elt "A" 0) (setq kCs-gVar-GridBubbleText (chr (1+ (vl-string-elt kCs-gVar-GridBubbleText 0)))) ) ); end cond ) ; end if for upperCase (if (= kCs-gVar-GridBubbleTextValueType "lowerCase") (cond ( (= kCs-gVar-GridBubbleText "z") (setq kCs-gVar-GridBubbleText "a") ) ( (/= kCs-gVar-GridBubbleText "z") ; ascii codes = 65 to 90 (vl-string-elt "A" 0) (setq kCs-gVar-GridBubbleText (chr (1+ (vl-string-elt kCs-gVar-GridBubbleText 0)))) ) ); end cond ) ; end if for lowerCase ) ; end defun ______________________________________________________________________________________________________ (defun kCs-SetDynamicBlockProperty (obj propertyName newValue) (mapcar '(lambda (item) ; VL-SOME is better in most cases. For just a few properties it doesn't really matter. (if (= (vla-get-propertyName item) propertyName) (vla-put-value item (vlax-make-variant newValue (vlax-variant-type (vla-get-value item)))) ); end if ) ; end lambda
(vlax-safearray->list (vlax-variant-value (vla-GetDynamicBlockProperties obj))) ; mapcar's list argument. There is a shortcut using vlax-invoke, ; but according to LISP documentation, that method is considered ; legacy. This longer way is 'newer'? Apparently?
) ; end mapcar ) ; end defun __________________________________________________________________________________________________________________________
@44:00 - The function I was looking for is "vl-some". It stops iterating as soon as the condition is met (but finishes that iteration). This frees up the code sooner.
Very good video, I watched all the lessons and I was able to reproduce the code and it works perfectly, I was also able to apply the code to different dynamic blocks and properties, my question is how would I change the code if I want the block to be partially displayed between the initial point insertion point and end point. (dynamic block insertion code only) ??
Thanks so much for your feedback! I think I know what you mean but I might be wrong. If that's the case just let me know and I'll just have another go at explaining ;-) So let's say your parameter just stretches a block. You want to pick two points (similar to drawing a line) but you only want the block to go half-way. You use (setq point1 (getpoint " Get first point: ")), then (setq point2 (getpoint point1 "Get second point: ")). You can establish the angle between the two points using (angle point1 point2). And (distance point1 point2). You can use "polar" which uses the arguments point, angle, distance with the distance being (* 0.5 (distance point1 point2)). When it comes time to apply the parameter to your block property, that is also half the distance as well. If you wanted the block to start halfway, you would use polar to set that point as your insert point for the block instead. The rotation for the block would simply be determined by "angle", possible plus a factor depending on the original angle of the block definition. That will probably entail converting to radians. Since the angle is in radians so if you go the "(Command "_insert"..." route you need to convert to degrees. The "vla-insert-block" works in radians if I'm not mistaken, so it's actually easier once you get used to radians.
great news it works perfectly now .........i re-watched the video and discovered some parenthesis miss placed and a variable name mistake................thank you so much for fast responding........ I'm looking forward for the next big project series
Great to hear! You're acquiring quite advanced code. When I first learnt these things it sometimes took weeks to debug all my code. You're off to a great start. And now that you have those sub-functions working you can re-purpose them for all sorts of things: You can manipulate almost any dynamic block property.
@@randomCADstuff how about a title block project its really useful for the average user and got a lot of the sweet attribute manipulation code that i aspire to learn please consider this one 😁
The title block video would be a great idea... just a matter of time. I am working on the code I mentioned in a reply to another comment. The concept works... but it's complicated. It would be exceedingly hard to convey what I've done in a short amount of time. This video series took me almost an entire weekend. A title block related video would take anywhere from 4-10 times as long... but would be a good long-term project to chip away at I guess. Or... once the project gets to a certain point, just provide it as a FAS file ready to use, and the video would just be directions opposed to having people code it themselves. All the above is very dependent on free time however. I could also make a much shorter video showing how I actually create and manage title blocks. I actually make things extremely simple when it comes to title blocks: Any text that is likely to change from sheet to sheet not connected to the title block itself. That way users can just double-click any text to edit it (feels easier for me opposed to going into attributes, especially multi-line attributes). My title blocks "block" only contains linework and static labels. I also just 'manually' copy sheets, but I'm working on LISP to automate that part (as well as other stuff). Until recently I've never really had to manage large numbers of sheets, so have gotten away with keeping things simple.
@@randomCADstuff well i hope to see another series regardless ,having a pro show you the ropes is my kind of way to learn programming and a project idea is just a way to keep me motivated to learn ...........happy holidays to you and im practicing while waiting for a new project hopfully soon 😊
I'm not 100% sure if you are doing your own program, or referring to the code's behavior. In case it's the latter, I will mention that I'm going to release a better version of this (the code not a tutorial) soon. The new version would allow you to enter "A-1" for example, and it will increment "A-2", "A-3" and so on...
I'm not 100% sure what you mean so you'll have to be more specific. There's a few changes to the code just for better performance and compatibility with the template (automatic layer selection for example). @@JakksonSloann99
To be honest I'm not quite good enough. Also, the wage at my day job exceeds what I could ever earn LISP programming. I'd probably have to be at the level of "one of the best in the industry" to really have a go at it. My skills will come in useful making my own venture competitive and successful, so everything I've learnt was well worth the effort (in my case!). Learning LISP allows you to target your own specific needs and make your tools work exactly as you want them; sometimes that's impossible to achieve even with a really good programmer (the back-and-forth gets expensive). If you happen to be seeking someone, a good place to start is www.theswamp.org/ . There are people there but I don't know their rates. If you don't find that helpful come back here and ask me again, I might be able to dig something up for you ;-)
This was a deep dive down a rabbit hole for sure but I like this tutorial because you are figuring it out and explaining code as you go. I have been watching 30 minutes at a time before work.
Thanks for this
Very true! It's really just me coding opposed to a tutorial. I didn't have enough time to really rehearse or plan beyond the basic idea of the videos - not ideal but I think better than no new videos at all. I tried something completely new with the grid numbering system (I previously just used hard-coded lists) and luckily it worked out well. The attribute and dynamic block property features are what I really wanted to show people. Once you get a nice little sub function there's so much you can do.
My goal is to create tutorials (or just videos) where the end result is something a person can actually use. I'm learning C# and .NET right now. TONS of great tutorials, but the code they create isn't something you can really use and play around with. It's hard to expand further from the lessons if you have something that's a dead end.
Thank you so much❤❤
Hi, let me point that I've gone through all the 5 lessons. What a great tool, lots of learning, it saves us a lot of time when placing the grid bubbles, also gives us the possibility to choose between various options. So, thank you very much for this! Two issue though: 1st, in the command window I cannot change the alignment, it gives me center (no matter what I choose) and 2nd (the old one with the lowerCaseLetter) with not being able two jump from "z" to "a". Thanks again! Can wait for future VL learning lessons.
1st issue - could be something wrong with the values entered in the Lookup table Top (0/0.125) Right (0.125/0) Bottom (0/-0.125) and Left (-0.125/0)??? I've no more ideas! :-)
I fixed it! Inside the getAttTextValue defun there was last flag1 was written LowerCaseLetter istead (wrong) of lowerCaseLetter (correct). Not it works just fine (except the 1st issue)
@@CoolsimexRO My Grid Bubble block was actually missing the "Center" in the Lookup Table, do you think it could be that? I'm going to post the code either here (if I can post something that long) or I'll post it on a website somewhere.
Looks like it worked... just check my reply below. The code also works. Added a few lines and a few notes.
@@CoolsimexRO (defun C:GB ( / pt1 newBlockInsert pointflag )
(setq pointflag nil)
(if (= nil kCs-gVar-GridBubbleTextValueType) (setq kCs-gVar-GridBubbleTextValueType "numeric"))
(if (= nil kCs-gVar-GridBubbleAlignment) (setq kCs-gVar-GridBubbleAlignment "Center"))
(if (= nil kCs-gVar-GridBubbleText) (setq kCs-gVar-GridBubbleText "NONE"))
(while (= pointflag nil)
(initget 128 "Text Alignment" )
(setq pt1 (getpoint (strcat "
Select Insert Point or choose Text or alignment [Text/Alignment]: " )))
(if (/= (type pt1) 'LIST)
(cond
(
(= pt1 "Text")
(getAttTextValue)
)
(
(= pt1 "Alignment")
(getAlignment)
)
(
(and (/= pt1 "Alignment") (/= pt1 "Text"))
(prompt "
Invalid input")
)
) ; end cond
) ; end if
(if (= (type pt1) 'LIST) (setq pointFlag T))
) ; end while
(setq newBlockInsert (vla-InsertBlock (vla-get-modelSpace (vla-get-activeDocument (vlax-get-acad-object))) (vlax-3d-point pt1) "#-ANNO-GRID BUBBLE" 1.0 1.0 1.0 0.0 ))
(prompt "
DEBUG1
")
(putAttributeValue newBlockInsert kCs-gVar-GridBubbleText)
(prompt "
DEBUG2
")
(setAlignment newBlockInsert)
(prompt "
DEBUG3
")
(gridBubbleTextIncrement)
(princ)
) ; end main defun ===========================================================================================================================
(defun putAttributeValue ( obj val / ) ; obj = VLA-Object, val = string
(vla-put-textString (vlax-safearray-get-element (vlax-variant-value (vla-getattributes obj)) 0) val)
) ; end defun ________________________________________________________________________________________
(defun getAttTextValue ( / flag1 flag2 userInput )
(if (= nil kCs-gVar-GridBubbleText) (setq kCs-gVar-GridBubbleText "NONE"))
(if (= nil kCs-gVar-GridBubbleTextValueType) (setq kCs-gVar-GridBubbleTextValueType "numeric"))
(setq flag1 nil)
(setq flag2 nil)
(while (and (= flag2 nil) (or (= flag1 "badValue") (= flag1 nil)))
(setq userInput (getstring (strcat "
Specify text String : " )))
(mapcar
'(lambda (x)
(cond
(
(< x 48)
(setq flag1 "badValue") ; Bad Value Range
)
(
(and (> x 47) (< x 58)) ; ASCII Character Codes for numbers 1 to 9.
(setq flag1 "numeric")
)
(
(and (> x 57) (< x 65)) ; Bad Value Range
(setq flag1 "badValue")
)
(
(and (> x 64) (< x 91)) ; ASCII Codes for Capital Letters
(setq flag1 "capitalLetter")
(if (> (strlen userInput) 1) (setq flag1 "badValue"))
)
(
(and (> x 90) (< x 97)) ; Bad Value Range
(setq flag1 "badValue")
)
(
(and (> x 96) (< x 123))
(setq flag1 "lowerCaseLetter") ; ASCII Codes for Lower Case Letters
(if (> (strlen userInput) 1) (setq flag1 "badValue"))
)
(
(> x 122)
(setq flag1 "badValue") ; Bad Value Range
)
) ; end cond
); end lambda
(vl-string->list userInput)
);end mapcar
(if (/= flag1 "badValue") (setq flag2 T) (prompt "
Invalid Input, please enter a valid value."))
(cond
(
(= flag1 "numeric")
(setq kCs-gVar-GridBubbleTextValueType "numeric")
)
(
(= flag1 "lowerCaseLetter")
(setq kCs-gVar-GridBubbleTextValueType "lowerCase")
)
(
(= flag1 "capitalLetter")
(setq kCs-gVar-GridBubbleTextValueType "upperCase")
)
(
(and (/= flag1 "numeric") (= flag1 "lowerCaseLetter") (= flag1 "capitalLetter")) ; **** ADDED THIS CASE TO PREVENT ERROR MESSAGE PRIOR TO TEXT BEING SET
(setq kCs-gVar-GridBubbleTextValueType "none")
)
) ; end cond
) ; end while
(setq kCs-gVar-GridBubbleText userInput)
(princ)
) ; end defun getAttTextValue______________________________________________________________________________
(defun getAlignment ()
(if (= nil kCs-gVar-GridBubbleAlignment) (setq kCs-gVar-GridBubbleAlignment "Center")) ;*** changed both here and below
(initget "Center Top Right Bottom Left")
(setq kCs-gVar-GridBubbleAlignment (getkword (strcat "
Enter value for alignment [Center/Top/Right/Bottom/Left] : ")))
) ; end defun getAlignment __________________________________________________________________________________________________________
(defun setAlignment ( _obj )
(if (or (= kCs-gVar-GridBubbleAlignment "Center") (= kCs-gVar-GridBubbleAlignment "Top") (= kCs-gVar-GridBubbleAlignment "Right") (= kCs-gVar-GridBubbleAlignment "Bottom") (= kCs-gVar-GridBubbleAlignment "Left"))
(kCs-SetDynamicBlockProperty _obj "Alignment" kCs-gVar-GridBubbleAlignment)
); end if
) ; end defun setAlignment _________________________________________________________________________________________________________
(defun gridBubbleTextIncrement ()
(if (= kCs-gVar-GridBubbleTextValueType "numeric")
(setq kCs-gVar-GridBubbleText (itoa (1+ (atoi kCs-gVar-GridBubbleText))))
) ; end if
(if (= kCs-gVar-GridBubbleTextValueType "upperCase")
(cond
(
(= kCs-gVar-GridBubbleText "Z")
(setq kCs-gVar-GridBubbleText "A")
)
(
(/= kCs-gVar-GridBubbleText "Z") ; ascii codes = 65 to 90 (vl-string-elt "A" 0)
(setq kCs-gVar-GridBubbleText (chr (1+ (vl-string-elt kCs-gVar-GridBubbleText 0))))
)
); end cond
) ; end if for upperCase
(if (= kCs-gVar-GridBubbleTextValueType "lowerCase")
(cond
(
(= kCs-gVar-GridBubbleText "z")
(setq kCs-gVar-GridBubbleText "a")
)
(
(/= kCs-gVar-GridBubbleText "z") ; ascii codes = 65 to 90 (vl-string-elt "A" 0)
(setq kCs-gVar-GridBubbleText (chr (1+ (vl-string-elt kCs-gVar-GridBubbleText 0))))
)
); end cond
) ; end if for lowerCase
) ; end defun ______________________________________________________________________________________________________
(defun kCs-SetDynamicBlockProperty (obj propertyName newValue)
(mapcar '(lambda (item) ; VL-SOME is better in most cases. For just a few properties it doesn't really matter.
(if (= (vla-get-propertyName item) propertyName)
(vla-put-value item (vlax-make-variant newValue (vlax-variant-type (vla-get-value item))))
); end if
) ; end lambda
(vlax-safearray->list (vlax-variant-value (vla-GetDynamicBlockProperties obj))) ; mapcar's list argument. There is a shortcut using vlax-invoke,
; but according to LISP documentation, that method is considered
; legacy. This longer way is 'newer'? Apparently?
) ; end mapcar
) ; end defun __________________________________________________________________________________________________________________________
@@randomCADstuff Hi, in the first video (AutoLISP & Dynamic Blocks - Part 1) at 14:30 you are inserting the Center in the Center in the Lookup Table.
@44:00 - The function I was looking for is "vl-some". It stops iterating as soon as the condition is met (but finishes that iteration). This frees up the code sooner.
Very good video, I watched all the lessons and I was able to reproduce the code and it works perfectly, I was also able to apply the code to different dynamic blocks and properties, my question is how would I change the code if I want the block to be partially displayed between the initial point insertion point and end point.
(dynamic block insertion code only)
??
Thanks so much for your feedback! I think I know what you mean but I might be wrong. If that's the case just let me know and I'll just have another go at explaining ;-)
So let's say your parameter just stretches a block. You want to pick two points (similar to drawing a line) but you only want the block to go half-way. You use (setq point1 (getpoint "
Get first point: ")), then (setq point2 (getpoint point1 "Get second point: ")).
You can establish the angle between the two points using (angle point1 point2). And (distance point1 point2). You can use "polar" which uses the arguments point, angle, distance with the distance being (* 0.5 (distance point1 point2)).
When it comes time to apply the parameter to your block property, that is also half the distance as well.
If you wanted the block to start halfway, you would use polar to set that point as your insert point for the block instead.
The rotation for the block would simply be determined by "angle", possible plus a factor depending on the original angle of the block definition. That will probably entail converting to radians. Since the angle is in radians so if you go the "(Command "_insert"..." route you need to convert to degrees. The "vla-insert-block" works in radians if I'm not mistaken, so it's actually easier once you get used to radians.
great news it works perfectly now .........i re-watched the video and discovered some parenthesis miss placed and a variable name mistake................thank you so much for fast responding........ I'm looking forward for the next big project series
Great to hear! You're acquiring quite advanced code. When I first learnt these things it sometimes took weeks to debug all my code. You're off to a great start. And now that you have those sub-functions working you can re-purpose them for all sorts of things: You can manipulate almost any dynamic block property.
@@randomCADstuff how about a title block project its really useful for the average user and got a lot of the sweet attribute manipulation code that i aspire to learn please consider this one 😁
The title block video would be a great idea... just a matter of time. I am working on the code I mentioned in a reply to another comment. The concept works... but it's complicated. It would be exceedingly hard to convey what I've done in a short amount of time. This video series took me almost an entire weekend. A title block related video would take anywhere from 4-10 times as long... but would be a good long-term project to chip away at I guess. Or... once the project gets to a certain point, just provide it as a FAS file ready to use, and the video would just be directions opposed to having people code it themselves. All the above is very dependent on free time however.
I could also make a much shorter video showing how I actually create and manage title blocks. I actually make things extremely simple when it comes to title blocks: Any text that is likely to change from sheet to sheet not connected to the title block itself. That way users can just double-click any text to edit it (feels easier for me opposed to going into attributes, especially multi-line attributes). My title blocks "block" only contains linework and static labels. I also just 'manually' copy sheets, but I'm working on LISP to automate that part (as well as other stuff). Until recently I've never really had to manage large numbers of sheets, so have gotten away with keeping things simple.
@@randomCADstuff well i hope to see another series regardless ,having a pro show you the ropes is my kind of way to learn programming and a project idea is just a way to keep me motivated to learn ...........happy holidays to you and im practicing while waiting for a new project hopfully soon 😊
This is incredibly useful. Thank you 🙏
Where can I obtain the code? This was very helpful
Unfortunately the code is lost. I have made a new improve version which is included with my template: ruclips.net/video/GNQ7n7sM3lE/видео.html
Thank you so much. I learned a lot.
How would i go about having my user defined text remain the same and have just number increment by 1 for each insertion?
I'm not 100% sure if you are doing your own program, or referring to the code's behavior. In case it's the latter, I will mention that I'm going to release a better version of this (the code not a tutorial) soon. The new version would allow you to enter "A-1" for example, and it will increment "A-2", "A-3" and so on...
@@randomCADstuff oh sweet that will be fantastic to reference!! I'll have to rethink my question in terms of what I'm looking for.
@@TheWhiteLarryByrd If you go to about the 12:00 mark of this video: ruclips.net/video/DtjEhLm8dms/видео.html, you'll see exactly what I changed.
@@randomCADstuff which defun changed in the code to allow that particular instance to now be able to occur in your updated template video?
I'm not 100% sure what you mean so you'll have to be more specific. There's a few changes to the code just for better performance and compatibility with the template (automatic layer selection for example). @@JakksonSloann99
pudes colocar el codigo para descargar; gracias
time table :
@2:46 setting up the scale dynamic parameter
@34:00 alignment code done
Do you perhaps happen to be doing commissions on lisp routines?
To be honest I'm not quite good enough. Also, the wage at my day job exceeds what I could ever earn LISP programming. I'd probably have to be at the level of "one of the best in the industry" to really have a go at it. My skills will come in useful making my own venture competitive and successful, so everything I've learnt was well worth the effort (in my case!). Learning LISP allows you to target your own specific needs and make your tools work exactly as you want them; sometimes that's impossible to achieve even with a really good programmer (the back-and-forth gets expensive). If you happen to be seeking someone, a good place to start is www.theswamp.org/ . There are people there but I don't know their rates. If you don't find that helpful come back here and ask me again, I might be able to dig something up for you ;-)
@@randomCADstuff Hi, I understand. Thank you for your response. I'll be sure to check out the reference site you gave. Appreciate it.