AutoLISP Tutorial > Introduction · AutoLISP Tutorial > Designing and Beginning the Program In this...
Transcript of AutoLISP Tutorial > Introduction · AutoLISP Tutorial > Designing and Beginning the Program In this...
AutoLISPTutorial>
Introduction
ThistutorialisdesignedtodemonstrateseveralpowerfulcapabilitiesoftheAutoLISP®programmingenvironmentforAutoCAD®andintroducefeaturesoftheAutoLISPlanguagethatmaybenewtoyou.
Thepurposeofthetutorialistodrawagardenpathusinganautomateddrawingtoolthatminimizesdraftingtimeandshowsthepowerofparametricprogramming.Youwilllearntocreateadrawingroutinethatautomatesthegenerationofacomplexshape—thekindofdraftingoperationyoudonotwanttohavetodomanuallyoverandoveragain.
WorkinginVisualLISPTutorialOverview
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>Introduction>
WorkinginVisualLISP
ThistutorialisintendedforexperiencedAutoCADusersandassumesyouhavesomefamiliaritywithLISPorAutoLISP.ItalsoassumesyouunderstandbasicWindows®filemanagementtaskssuchascreatingdirectories,copyingfiles,andnavigatingthroughthefilesystemonyourharddiskornetwork.
Inthistutorial
TheVisualLISP®(VLISP)environmentisintroduced.Thisenvironmentprovidesyouwithediting,debugging,andothertoolsspecifictothecreationofAutoLISPapplications.
ActiveX®andReactorfunctionsofAutoLISParedemonstrated,aswellasseveralotherextensionstotheAutoLISPlanguageprovidedwithVLISP.
Therearethefollowingtwopossibleexecutioncontextsforthistutorial:
TheapplicationmayberunasinterpretedLISPinpiecemealfilesand/orfunctionsthatareloadedintoasingledocument,or
TheprogramcodecanbecompiledintoaVLXapplication,denotedbya*.vlxexecutable.AVLXoperatesfromaself-containednamespacethatcaninteractwiththeapplication-loadingdocument.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>Introduction>
TutorialOverview
YourgoalinthistutorialistodevelopanewcommandforAutoCADthatdrawsagardenpathandfillsitwithcirculartiles.Thetutorialisdividedintosevenlessons.Asyouprogressfromlessontolesson,youreceiveprogressivelylessdetailedinstructionsonhowtoperformindividualtasks.HelpisavailableintheVLISPdocumentationifyouhaveanyquestions.
Lessons4and5areatanintermediatelevelandgobeyondbasicAutoLISPconcepts.Lessons6and7containadvancedandfairlycomplexprogrammingtasksandaredesignedforexperiencedAutoLISPdevelopers.
IfyouchosethefullinstallationoptionwhenyouinstalledAutoCAD,thesourcecodefilesareinthefollowingdirectory:
<AutoCADdirectory>\Tutorial\VisualLISP\
IfyouhavealreadyinstalledAutoCADanddidnotinstallthesamples,youcanreruntheinstallation,chooseCustom,andselectonlytheTutorialsitem.
ItisrecommendedyoudonotmodifythesamplesourcecodefilessuppliedwithAutoCAD.Ifsomethingisnotworkingcorrectlywithinyourprogram,youmaywanttocopythesuppliedsourcecodeintoyourworkingdirectory.Throughoutthetutorial,theworkingdirectoryisreferredtoas:
<AutoCADdirectory>\Tutorial\VisualLISP\MyPath
Ifyouchooseadifferentpathforyourworkingdirectory,substituteyourdirectorynameattheappropriatetimes.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>
DesigningandBeginningtheProgram
Inthisfirstlesson,you'llbeginbydefiningwhattheapplicationwilldo.UsingtheVisualLISP®(VLISP)developmentenvironment,you'llcreateaLISPfileandbeginwritingAutoLISP®codetosupportyourapplication.Intheprocess,you'llbegintodiscoverhowVLISPfacilitatesapplicationdevelopment.
DefiningOverallProgramGoalsGettingStartedwithVisualLISPLookingatVisualLISPCodeFormattingAnalyzingtheCodeFillingtheGapsintheProgramLettingVisualLISPCheckYourCodeRunningtheProgramwithVisualLISPWrappingUpLesson1
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>DesigningandBeginningtheProgram>
DefiningOverallProgramGoals
DevelopinganAutoLISPprogrambeginswithanideaforautomatingsomeaspectofAutoCAD®.Itmaybeaneedtospeeduparepetitivedraftingfunction,ortosimplifyacomplexseriesofoperations.Forthetutorial,thegardenpathyouwantyourprogramtodrawisacomplexshapewithavariablenumberofcomponents,basedoninitialinputfromtheuser.Here'swhatitwilllooklike:
Yourprogrammustdothefollowingtodrawthegardenpath:
Givenastartpoint,anendpoint,andawidth,drawarectilinearboundary.Theboundarycanbeatany2Dorientation.Thereshouldbenolimitonhowlargeorsmallitcanbe.
Prompttheuserfortilesizeandtilespacingvalues.Thetilesaresimplecirclesandwillfilltheboundarybutmustnotoverlaporcrosstheboundary.
Placethetilesinalternatingrows.
Toseehowthingsshouldwork,youcanrunacompletedversionoftheapplicationthatissuppliedwithAutoCAD.
Torunthesuppliedexample
1. FromtheAutoCADToolsmenu,chooseLoadApplication.
2. Selectgardenpath.vlxfromtheTutorial\VisualLISPdirectory,andchooseLoad.
3. ChooseClose.
4. AttheCommandprompt,entergpath.
5. RespondtothefirsttwopromptsbypickingastartpointandanendpointintheAutoCADdrawingarea.
6. Enter2atthehalf-widthofPathprompt.
7. ChooseOKwhenpromptedbytheGardenPathTileSpecificationsdialogbox.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>DesigningandBeginningtheProgram>
GettingStartedwithVisualLISP
Nowthatyou'veseenhowtheapplicationissupposedtowork,youcanbegindevelopingitwithVLISP.Butfirst,ithelpstodemonstratewhatcanhappenwhenVLISPiswaitingforcontroltoreturnfromAutoCAD.Youmayhavealreadyencounteredthis.
ToseeVisualLISPwaitforcontroltoreturnfromAutoCAD
1. FromtheAutoCADToolsmenu,chooseLoadApplication.
2. Selectgardenpath.vlxfromtheTutorial\VisualLISPdirectory,andchooseLoad.
3. ChooseClose.
4. AttheAutoCADCommandprompt,entervlisptostartVisualLISP.
5. SwitchbacktotheAutoCADwindow(eitherselectAutoCADfromthetaskbarorpressALT+TABandchooseAutoCAD),andentergpathattheAutoCADCommandprompt.
6. Beforerespondingtothepromptsfromgpath,switchbacktotheVLISPwindow.IntheVLISPwindow,themousepointerappearsasaVLISPsymbol,andyoucannotchooseanycommandsorentertextanywhereintheVLISPwindow.ThepointersymbolisareminderthatthereisanactivityyoumustcompleteinAutoCADbeforeresumingworkwithVLISP.RememberthiswheneveryouseetheVLISPpointer.
7. ReturntotheAutoCADwindowandrespondtoallthepromptsfromgpath.
Nowyouarereadytobeginbuildingthegardenpathapplication.
TobeginapplicationdevelopmentwithVisualLISP
1. FromtheVLISPFilemenu,chooseNewFile.
2. Enterthefollowingcodeinthetexteditorwindow(itisthewindowtitled“<Untitled-0>”);youcanomitthecomments,ifyouwish:
;;;FunctionC:GPathisthemainprogramfunctionanddefinesthe
;;;AutoCADGPATHcommand.
(defunC:GPath()
;;Asktheuserforinput:firstforpathlocationand
;;direction,thenforpathparameters.Continueonlyifyouhave
;;validinput.
(if(gp:getPointInput);
(if(gp:getDialogInput)
(progn
;;Atthispoint,youhavevalidinputfromtheuser.
;;Drawtheoutline,storingtheresultingpolyline
;;"pointer"inthevariablecalledPolylineName.
(setqPolylineName(gp:drawOutline))
(princ"\nThegp:drawOutlinefunctionreturned<")
(princPolylineName)
(princ">")
(Alert"Congratulations-yourprogramiscomplete!")
)
(princ"\nFunctioncancelled.")
)
(princ"\nIncompleteinformationtodrawaboundary.")
)
(princ);exitquietly
)
;;;Displayamessagetolettheuserknowthecommandname.
(princ"\nTypegpathtodrawagardenpath.")
(princ)
3. ChooseFile SaveAsfromthemenu,andsavethecodeinthenewfileas<AutoCADdirectory>\Tutorial\VisualLISP\MyPath\gpmain.lsp.
4. Reviewyourwork.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>DesigningandBeginningtheProgram>
LookingatVisualLISPCodeFormatting
VLISPrecognizesthevarioustypesofcharactersandwordsthatmakeupanAutoLISPprogramfileandhighlightsthecharactersindifferentcolors.Thismakesiteasierforyoutospotsomethingincorrectquickly.Forexample,ifyoumissaclosingquotationmarkfollowingatextstring,everythingyoutypecontinuestodisplayinmagenta,thecolordenotingstrings.Whenyouentertheclosingquotationmark,VLISPcorrectlycolorsthetextfollowingthestring,accordingtothelanguageelementitrepresents.
Asyouentertext,VLISPalsoformatsitbyaddingspacingandindentation.TogetVLISPtoformatcodeyoucopyintoitstexteditorfromanotherfile,chooseTools FormatCodeinEditorfromtheVLISPmenu.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>DesigningandBeginningtheProgram>
AnalyzingtheCode
Thedefunfunctionstatementdefinesthenewfunction.NoticethemainfunctionisnamedC:GPath.TheC:prefixestablishesthatthisfunctioniscallablefromtheAutoCADcommandline.GPathisthenameusersentertolaunchtheapplicationfromtheAutoCADCommandprompt.Thefunctionsthatobtaininputfromusersarenamedgp:getPointInputandgp:getDialogInput.Thefunctionthatdrawsthegardenpathoutlineisgp:drawOutline.Thesenamesareprefixedwithgp:toindicatetheyarespecifictothegardenpathapplication.Thisisnotarequirement,butitisagoodnamingconventiontousetodistinguishapplication-specificfunctionsfromgeneralutilityfunctionsyoufrequentlyuse.
Inthemainfunction,princexpressionsdisplaytheresultsoftheprogramifitrunssuccessfully,orawarningmessageiftheprogramencountersanunexpectedevent.Forexample,aswillbeseeninLesson2,iftheuserpressesENTERinsteadofpickingapointonthescreen,thecalltogp:getPointInputendsprematurely,returninganilvaluetothemainfunction.Thiscausestheprogramtoissueaprincmessageof“Incompleteinformationtodrawaboundary.”
Thecalltoprincneartheendoftheprogramservesasaprompt.Uponapplicationload,thepromptinformsuserswhattheyneedtotypetoinitiatethedrawingofagardenpath.Thefinalprincwithoutastringargumentforcestheprogramtoexitquietly,meaningthevalueofthemainfunction'sfinalexpressionisnotreturned.Ifthefinalsuppressingprincwereomitted,thepromptwoulddisplaytwice.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>DesigningandBeginningtheProgram>
FillingtheGapsintheProgram
Forthecodeinthisnewfiletoworkcorrectly,youmustwritethreemorefunctiondefinitions.Themaingardenpathcodecontainscallstothreecustomfunctions:
gp:getPointInput
gp:getUserInput
gp:drawOutline
Fornow,youwilljustwritestubbed-outfunctiondefinitions.Astubbed-outfunctionservesasaplaceholderforthecompletefunctionthatistofollow.Itallowsyoutotryoutpiecesofyourcodebeforeaddingallthedetailneededtocompletetheapplication.
Todefinestubbed-outfunctionsfortheapplication
1. PositionyourcursoratthetopoftheprogramcodeinthetexteditorwindowandpressENTERacoupleoftimestoaddblanklines.
2. Enterthefollowingcode,beginningwhereyouinsertedtheblanklines:
;;;Functiongp:getPointInputwillgetpathlocationandsize
(defungp:getPointInput()
(alert
"Functiongp:getPointInputwillgetuserdrawinginput"
)
;;Fornow,returnT,asifthefunctionworkedcorrectly.
T
)
;;;Functiongp:getDialogInputwillgetpathparameters
(defungp:getDialogInput()
(alert
"Functiongp:getDialogInputwillgetuserchoicesviaadialog"
)
;;Fornow,returnT,asifthefunctionworkedcorrectly.
T
)
;;;Functiongp:drawOutlinewilldrawthepathboundary
(defungp:drawOutline()
(alert
(strcat"Thisfunctionwilldrawtheoutlineofthepolyline"
"\nandreturnapolylineentityname/pointer."
)
)
;;Fornow,simplyreturnaquotedsymbol.Eventually,this
;;functionwillreturnanentitynameorpointer.
'SomeEname
)
RightbeforetheendofeachinputfunctionisalineofcodethatcontainsonlyaT.Thisisusedasareturnvaluetothecallingfunction.AllAutoLISPfunctionsreturnavaluetothefunctionthatcalledthem.TheletterTisthesymbolfor“true”inAutoLISP,andaddingitcausesthefunctiontoreturnatruevalue.Thewaygpmain.lspisstructured,eachinputfunctionitcallsmustreturnavalueotherthannil(whichindicates“novalue”)fortheprogramtoproceedtothenextstep.
AnAutoLISPfunctionwill,bydefault,returnthevalueofthelastexpressionevaluatedwithinit.Inthestubbed-outfunctions,theonlyexpressionisacalltothealertfunction.Butalertalwaysreturnsnil.Ifthisisleftasthelastexpressioningp:getPointInput,itwillalwaysreturnnil,andyouwillneverpassthroughtheiftothegp:getDialogInputfunction.
Forasimilarreason,theendofthegp:DrawOutlinefunctionreturnsaquotedsymbol('SomeEname)asaplaceholder.AquotedsymbolisaLISPconstructthatisnotevaluated.(IfyouarecuriousabouthowtheLISPlanguageworks,thereareanumberofgoodbooksavailable,mentionedattheendofthistutorial.)
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>DesigningandBeginningtheProgram>
LettingVisualLISPCheckYourCode
VLISPhasapowerfulfeatureforcheckingyourcodeforsyntacticalerrors.Usethistoolbeforetryingtoruntheprogram.Youcancatchcommontypingerrors,suchasmissingparenthesesormissingquotationmarks,andothersyntacticalproblems.
Tocheckthesyntaxofyourcode
1. Makesurethetexteditorwindowcontaininggpmain.lspistheactivewindow.(Clickinthetitlebarofthewindowtoactivateit.)
2. FromtheVLISPmenu,chooseTools CheckTextinEditor.
TheBuildOutputwindowisdisplayedwiththeresultsofthesyntaxcheck.IfVLISPdidnotdetectanyerrors,thewindowcontainstextsimilartothefollowing:
[CHECKINGTEXTGPMAIN.LSPloading...]
......
;Checkdone.
Ifyouhaveproblemsandneedhelp,refertothe“DevelopingProgramswithVisualLISP”sectionoftheAutoLISPDeveloper'sGuide.Seeifyoucandeterminewheretheproblemislocated.Ifyouarespendingtoomuchtimelocatingtheproblem,usethesamplegpmain.lspfileprovidedinthelesson1directorytocontinuewiththetutorial.
Tousethesuppliedgpmain.lspprogram(ifnecessary)
1. Closethetexteditorwindowcontainingthegpmain.lspcodeyouentered.
2. ChooseFile OpenFilefromtheVLISPmenu,andopenthegpmain.lspfileinthe\Tutorial\VisualLISP\lesson1directory.
3. ChooseFile SaveAsandsavethefileinyour\Tutorial\VisualLISP\MyPathdirectoryasgpmain.lsp,replacingthecopyyoucreated.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>DesigningandBeginningtheProgram>
RunningtheProgramwithVisualLISP
RunningAutoLISPprogramsinVLISPallowsyoutousethemanydebuggingfeaturesofVLISPtoinvestigateproblemsthatmayoccurinyourapplication.
Toloadandruntheprogram
1. Withthetexteditorwindowactive,chooseTools LoadTextinEditorfromtheVLISPmenu.
2. Atthe_$promptintheVLISPConsolewindow,enter(C:GPath).TheConsolewindowexpectscommandstobeenteredinAutoLISPsyntax,soallfunctionnamesmustbeenclosedinparentheses.
3. PressENTERorclickOKinresponsetothemessagewindows.Thefinalmessageshouldread“Congratulations-yourprogramiscomplete!”
Note IfAutoCADisminimizedwhenyourungpath,youwillnotseethepromptsuntilyourestoretheAutoCADwindow(usingeitherthetaskbarorALT+TAB).
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>DesigningandBeginningtheProgram>
WrappingUpLesson1
Inthislesson,you
Definedprogramgoals.
Learnedthevalueofstubfunctions.
Learnedaboutnamingfunctionstoidentifythemasspecifictoyourapplicationorasgeneralfunctionstobeusedoverandover.
LearnedhowtouseVLISPtocheckyourcode.
LearnedhowtoloadandrunaprograminVLISP.
Youaredonewiththislesson.Saveyourprogramfileagaintobecertainyouhavethelatestrevisions.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>
UsingVisualLISPDebuggingTools
ThislessonteachesyouhowtouseseveralvaluableVisualLISP®debuggingtoolsthatspeedupthedevelopmentofAutoLISP®programs.Youwillalsolearnthedifferencebetweenlocalandglobalvariables,andwhentousethem.Yourprogramwillbecomemoreactive—promptinguserstoentersomeinformation.Theinformationwillbestoredinalistandyou'llbegintounderstandthepowerofusinglistswithinyourAutoLISPprograms.Afterall,LISPgotitsnamebecauseitisaLIStProcessinglanguage.
DifferentiatingBetweenLocalandGlobalVariablesUsingAssociationListstoBundleDataExaminingProgramVariablesRevisingtheProgramCodeCommentingProgramCodeSettingaBreakpointandUsingMoreWatchesWrappingUpLesson2
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>UsingVisualLISPDebuggingTools>
DifferentiatingBetweenLocalandGlobalVariables
Thislessondiscussestheuseoflocalvariablesversusglobaldocumentvariables.Globalvariablesareaccessiblebyallfunctionsloadedwithinadocument(anAutoCAD®drawing).Thesevariablesmayretaintheirvalueaftertheprogramthatdefinedthemcompletes.Sometimes,thisiswhatyouwant.You'llseeanexampleofthislaterinthetutorial.
Localvariablesretaintheirvalueonlyaslongasthefunctionthatdefinedthemisrunning.Afterthefunctionfinishesrunning,thelocalvariablevaluesareautomaticallydiscarded,andthesystemreclaimsthememoryspacethevariableused.Thisisknownasautomaticgarbagecollection,andisafeatureofmostLISPdevelopmentenvironments,suchasVLISP.Localvariablesusememorymoreefficientlythanglobalvariables.
Anotherbigadvantageisthatlocalvariablesmakeiteasiertodebugandmaintainyourapplications.Withglobalvariables,youareneversurewhenorinwhichfunctionthevariable'svaluemightbemodified;withlocalvariablesyoudon'thaveasfartotrace.Youusuallyendupwithfewersideeffects(thatis,one
partoftheprogramaffectingavariablefromanotherpartoftheprogram).
Becauseoftheadvantagescited,thistutorialuseslocalvariablesalmostexclusively.
Note IfyouhavebeenworkingwithAutoLISPforsometime,youmayhavedevelopedthepracticeofusingglobalvariablesduringdevelopmenttoexamineyourprogramwhileyouarebuildingit.Thispracticeisnolongernecessary,giventhepowerfuldebuggingtoolsofVLISP.
UsingLocalVariablesintheProgramExaminingthegp:getPointInputFunction
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>UsingVisualLISPDebuggingTools>DifferentiatingBetweenLocalandGlobalVariables>
UsingLocalVariablesintheProgram
Refertothegp:getPointInputfunctionyoucreatedinLesson1:
(defungp:getPointInput()
(alert
"Functiongp:getPointInputwillgetuserdrawinginput"
)
;;Fornow,returnT,asifthefunctionworkedcorrectly.
T
)
Sofar,thefunctiondoesnotdomuchwork.Youwillnowbegintobuildonitbyaddingfunctionstogetinputfromtheuser,whichwilldefinethestartpoint,endpoint,andwidthofthepath.
ItisagoodpracticewhencreatingAutoLISPprogramstoemulatethebehaviorofAutoCAD.Forthisreason,insteadofaskingtheusertoindicatethewidthbyselectingapointinthedrawinginrespecttothecenterlineofalinearshape,yourprogramshouldaskforaselectionofthehalf-width.
Oncethegp:getPointInputfunctioniscomplete,thevariables,aswellasthevaluesassignedtothem,willnolongerexist.Therefore,youwillstoreuser-suppliedvaluesinlocalvariables.Here'swhatthefunctionmightlooklike:
(defungp:getPointInput(/StartPtEndPtHalfWidth)
(if(setqStartPt(getpoint"\nStartpointofpath:"))
(if(setqEndPt(getpointStartPt"\nEndpointofpath:"))
(if(setqHalfWidth(getdistEndPt"\nhalf-widthofpath:"))
T
)
)
)
)
Thelocalvariablesaredeclaredfollowingtheslashcharacter,inthedefun
statementthatbeginsthefunction.Thefirstcalltogetpointpromptstheusertoindicateastartpoint.Theendpointisthenacquiredinrelationtothechosenstartpoint.Whileselectingtheendpoint,theuserwillobservearubber-bandlineextendingfromthestartpoint.Similarly,whilesettingthehalf-widthvalue,theuserwillviewanotherrubber-bandline,thistimerepresentingdistance,emanatingfromtheendpoint.
Toseehowgp:getPointInputworks
1. Typethegp:getPointInputcodeintotheVLISPConsolewindow.
2. WiththeConsolewindowcursorfollowingthelastparenthesisoftheblockofcode(oronthenextlinebelowit),pressENTERandyouwillreplaceanypreviouslyloadedversionofthegp:getPointInputfunction.
3. ExecutethefunctionfromtheConsolewindowbyentering(gp:getPointInput)attheConsoleprompt.
4. Pickpointswhenprompted,andenterahalf-widthvalue.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>UsingVisualLISPDebuggingTools>DifferentiatingBetweenLocalandGlobalVariables>
Examiningthegp:getPointInputFunction
Whenyouranthegp:getPointInputfunction,controlwasautomaticallypassedfromVLISPtoAutoCAD.Yourespondedtothreeprompts,afterwhichcontrolwaspassedbackfromAutoCADtoVLISP,andaTsymboldisplayedintheConsolewindow.
Withintheprogram,here'swhathappens:
1. VLISPwaitsforyoutopickthefirstpoint.
2. Whenyoupickthefirstpoint,theprogramstoresthevalueofyourselection(alistcontainingthreecoordinatevalues—anX,Y,andZvalue)intotheStartPtvariable.
3. Thefirstiffunctionexaminestheresulttodeterminewhetheravalidvaluewasenteredornovaluewasentered.Whenyoupickastartpoint,controlispassedtothenextgetpointfunction.
4. Whenyoupickanendpoint,thepointvalueisstoredintheEndptvariable.
5. Theresultofthisstatementisexaminedbythenextifstatement,andcontrolispassedtothegetdistfunction.
6. Thegetdistfunctionactsinasimilarfashionwhenyoupickapointonthescreenorenteranumericvalue.TheresultofthegetdistfunctionisstoredintheHalfWidthvariable.
7. ProgramflowreachestheTnesteddeeplywithinthefunction.Nootherfunctionsfollowthis,sothefunctionends,andthevalueTisreturned.ThisistheTyouseeattheConsolewindow.
Youneedsomewaytoreturnvaluesfromonefunctiontoanother.Onewayto
dothisistocreatealistofthevaluesretrievedfromgp:getPointInput,ashighlightedinthefollowingcode:
(defungp:getPointInput(/StartPtEndPtHalfWidth)
(if(setqStartPt(getpoint"\nStartpointofpath:"))
(if(setqEndPt(getpointStartPt"\nEndpointofpath:"))
(if(setqHalfWidth(getdistEndPt"\nhalf-widthofpath:"))
(listStartPtEndPtHalfWidth)
)
)
)
)
Copythisversionofgp:getPointInputintotheConsolewindowandpressENTER.Here'sanopportunitytotryanotherfeatureoftheConsolewindow.
TousetheConsolewindowhistoryfeaturetorungp:getPointInput
1. PressTAB.ThisinvokestheConsolehistorycommand,cyclingthroughanycommandspreviouslyenteredintheConsolewindow.Ifyougotoofar,pressSHIFT+TABtocycleintheotherdirection.
2. Whenyousee(gp:getPointInput)attheConsoleprompt,pressENTERtoexecutethefunctiononceagain.
3. Respondtothepromptsasbefore.
Thefunctionreturnsalistcontainingtwonestedlistsandareal(floatingpoint)value.Thereturnvalueslooklikethefollowing:
((4.462074.623180.0)(7.666884.623180.0)0.509124)
ThesevaluescorrespondtotheStartPt,EndPt,andHalfWidthvariables.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>UsingVisualLISPDebuggingTools>
UsingAssociationListstoBundleData
Thepreviousexampleworks,butyoucandobetter.Inthenextexercise,youwillbuildanassociationlist,orassoclist(aftertheLISPfunctionthatdealswithassociationlists).Inanassociationlist,thevaluesyouareinterestedinareassociatedwithkeyvalues.Hereisasampleassociationlist:
((104.462074.623180.0)(117.666884.623180.0)(40.1.018248))
Inthesampleassociationlist,thekeyvaluesarethenumbers10,11,and40.Thesekeyvaluesserveasauniqueindexwithinthelist.ThisisthemechanismAutoCADusestoreturnentitydatatoAutoLISPifyouaccessanentityfromwithinyourprogram.Akeyvalueof10indicatesastartpoint,akeyvalueof11istypicallyanendpoint.
Whataretheadvantagesofanassociationlist?Foronething,unliketheregularlist,theorderofthevaluesreturneddoesnotmatter.Lookatthefirstlistagain:
((4.462074.623180.0)(7.666884.623180.0)0.509124)
Lookatthereturnvalues;itisnotapparentwhichsublististhestartpointandwhichistheendpoint.Furthermore,ifyoumodifythefunctioninthefuture,anyotherfunctionthatreliesondatareturnedinaspecificordermaybeadverselyaffected.
Usinganassociationlist,theorderofthevaluesdoesnotmatter.Iftheorderofanassociationlistchanges,youcanstilltellwhichvaluedefineswhat.Forexample,an11valueisstillanendpoint,regardlessofwhereitoccurswithintheoveralllist:
((117.666884.623180.0);orderoflist
(40.1.018248);hasbeen
(104.462074.623180.0));modified
PuttingAssociationListstoUse
AutoLISPTutorial>UsingVisualLISPDebuggingTools>UsingAssociationListstoBundleData>
PuttingAssociationListstoUse
Whenyouuseassociationlists,youshoulddocumentwhatyourkeyvaluesrepresent.Forthegardenpath,thekeyvaluesof10,11,40,41,and50willmeanthefollowing:
10indicatesthe3Dcoordinateofthestartpointofthepath.
11indicatesthe3Dcoordinateoftheendpointofthepath.
40indicatesthewidth(notthehalf-width)ofthepath.
41indicatesthelengthofthepath,fromstarttoend.
50indicatestheprimaryvector(orangle)ofthepath.
Thefollowingisanupdatedversionofthegp:getPointInputfunction.Withinit,anAutoLISPfunctioncalledcons(shortforconstructalist)buildsthekeyedsubliststhatbelongtotheassociationlist.CopythisversiontotheConsolewindow,pressENTER,andrun(gp:getPointInput)again:
(defungp:getPointInput(/StartPtEndPtHalfWidth)
(if(setqStartPt(getpoint"\nStartpointofpath:"))
(if(setqEndPt(getpointStartPt"\nEndpointofpath:"))
(if(setqHalfWidth(getdistEndPt"\nhalf-widthofpath:"))
;;ifyou'vemadeitthisfar,buildtheassociationlist
;;asdocumentedabove.Thiswillbethereturnvalue
;;fromthefunction.
(list
(cons10StartPt)
(cons11EndPt)
(cons40(*HalfWidth2.0))
(cons50(angleStartPtEndPt))
(cons41(distanceStartPtEndPt))
)
)
)
)
)
Noticethat,whenbuildingthelist,theprogramconvertsthehalf-widthspecifiedbytheuserintoafullwidthbymultiplyingitsvalueby2.
TheConsolewindowshowsoutputsimilartothefollowing:
_$(gp:getPointInput)((102.160981.601160.0)(1112.71267.119630.0)(40.0.592604)(50.0.481876)(41.11.9076))
_$
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>UsingVisualLISPDebuggingTools>UsingAssociationListstoBundleData>
StoringtheReturnValueofgp:getPointInputinaVariable
Nowtrysomethingelse.Callthefunctionagain,butthistimestorethereturnvalueinavariablenamedgp_PathData.Todothis,enterthefollowingattheConsolewindowprompt:
(setqgp_PathData(gp:getPointInput))
Toviewthevalueofthevariableyoujustset,enteritsnameattheConsolewindowprompt:
_$gp_PathData
VLISPreturnsdatalikethefollowing:
((102.177421.157710.0)(1113.20577.004660.0)(40.1.12747)(50.0.487498)(41.12.4824))
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>UsingVisualLISPDebuggingTools>
ExaminingProgramVariables
VLISPprovidesyouwithanentiretoolkitofprogramminganddebuggingtools.OneofthemostvaluabletoolsisaWatch,whichletsyouexaminevariablesinmoredetailthanappearsintheVLISPConsolewindow.Youcanalsowatchlocalvariableswithinfunctionsasthefunctionexecutes.
Towatchthevalueofavariable
1. ChooseDebug AddWatchfromtheVLISPmenu.VLISPdisplaysadialogboxtitled“AddWatch.”
Enterthenameofthevariableyouwishtoexamine.Forthisexample,specifygp_PathData,thevariableyoujustsetfromtheConsolewindow.VLISPdisplaysaWatchwindow:
VLISPdisplaysthevalueofthevariableonasinglelinewithintheWatchwindow—thebasewindowshownintheillustration.Inthiscase,thevalueofthevariableisalonglist,andyoucannotseeitsentirevalue.YoucanresizetheWatchwindowbydraggingitsborder,butthereisabetteralternative.
2. Double-clickonthevariablenameintheWatchwindow.ThisopensanInspectwindow:
TheInspectwindowindicatesthedatatypeofthevariableyouareinspecting(inthiscase,alist),andthevalueofthevariable.Forlists,Inspectdisplayseachlistitemonitsownline.
3. Double-clickonthelinewiththeassociationlistkey11.VLISPopensanotherInspectwindow:
4. Whenyouaredoneinspectingvariables,closealltheInspectwindowsbutkeeptheWatchwindowopen.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>UsingVisualLISPDebuggingTools>
RevisingtheProgramCode
Nowthatyou'veseenhowtouseassociationlistsinAutoLISPcode,youcanusethismethodinwritingthecompletedversionofthegp:getPointInputfunction.Usingthefollowingcode,replaceormodifytheversionofgp:getPointInputyoupreviouslysavedingpmain.lsp.
Note Ifyouneedorwanttotypethecodeintogpmain.lsp,ratherthancopyitfromanotherfile,youcansavetimebyleavingoutthecomments(alllinesthatbeginwithsemicolons).Butdon'tgetusedtotheideaofwritingcodewithoutcomments!
;;;--------------------------------------------------------------;
;;;Function:gp:getPointInput;
;;;--------------------------------------------------------------;
;;;Description:Thisfunctionaskstheusertoselectthree;
;;;pointsinadrawing,whichwilldeterminethe;
;;;pathlocation,direction,andsize.;
;;;--------------------------------------------------------------;
;;;Iftheuserrespondstothegetfunctionswithvaliddata,;
;;;usestartPtandendPttodeterminetheposition,length,;
;;;andangleatwhichthepathisdrawn.;
;;;--------------------------------------------------------------;
;;;Thereturnvalueofthisfunctionisalistconsistingof:;
;;;(10.StartingPoint);;Listof3reals(apoint)denoting;
;;;;;startingpointofgardenpath.;
;;;(11.EndingPoint);;Listof3reals(apoint)denoting;
;;;;;endingpointofgardenpath.;
;;;(40.Width);;Realnumberdenotingboundary;
;;;;;width.;
;;;(41.Length);;Realnumberdenotingboundary;
;;;;;length.;
;;;(50.PathAngle);;Realnumberdenotingtheangle;
;;;;;ofthepath,inradians.;
;;;--------------------------------------------------------------;
(defungp:getPointInput(/StartPtEndPtHalfWidth)
(if(setqStartPt(getpoint"\nStartpointofpath:"))
(if(setqEndPt(getpointStartPt"\nEndpointofpath:"))
(if(setqHalfWidth(getdistEndPt"\nhalf-widthofpath:"))
;;ifyou'vemadeitthisfar,buildtheassociationlist
;;asdocumentedabove.Thiswillbethereturnvalue
;;fromthefunction.
(list
(cons10StartPt)
(cons11EndPt)
(cons40(*HalfWidth2.0))
(cons50(angleStartPtEndPt))
(cons41(distanceStartPtEndPt))
)))))
Next,youneedtoupdatethemainfunction,C:GPath,ingpmain.lsp.Modifyittolooklikethefollowingcode:
(defunC:GPath(/gp_PathData)
;;Asktheuserforinput:firstforpathlocationand
;;direction,thenforpathparameters.Continueonlyifyou
;;havevalidinput.Storethedataingp_PathData.
(if(setqgp_PathData(gp:getPointInput))
(if(gp:getDialogInput)
(progn
;;Atthispoint,youhavevalidinputfromtheuser.
;;Drawtheoutline,storingtheresultingpolyline
;;pointerinthevariablecalledPolylineName.
(setqPolylineName(gp:drawOutline))
(princ"\nThegp:drawOutlinefunctionreturned<")
(princPolylineName)
(princ">")
(Alert"Congratulations-yourprogramiscomplete!")
);_endofprogn
(princ"\nFunctioncancelled.")
);_endofif
(princ"\nIncompleteinformationtodrawaboundary.")
);_endofif
(princ) ;exitquietly
);_endofdefun
Ifyouarecopyingandpastingthecode,addthefollowingcommentsasaheaderprecedingC:GPath:
;;;**************************************************************;
;;;Function:C:GPathTheMainGardenPathFunction;
;;;--------------------------------------------------------------;
;;;Description:Thisisthemaingardenpathfunction.Itisa;
;;;C:function,meaningthatitisturnedintoan;
;;;AutoCADcommandcalledGPATH.Thisfunction;
;;;determinestheoverallflowofthegardenpath;
;;;program.;
;;;**************************************************************;
;;;Thegp_PathDatavariableisanassociationlistoftheform:;
;;;(10.StartingPoint)-Listof3reals(apoint)denoting;
;;;startingpointofthegardenpath.;
;;;(11.EndingPoint)-Listof3reals(apoint)denoting;
;;;endpointofthegardenpath.;
;;;(40.Width)-Realnumberdenotingboundary;
;;;width.;
;;;(41.Length)-Realnumberdenotingboundary;
;;;length.;
;;;(50.PathAngle)-Realnumberdenotingtheangleof;
;;;thepath,inradians.;
;;;(42.TileSize)-Realnumberdenotingthesize;
;;;(radius)ofthegardenpathtiles.;
;;;(43.TileOffset)-Spacingoftiles,bordertoborder.;
;;;(3.ObjectCreationStyle);
;;;-Objectcreationstyleindicateshow;
;;;thetilesaretobedrawn.The;
;;;expectedvalueisastringandone;
;;;oneofthreevalues(stringcase;
;;;isunimportant):;
;;;"ActiveX";
;;;"Entmake";
;;;"Command";
;;;(4.PolylineBorderStyle);
;;;-Polylineborderstyledetermines;
;;;thepolylinetypetobeusedfor;
;;;pathboundary.Theexpectedvalue;
;;;oneofthefollowing(stringcaseis;
;;;unimportant):;
;;;"Pline";
;;;"Light";
;;;**************************************************************;
Totestthecoderevisions
1. Savetheupdatedfile.
2. UsetheCheckfeaturetosearchforanysyntacticalerrors.
3. Formatthecode,tomakeitmorereadable.
4. Loadthecode,sothatVLISPredefinestheearlierversionsofthefunctions.
5. Toruntheprogram,enter(c:gpath)attheConsoleprompt.
Iftheprogramdoesnotrunsuccessfully,tryfixingitandrunningitagain.Repeatuntilyouaretoofrustratedtocontinue.Ifallelsefails,youcancopythecorrectcodefromtheTutorial\VisualLISP\Lesson2directory.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>UsingVisualLISPDebuggingTools>
CommentingProgramCode
VLISPtreatsanyAutoLISPstatementbeginningwithasemicolonasacomment.Thelasttwocodeexamplescontainedalotofcomments.AcommentinanAutoLISPprogramissomethingyouwriteforyourself,notfortheprogram.Commentingcodeisoneofthebestprogrammingpracticesyoucanestablishforyourself.Whywritecomments?
Toexplainthecodetoyourselfwhenyouareeditingtheprogramninemonthsinthefuture,addingallthosefeaturesyourusershavebeenaskingyouabout.Memoryfades,andthemostapparentsequenceoffunctionscaneasilyturnintoanunrecognizabletangleofparentheses.
Toexplainthecodetootherswhoinherittheresponsibilityofupdatingtheprogram.Readingsomeoneelse'scodeisanextremelyfrustratingexperience,especiallyifthecodecontainsveryfewcomments.
VLISPcontainssomeutilitiesthathelpyouasyoucommentyourcode.Noticesomecommentsintheexamplesbeginwiththreesemicolons(;;;),sometimestwo(;;),andsometimesjustone(;).Referto“ApplyingVisualLISPCommentStyles”intheAutoLISPDeveloper'sGuidetoseehowVLISPtreatsthedifferentcomments.
Tosavespace,theremainingcodeexamplesinthistutorialdonotincludeallthecommentsinthesamplesourcefiles.Itisassumedyouhavealreadyestablishedthebeneficialhabitofextensivecommentingandwilldosowithoutanyprompting.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>UsingVisualLISPDebuggingTools>
SettingaBreakpointandUsingMoreWatches
Abreakpointisasymbol(point)youplaceinsourcecodetoindicatewhereyouwantaprogramtostopexecutingtemporarily.Whenyourunyourcode,VLISPproceedsnormallyuntilitencountersabreakpoint.Atthatpoint,VLISPsuspendsexecutionandwaitsforyoutotellitwhattodo.Ithasn'thaltedtheprogramforgood—ithasplaceditinastateofsuspendedanimation.
Whileyourprogramissuspended,youcan
Stepthroughyourcode,functionbyfunction,orexpressionbyexpression.
Resumenormalexecutionofyourprogramatanypoint.
Alterthevalueofvariablesdynamically,andchangetheresultsoftheprogrambeingexecuted.
AddvariablestotheWatchwindow.UsingtheDebugToolbarSteppingThroughCodeWatchingVariablesAsYouStepThroughaProgramSteppingOutofthegp:getPointInputFunctionandintoC:Gpmain
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>UsingVisualLISPDebuggingTools>SettingaBreakpointandUsingMoreWatches>
UsingtheDebugToolbar
TheDebugtoolbarcontainsseveraltoolsyouwillemployasyouworkthroughthissection.Bydefault,thistoolbarisattachedtotheViewandToolstoolbars,andappearsasasingleVLISPtoolbar.
TheDebugtoolbaristheleft-mostsetoficons.Mostoftheitemsonthetoolbarareinactiveuntilyourunyourprogramindebuggingmode(thatis,withoneormorebreakpointsdefined).
Ifyouhaven'tdonesoalready,detachtheDebugtoolbarfromitspositionatthetopofthescreen.Todothis,grabanddragitbythetwoverticalgripsattheleftofthetoolbar.YoucandetachanyoftheVLISPtoolbarsandpositionthemonyourscreenwheretheyaremosteffectiveforyourstyleofwork.
TheDebugtoolbarisdividedintothreemaingroupsofbuttons,eachconsistingofthreebuttons.Whenyourunaprogramindebuggingmode,thetoolbarlookslikethefollowing:
Thefirstthreebuttonsallowyoutostepthroughyourprogramcode.
ThenextthreebuttonsdeterminehowVLISPshouldproceedwheneverithasstoppedatabreakpointoranerror.
Thenextthreebuttonssetorremoveabreakpoint,addaWatch,andjumptothepositionwithinyoursourcecodewherethelastbreakoccurred.
ThelastbuttonontheDebugtoolbarisaStepIndicator.Itdoesnotexecuteanyfunctionbutprovidesavisualindicationofwhereyourcursorispositionedasyoustepthroughyourcode.Whenyouarenotrunningindebuggingmode,this
buttonappearsblank.
Tosetabreakpoint
1. IntheVLISPeditorwindowcontaininggpmain.lsp,positionyourcursorjustinfrontoftheopeningparenthesisofthesetqfunctionofthefollowinglineofcode,withinthegp:getPointInputfunction:
(setqHalfWidth(getdistEndPt"\nhalf-widthofpath:"))
2. Clickthemouseonce.Thepositionisillustratedinthefollowingscreensnapshot:
3. Withthetextinsertionpointset,choosetheToggleBreakpointbuttonontheDebugtoolbar.
TheToggleBreakpointbuttonactsasatoggle,alternatingbetweenonandoffstates.Ifthereisnobreakpointatthecursorposition,itsetsone;ifthereisalreadyabreakpointthere,itremovesit.
4. ChoosetheLoadActiveEditWindowbuttonontheToolstoolbartoloadthefile.
5. Runthe(C:GPath)functionfromtheVLISPConsoleprompt.VLISPexecutestheprogramnormallyuptothebreakpoint.Inthiscase,itwillpromptyouforthefirsttwopoints—thestartpointandendpointofthepath.
6. Specifythestartpointandendpointwhenprompted.Afteryouspecifythepoints,VLISPsuspendsexecutionoftheprogramandreturnsfocustothetexteditorwindow,highlightingthelineofcodeatthebreakpointposition:
Thereareacoupleofthingstonotice:
Thecursorislocatedrightatthebreakpoint.Thismaybedifficulttonotice,soVLISPprovidesanotherclue.
IntheDebugtoolbar,theStepIndicatoricondisplaysaredI-beamcursorinfrontofapairofparentheses.ThisindicatestheVLISPdebuggerisstoppedbeforetheexpression.
AutoLISPTutorial>UsingVisualLISPDebuggingTools>SettingaBreakpointandUsingMoreWatches>
SteppingThroughCode
Therearethreebuttonsyoucanusetostepthroughyourcode.Thesearethethreeleft-mosticonsontheDebugtoolbar.Insequence,theyrepresentthefollowingactions:
Stepintothehighlightedexpression.
Stepovertotheendofthehighlightedexpression.
Stepouttotheendofthefunctionwhereyouarecurrentlystopped.
Beforeyoumakeaselection,takeanotherlookatthestatusofthehighlightedcode,thecursorposition,andtheStepIndicatorbutton.Insummary:anexpressionishighlighted,consistingofagetdistfunctionnestedwithinasetqfunction,andthecursorispositionedattheverybeginningofthehighlightedblock.
Tostepthroughthecodefromthebreakpoint
1. ChoosetheStepOverbutton.
AfteryouchoosetheStepOverbutton,controlpassestoAutoCADandyouarepromptedtospecifythewidthofthepath.
2. Replytotheprompt.Afteryouspecifythewidth,controlpassesbacktoVLISP.Noticewhereyourcursorisandwhatthestepindicatorbuttonshows.VLISPevaluatestheentirehighlightedexpression,thenstopsattheendoftheexpression.
3. ChoosetheStepOverbuttonagain.VLISPjumpstothebeginningofthe
nextblockofcode,andhighlightstheentireblock.
4. ChoosetheStepInto(notStepOver)button.
Note Duringthisexercise,ifyoumakeanincorrectselectionandskipasteportwo,youcanrestarttheexerciseveryeasily.First,choosetheResetbuttonfromtheDebugtoolbar.ThisterminatestheexecutionofanyVLISPcode,andresetstheVLISPsystemtothetoplevel.Next,startoveratstep1.
Nowthefirstconsfunctionishighlighted,andVLISPisstoppedrightbeforethefunction(noticetheStepIndicatorbutton).
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>UsingVisualLISPDebuggingTools>SettingaBreakpointandUsingMoreWatches>
WatchingVariablesAsYouStepThroughaProgram
Whileyoustepthroughyourprogram,youcanaddvariablestotheWatchwindowandchangetheirvalues.
IfyoudonotseetheWatchwindow,simplychoosetheWatchWindowbuttononthetoolbartobringitback.
IfyourWatchwindowstillscontainsthevariablegp_PathData,choosetheClearWindowbuttondisplayedatthetopoftheWatchwindow.
ToaddvariablestotheWatchwindow
1. Double-clickonanyoccurrenceofStartPtintheVLISPtexteditorwindow.Thisisthenameofthevariablewhosevalueyouwanttotrack.
2. ChoosetheAddWatchbuttonintheWatchwindow,orright-clickandchooseAddWatch.
3. RepeatthisprocessforthevariablesEndPtandHalfWidth.YourWatchwindowshouldresemblethefollowing:
Ifyouaredebuggingaprogramthatisn'tworkingcorrectly,usebreakpointsincombinationwithwatchestomakesureyourvariablescontainthevaluesyouexpect.
Ifavariabledoesnotcontainthevalueyouthinkitshould,youcanchangethevalueandseehowitaffectstheprogram.Forexample,saythatyouexpectthehalfwidthvaluetobeawholenumber.Butbecauseyouweren'tcarefulaboutpickingthepointsduringtheinputselections,youendedupwithavaluelike1.94818.
Tochangethevalueofavariablewhiletheprogramisrunning
1. EnterthefollowingattheConsoleprompt:
(setqhalfwidth2.0)
NotethatthevalueintheWatchwindowchanges.Butcanyoubesurethenewvaluewillbeusedwhenthewidthsublist(40.width)iscreatedintheassociationlist?AddonemoreexpressiontotheWatchwindowtotestthis.
2. ChooseDebug WatchLastEvaluationfromtheVLISPmenu.Thisaddsavariablenamed*Last-Value*toyourWatchwindow.*Last-Value*isaglobalvariableinwhichVLISPautomaticallystoresthevalueofthelastexpressionevaluated.
3. Stepthroughtheprogram(choosingeithertheStepIntoorStepOverbutton)untiltheexpressionresponsibleforbuildingthewidthsublistisevaluated.Thecodeforthisactionis:
(cons40(*HalfWidth2.0))
IfyouoverrodethevalueofHalfWidthasspecified,theevaluationofthisexpressionshouldreturn(40.4.0)intheWatchwindow.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>UsingVisualLISPDebuggingTools>SettingaBreakpointandUsingMoreWatches>
SteppingOutofthegp:getPointInputFunctionandintoC:Gpmain
Thereisonemorepointtoillustrate:whathappenstothevalueofthelocalvariablesingp:getPointInputafteryouexitthefunction.
Toexitgp:getPointInputandreturncontroltoc:gpath
1. ChoosetheStepOutbutton.
VLISPstepstotheveryendofthegp:getPointInputfunctionandstopsjustbeforeexiting.
2. ChoosetheStepIntobutton.
Controlreturnstoc:gpmain,thefunctionthatcalledgp:getPointInput.
ExaminethevaluesofthevariablesintheWatchwindow.Becausetheyarevariableslocaltothegp:getPointInputfunction,endptandStartPtarenil.VLISPautomaticallyreclaimedthememoryoccupiedbythesevariables.Normally,thethirdlocalfunctionvariableHalfWidthalsocontainsavalueofnil,butduetodebuggingactivity,itwasoverriddengloballyintheConsolewindowandstillpossessesthevalue2.0intheWatchwindow.Alsotheglobal*Last-Value*variabledisplaystheassociationlistconstructedbygp:getPointInput.
Yourfirstdebuggingsessioniscomplete.Butdon'tforgetyourprogramisstillinsuspendedanimation.
Tocompletethislesson
1. ChoosetheContinuebuttonontheDebugtoolbar.Respondtotheprompts.Thisrunstheprogramtocompletion.
2. ChooseDebug ClearAllBreakpointsfromtheVLISPmenu.Respond“yes”totheprompt.Thisremovesallthebreakpointswithinyourcode.Remember:youcanremoveindividualbreakpointsbypositioningthecursoratthebreakpointandchoosingtheToggleBreakpointbutton.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>UsingVisualLISPDebuggingTools>
WrappingUpLesson2
Inthislesson,you
Learnedaboutlocalandglobalvariables.
Setandremovedbreakpointsinaprogram.
Steppedthroughaprogramwhileitwasexecuting.
Watchedanddynamicallychangedthevalueofprogramvariablesduringexecution.
Sawhowlocalvariablesareresettonilafterthefunctionthatdefinedthemcompletesitsrun.
ThetoolsyoulearnedinthislessonwillbepartofyourdailyworkifyouintendtodevelopAutoLISPapplicationswithVLISP.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>
DrawingthePathBoundary
Inthislesson,youwillexpandyourprogramsoitactuallydrawssomethingwithinAutoCAD—thepolylineoutlineofthegardenpath.Todrawtheborder,youmustcreatesomeutilityfunctionsthatarenotspecifictoasingleapplicationbutaregeneralinnatureandmayberecycledforlateruse.Youwillalsolearnaboutwritingfunctionsthatacceptarguments—datathatispassedtothefunctionfromtheoutside—andwhytheuseofargumentsisapowerfulprogrammingconcept.Bytheendofthelesson,youwilldrawanAutoCADshapeparametrically,whichmeansdynamicallydrawingashapebasedontheuniquedataparametersprovidedbytheuser.
PlanningReusableUtilityFunctionsDrawingAutoCADEntitiesEnablingtheBoundaryOutlineDrawingFunctionWrappingUpLesson3
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>DrawingthePathBoundary>
PlanningReusableUtilityFunctions
Utilityfunctionsperformtaskscommontomanyapplicationsyouwillbewriting.Thesefunctionsformatoolkityoucanuseoverandoveragain.
Whenyoucreateafunctionaspartofatoolkit,spendsometimedocumentingitthoroughly.Inyourcomments,alsonotethefeaturesyouwouldliketoaddtothefunctioninthefuture,shouldtimepermit.
ConvertingDegreestoRadiansConverting3DPointsto2DPoints
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>DrawingthePathBoundary>PlanningReusableUtilityFunctions>
ConvertingDegreestoRadians
Youwillnowcreateafunctiontopreventyoufromrepetitivelytypinganequation.Itlookslikethis:
(defunDegrees->Radians(numberOfDegrees)
(*pi(/numberOfDegrees180.0)))
ThisfunctioniscalledDegrees->Radians.Thefunctionnameindicatesitspurpose.
Whydoyouneedafunctiontoconvertangularmeasurements?Behindthescenes,AutoCAD®usesradianangularmeasurementtokeeptrackofangles,whereasmostpeoplethinkintermsofdegrees.Thisfunctioninyourtoolkitallowsyoutothinkindegrees,andletsAutoLISP®convertthosenumberstoradians.
Totesttheutilityfunction
1. EnterthefollowingattheVLISPConsoleprompt:
(defunDegrees->Radians(numberOfDegrees)
(*pi(/numberOfDegrees180.0)))
2. EnterthefollowingattheVLISPConsoleprompt:
(degrees->radians180)
Thefunctionreturnsthenumber3.14159.Accordingtohowthisfunctionworks,180degreesisequivalentto3.14159radians.
Tousethisfunctionwithinyourprogram,simplycopythefunctiondefinitionfromtheConsolewindowintoyourgpmain.lspfile.Youcanpasteitanywhereinthefile,aslongasyoudonotpasteitintothemiddleofanexistingfunction.
Tocleanupyourwork,selectthetextyoujustpastedin,thenchoosetheFormatSelectionbutton;VLISPwillproperlyindentandformatthecode.
Next,addsomecommentsdescribingthefunction.Whenyouhavefullydocumentedthefunction,yourcodeshouldlooksomethinglikethis:
;;;--------------------------------------------------------------;
;;;Function:Degrees->Radians;
;;;--------------------------------------------------------------;
;;;Description:Thisfunctionconvertsanumberrepresentingan;
;;;angularmeasurementindegrees,intoitsradian;
;;;equivalent.Thereisnoerrorcheckingonthe;
;;;numberOfDegreesparameter--itisalways;
;;;expectedtobeavalidnumber.;
;;;--------------------------------------------------------------;
(defunDegrees->Radians(numberOfDegrees)
(*pi(/numberOfDegrees180.0))
)
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>DrawingthePathBoundary>PlanningReusableUtilityFunctions>
Converting3DPointsto2DPoints
Anotherusefulfunctioninthegardenpathprogramconverts3Dpointsto2Dpoints.AutoCADusuallyworkswith3Dcoordinates,butsomeentities,suchaslightweightpolylines,arealwaysmeanttobe2D.Thepointsreturnedbythegetpointfunctionare3D,soyouneedtocreateafunctiontoconvertthem.
Toconverta3Dpointtoa2Dpoint
1. EnterthefollowingattheConsolewindowprompt:
(defun3dPoint->2dPoint(3dpt)(list(car3dpt)(cadr3dpt)))
2. TestthefunctionbyenteringthefollowingattheConsoleprompt:
(3dpoint->2dpoint(list10200))
Thisworks,butthereisanotherconsiderationforthegardenpathapplication.Althoughitoftendoesn'tmatterwhetheranumberisanintegerorarealinLISPfunctions,thisisn'tthecasewithActiveXfunctions,whichyou'lluselaterinthislesson.ActiveXfunctionsrequirerealnumbers.Youcaneasilymodifythefunctiontoensureitreturnsrealsinsteadofintegers.
3. EnterthefollowingcodeattheConsoleprompt:
(defun3dPoint->2dPoint(3dpt)(list(float(car3dpt))
(float(cadr3dpt))))
4. Runthefunctionagain:
(3dpoint->2dpoint(list10200))
Noticethereturnvaluesarenowreals(indicatedbythedecimalvalues).
5. Testthefunctionagain,thistimeusingthegetpointfunction.EnterthefollowingattheConsoleprompt:
(setqmyPoint(getpoint))
6. PickapointintheAutoCADdrawingarea.Thegetpointfunctionreturnsa3Dpoint.
7. EnterthefollowingattheConsoleprompt:
(3dPoint->2DpointmyPoint)
Notethe2Dpointreturned.Nowaddthefunctiontothegpmain.lspfile,justasyoudidwithDegrees->Radians.Thenewcodeshouldlooklikethefollowing:
;;;--------------------------------------------------------------;
;;;Function:3dPoint->2dPoint;
;;;--------------------------------------------------------------;
;;;Description:Thisfunctiontakesoneparameterrepresentinga;
;;;3Dpoint(listofthreeintegersorreals),and;
;;;convertsitintoa2Dpoint(listoftworeals).;
;;;Thereisnoerrorcheckingonthe3Dpoint;
;;;parameter--itisassumedtobeavalidpoint.;
;;;--------------------------------------------------------------;
;;;Todo:Addsomekindofparametercheckingsothatthis;
;;;functionwon'tcrashaprogramifitispasseda;
;;;nullvalue,orsomeotherkindofdatatypethana;
;;;3Dpoint.;
;;;--------------------------------------------------------------;
(defun3dPoint->2dPoint(3dpt)
(list(float(car3dpt))(float(cadr3dpt)))
)
Notethatthefunctionheadingincludesacommentaboutsomeworkyoushoulddoonthisfunctioninthefuture.Ifyouwanttoearnsomeextracredit,thinkabouthowyouwouldgoaboutfoolproofingthisfunctionsothatinvaliddatadoesnotmakeitcrash.Hint:numberpandlistpfunctions…
(listp'(110))=>T
(numberp3.4)=>T
AutoLISPTutorial>DrawingthePathBoundary>
DrawingAutoCADEntities
MostAutoLISPprogramsdrawentitiesusingoneofseveralmethods:
ActiveXfunctions
Theentmakefunction
Thecommandfunction
ThislessonfocusesonentitycreationviaActiveX®.InLesson5,youwillimplementtheentmakeandAutoCADcommandalternatives.
CreatingEntitiesUsingActiveXFunctionsUsingentmaketoBuildEntitiesUsingtheAutoCADCommandLine
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>DrawingthePathBoundary>DrawingAutoCADEntities>
CreatingEntitiesUsingActiveXFunctions
ThenewestwayofcreatingentitiesisbyusingtheActiveXfunctionswithinVLISP.ActiveXhasseveraladvantagesoverentmakeandcommand.
ActiveXfunctionsarefaster.
ActiveXfunctionnamesindicatetheactiontheyperform,resultingineasierreadability,maintenance,andbug-fixing.
YouwillseeanexampleofanActiveXfunctionlaterinthislesson.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>DrawingthePathBoundary>DrawingAutoCADEntities>
UsingentmaketoBuildEntities
Theentmakefunctionallowsyoutobuildanentitybygatheringvaluesforthingssuchascoordinatelocationandorientation,layer,andcolorintoanassociationlist,thenaskingAutoCADtobuildtheentityforyou.Theassociationlistyoubuildfortheentmakefunctionlooksverymuchliketheassociationlistyougetbackwhenyoucalltheentgetfunction.Thedifferenceisthatentgetreturnsinformationaboutanentity,whileentmakebuildsanewentityfromrawdata.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>DrawingthePathBoundary>DrawingAutoCADEntities>
UsingtheAutoCADCommandLine
WhenAutoLISPfirstappearedinAutoCAD,theonlyavailablemeansforentitycreationwasthecommandfunction.ThisallowsanAutoLISPprogrammertocodejustaboutanycommandthatcanbeexecutedfromtheAutoCADCommandprompt.Thisisreliable,butitisnotasfastasActiveXmethodsanddoesnotprovidetheflexibilityofentmake.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>DrawingthePathBoundary>
EnablingtheBoundaryOutlineDrawingFunction
Afterthelastlesson,thegp:drawOutlinefunctionlookedlikethefollowing:
;;;--------------------------------------------------------------;
;;;Function:gp:drawOutline;
;;;--------------------------------------------------------------;
;;;Description:Thisfunctiondrawstheoutlineofthe;
;;;gardenpath.;
;;;--------------------------------------------------------------;
(defungp:drawOutline()
(alert
(strcat"Thisfunctionwilldrawtheoutlineofthepolyline"
"\nandreturnapolylineentityname/pointer."
)
)
;;Fornow,simplyreturnaquotedsymbol.Eventually,this
;;functionwillreturnanentitynameorpointer.
'SomeEname
)
Asitexists,thecodedoesnotdomuch.However,usingtheassociationlistinformationstoredinthevariablegp_PathData,youhaveenoughinformationtocalculatethepointsforthepathboundary.Younowhavetodeterminehowtopasstheinformationinthatvariabletogp:drawOutline.
Remembergp_PathDataisalocalvariabledefinedwithintheC:GPathfunction.InAutoLISP,localvariablesdeclaredinonefunctionarevisibletoanyfunctioncalledfromthatfunction(refertoDifferentiatingBetweenLocalandGlobalVariablesforclarification).Thegp:drawOutlinefunctioniscalledfromwithinC:GPath.Youcanrefertothegp_PathDatavariableingp:drawOutline,butthisisnotagoodprogrammingpractice.
Why?Whenthetwofunctionsusingthesamevariablearedefinedinthesamefile,asintheexamplesshownsofar,itisnottoodifficulttofigureoutwherethe
variableisdefinedandwhatitisusedfor.Butifthefunctionsaredefinedindifferentfiles—asisoftenthecase—youwouldhavetosearchthroughbothfilestofigureoutwhatgp_PathDatarepresents.
PassingParameterstoFunctionsWorkingwithanAssociationListUsingAnglesandSettingUpPointsUnderstandingtheActiveXCodeingp:drawOutlineEnsuringThatActiveXIsLoadedObtainingaPointertoModelSpaceConstructinganArrayofPolylinePointsConstructingaVariantfromaListofPointsPuttingItAllTogether
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>DrawingthePathBoundary>EnablingtheBoundaryOutlineDrawingFunction>
PassingParameterstoFunctions
Abetterwaytoconveyinformationfromonefunctiontoanotheristopassparameterstothecalledfunction.Designthefunctionsoitexpectstoreceiveanumberofvalues.RemembertheDegrees->Radiansfunction?ThisfunctionispassedaparameternamednumberOfDegrees:
(defunDegrees->Radians(numberOfDegrees)
(*pi(/numberOfDegrees180.0)))
Whenyoucallthefunction,itexpectsyoutopassitanumber.ThenumberwithinDegrees->RadiansisdeclaredastheparameternamednumberOfDegrees.Forexample:
_$(degrees->radians
90)1.5708
Inthiscase,thenumber90isassignedtotheparameternumberOfDegrees.
Youcanalsopassavariabletoafunction.Forexample,youmighthaveavariablecalledaDegreeValuethatcontainsthenumber90.ThefollowingcommandssetaDegreeValueandpassthevariabletoDegrees->Radians:
_$(setqaDegreeValue
90)90
_$(degrees->radians
aDegreeValue)1.5708
AutoLISPTutorial>DrawingthePathBoundary>EnablingtheBoundaryOutlineDrawingFunction>
WorkingwithanAssociationList
Youcanpasstheassociationlistinthegp_PathDatavariabletothegp:drawOutlinefunctionbyinvokingthefunctionasfollows:
(gp:drawOutlinegp_PathData)
Simpleenough,butyoualsoneedtofigureouthowtoprocesstheinformationstoredintheassociationlist.TheVLISPInspectfeaturecanhelpyoudeterminewhattodo.
TousetheVLISPInspectfeaturetoanalyzeyourassociationlist
1. Loadthecodethatisinthetexteditorwindow.
2. EnterthefollowingexpressionattheConsoleprompt:
(setqBoundaryData(gp:getPointInput))
VLISPwillstoretheinformationyouprovideinavariablenamedBoundaryData.
3. Respondtothepromptsforstartpoint,endpoint,andhalf-width.
4. SelecttheBoundaryDatavariablenameintheConsolewindowbydouble-clickingit.
5. ChooseView InspectfromtheVLISPmenu.
VLISPdisplaysawindowlikethefollowing:
TheInspectwindowshowsyoueachsublistwithintheBoundaryDatavariable.
6. EnterthefollowingattheVLISPConsoleprompt:
(assoc50BoundaryData)
Theassocfunctionreturnstheentryintheassociationlistthatisidentifiedbythespecifiedkey.Inthisexample,thespecifiedkeyis50;thisisassociatedwiththeangleofthegardenpath(seePuttingAssociationListstoUseforalistofthekey-valuepairsdefinedforthisapplication).
7. EnterthefollowingattheVLISPConsoleprompt:
(cdr(assoc50BoundaryData))
Thecdrfunctionreturnsthesecondelement,andanyremainingelementsafterthat,fromalist.Inthisexample,cdrretrievestheanglevalue,whichisthesecondandlastelementintheentryreturnedbytheassocfunction.Bythispoint,youshouldhavenotroubleunderstandingthefollowingcodefragment:
(setqPathAngle(cdr(assoc50BoundaryData))
Width(cdr(assoc40BoundaryData))
HalfWidth(/Width2.00)
StartPt(cdr(assoc10BoundaryData))
PathLength(cdr(assoc41BoundaryData))
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>DrawingthePathBoundary>EnablingtheBoundaryOutlineDrawingFunction>
UsingAnglesandSettingUpPoints
Therearestillacoupleofissuesremaining.First,youneedtofigureouthowtodrawthepathatanyangletheuserspecifies.Fromthegp:getPointInputfunction,youcaneasilyestablishtheprimaryangleofthepath.Todrawit,youneedacoupleofadditionalvectorsperpendiculartotheprimaryangle.
ThisiswheretheDegrees->Radiansfunctionisuseful.ThefollowingcodefragmentdemonstrateshowyoucansetupyourtwoperpendicularvectorsusingthePathAnglevariableasanargumentpassedtotheDegrees->Radiansfunction:
(setqangp90(+PathAngle(Degrees->Radians90))
angm90(-PathAngle(Degrees->Radians90)))
Withthedatayounowhaveinhand,youcanestablishthefourcornerpointsofthepathusingpolarfunction:
(setqp1(polarStartPtangm90HalfWidth)
p2(polarp1PathAnglePathLength)
p3(polarp2angp90Width)
p4(polarp3(+PathAngle(Degrees->Radians180))
Thepolarfunctionreturnsa3Dpointataspecifiedangleanddistancefromapoint.Forinstance,polarlocatesp2byprojectingp1adistanceofPathLengthalongavectororientedatanangleofPathAngle,counter-clockwisefromthex-axis.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>DrawingthePathBoundary>EnablingtheBoundaryOutlineDrawingFunction>
UnderstandingtheActiveXCodeingp:drawOutline
Thegp:drawOutlinefunctionissuesActiveXcallstodisplaythepath'spolylineborderinAutoCAD.ThefollowingcodefragmentusesActiveXtodrawtheborder:
;;AddpolylinetothemodelspaceusingActiveXautomation.
(setqpline(vla-addLightweightPolyline
*ModelSpace* ;GlobalDefinitionforModelSpace
VLADataPts ;verticesofpathboundary
);_endofvla-addLightweightPolyline
);_endofsetq
(vla-put-closedplineT)
Howdoyoumakesenseofthiscode?AnessentialresourceistheActiveXandVBAReference,whichdescribesthemethodsandpropertiesaccessibletoActiveXclientssuchasthisgardenpathapplication.The“WorkingwithActiveX”sectionoftheAutoLISPDeveloper'sGuideexplainshowtotranslatetheVBA™syntaxintheActiveXandVBAReferenceintoActiveXcallsinAutoLISPsyntax.
Forthemoment,though,youcangainarudimentaryunderstandingbyscrutinizingthepatternofthetwovla-callsintheprecedingexample.ThenamesofallAutoLISPActiveXfunctionsthatworkonAutoCADobjectsareprefixedwithvla-.Forexample,addLightweightPolylineisthenameofanActiveXmethod,andvla-addLightweightPolylineistheAutoLISPfunctionthatinvokesthismethod.Thevla-put-closedcallupdatestheclosedpropertyoftheplineobject,thepolylinedrawnbyvla-addLightweightPolyline.
TheAutomationobjectsthatfactorintoAutoLISPActiveXcallsabidebyafewstandardrules:
Thefirstargumenttoavla-put,vla-get,orvla-methodcallis
theobjectbeingmodifiedorqueried,forexample,*ModelSpace*inthefirstfunctioncallandplineinthesecondcall.
Thereturnvalueofavla-methodcallisaVLA-object,whichcanbeusedinsubsequentcalls.Forexample,vla-addLightweightPolylineyieldsareturnobject,pline,thatisalteredinthenextActiveXcall.
TheActiveXobjectmodelisstructuredhierarchically.Objectsaretraversedfromtheapplicationobjectatthetopmostleveldowntoindividualdrawingprimitives,suchaspolylineandcircleobjects.Thus,thegp:drawOutlinefunctionisnotyetcomplete,becausethe*ModelSpace*automationobjectmustfirstbeaccessedviatherootapplicationobject.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>DrawingthePathBoundary>EnablingtheBoundaryOutlineDrawingFunction>
EnsuringThatActiveXIsLoaded
ActiveXfunctionalityisnotautomaticallyenabledwhenyoustartAutoCADorVLISP,soyourprogramsmustensurethatActiveXisloaded.Thefollowingfunctioncallaccomplishesthis:
(vl-load-com)
IfActiveXsupportisnotyetavailable,executingvl-load-cominitializestheAutoLISPActiveXenvironment.IfActiveXisalreadyloaded,vl-load-comdoesnothing.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>DrawingthePathBoundary>EnablingtheBoundaryOutlineDrawingFunction>
ObtainingaPointertoModelSpace
WhenyouaddentitiesthroughActiveXfunctions,youneedtoidentifythemodelspaceorpaperspaceinwhichtheentityistobeinserted.(InActiveXterminology,entitiesareobjects,butthistutorialwillcontinueusingthetermentity.)TotellAutoCADwhichspacethenewentitiesshouldoccupy,youneedtoobtainapointertothatspace.Unfortunately,obtainingapointertomodelspaceisnotasimple,single-shotfunction.Thefollowingcodefragmentshowshowtheoperationneedstobesetup:
(vla-get-ModelSpace(vla-get-ActiveDocument
(vlax-get-Acad-Object)))
Workingfromtheinsideout,thevlax-get-Acad-ObjectfunctionretrievesapointertoAutoCAD.Thispointerispassedtothevla-get-ActiveDocumentfunction,whichretrievesapointertotheactivedrawing(document)withinAutoCAD.TheActiveDocumentpointeristhenpassedtothevla-get-ModelSpacefunctionthatretrievesapointertothemodelspaceofthecurrentdrawing.
Thisisnotthekindofexpressionyouwanttotypeoverandover.Forexample,lookathowmuchmorecomplicatedthecodeforaddingapolylineusingActiveXappearswhentheentiremodelspaceexpressionisused:
(setqpline(vla-addLightweightPolyline
(vla-get-ModelSpace
(vla-get-ActiveDocument
(vlax-get-Acad-Object)
)
)
VLADataPts)
)
(vla-put-closedplineT)
Thefunctionisdefinitelylessunderstandable.Notonlythat,butwithineveryexpressionwithinyourprogramwhereanentityiscreated,yourepeatthesamesetofnestedfunctions.Thisdemonstratesoneofthefewexcellentusesforglobalvariables.Thegardenpathapplicationcanaddalotofentitiestomodelspace(thinkofallthetilesinthepath),so,setupaglobalvariabletostorethepointertothemodelspace,asinthefollowingcode:
(setq*ModelSpace*(vla-get-ModelSpace(vla-get-ActiveDocument
(vlax-get-Acad-Object))))
Youcanusethevariable*ModelSpace*anytimeyoucallanActiveXentitycreationfunction.Theonlytrickythingwiththisschemeisthe*ModelSpace*variablemustbereadytogobeforeyoustartdrawing.Forthisreason,thesetqestablishingthisvariablewillbecalledatthetimetheapplicationisloaded,immediatelyafterthecalltovl-load-com.Thesecallswillbeplacedbeforeanydefunintheprogramfile.Asaresult,theyareexecutedassoonasthefileisloaded.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>DrawingthePathBoundary>EnablingtheBoundaryOutlineDrawingFunction>
ConstructinganArrayofPolylinePoints
Thelastissuetodealwithishowtotransformtheindividualpointvariables—p1,p2,p3,andp4—intotheformatrequiredforthevla-addLightweightpolylinefunction.First,getsomehelponthetopic.
Toobtaininformationonafunction
1. ChoosetheHelpbuttonontheVLISPtoolbar.
2. Entervla-addLightweightpolylineintheEnterItemNamedialogbox,andchooseOK.(TheHelpsystemisnotcasesensitive,sodonotworryabouthowyoucapitalizethefunctionname.)
HelpstatesthatAddLightWeightPolylinerequiresyoutospecifythepolylineverticesasanarrayofdoublesintheformofavariant.HereishowHelpdescribesthisparameter:
Thearrayof2DWCScoordinatesspecifyingtheverticesofthepolyline.Atleasttwopoints(fourelements)arerequiredforconstructingalightweightpolyline.Thearraysizemustbeamultipleof2.
AvariantisanActiveXconstructthatservesasacontainerforvarioustypesofdata.Strings,integers,andarrayscanallberepresentedbyvariants.Thevariantstoresdataalongwiththeinformationidentifyingthedata.
Sofar,youhavefourpoints,eachintheformat(x,y,z).Thechallengeistoconvertthesefourpointsintoalistofthefollowingform:
(x1y1x2y2x3y3x4y4)
Theappendfunctiontakesmultiplelistsandconcatenatesthem.TocreatealistofthefourpointsintheproperformatfortheActiveXfunction,youcanuse
thefollowingexpression:
(setqpolypoints(append(3dPoint->2dPointp1)
(3dPoint->2dPointp2)
(3dPoint->2dPointp3)
(3dPoint->2dPointp4)))
Writingthe3dPoint->2dPointfunctionfourtimesisabitcumbersome.Youcanreducethecodefurtherbyusingthemapcarandapplyfunctions.Whenselected,mapcarexecutesafunctiononindividualelementsinoneormorelists,andapplypassesalistofargumentstothespecifiedfunction.Theresultingcodelookslikethefollowing:
(setqpolypoints(apply'append(mapcar'3dPoint->2dPoint
(listp1p2p3p4))))
Beforethecalltomapcar,thelistofpointsisinthisform:
((x1y1z1)(x2y2z2)(x3y3z3)(x4y4z4))
Aftermapcaryouhavealistofpointsinthefollowingform:
((x1y1)(x2y2)(x3y3)(x4y4))
Andfinally,afterapplyingtheappendfunctiononthelistreturnedfrommapcar,youendupwiththefollowing:
(x1y1x2y2x3y3x4y4)
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>DrawingthePathBoundary>EnablingtheBoundaryOutlineDrawingFunction>
ConstructingaVariantfromaListofPoints
Sofar,thedatainthepolypointsvariableisinalistformatsuitableformanyAutoLISPcalls.However,thedataistobesuppliedasaninputparametertoanActiveXcallthatexpectsavariantarrayofdoubles.Youcanuseanotherutilityfunctiontomaketherequiredconversionfromlisttovariant:
(defungp:list->variantArray(ptsList/arraySpacesArray)
;allocatespaceforanarrayof2dpointsstoredasdoubles
(setqarraySpace(vlax-make-safearray
vlax-vbdouble;elementtype
(cons0
(-(lengthptsList)1)
);arraydimension
)
)
(setqsArray(vlax-safearray-fillarraySpaceptsList))
;returnarrayvariant
(vlax-make-variantsArray)
)
Thefollowingactionstakeplaceingp:list->variantArray:
Thevlax-make-safearrayfunctioniscalledtoallocateanarrayofdoubles(vlax-vbdouble).Thevlax-make-safearrayfunctionalsorequiresyoutospecifythelowerandupperindexboundariesofthearray.Ingp:list->variantArray,thecalltovlax-make-safearrayspecifiesastartindexof0andsetstheupperlimittoonelessthanthenumberofelementspassedtoit(ptsList).
Thevlax-safearray-fillfunctioniscalledtopopulatethearraywiththeelementsinthepointlist.
Thevlax-make-variantiscalledtoconvertthesafearrayintoa
variant.Asthelastfunctioncallingp:list->variantArray,thereturnvalueispassedtothecallingfunction.
Thefollowingisanexampleofafunctioncallthatinvokesgp:list->variantArraytoconvertalisttoavariantarrayofdoubles:
;dataconversionfromlisttovariant
(setqVLADataPts(gp:list->variantArraypolypoints))
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>DrawingthePathBoundary>EnablingtheBoundaryOutlineDrawingFunction>
PuttingItAllTogether
Younowhaveallthecodeyouneedtodrawtheoutlineofthegardenpath.
Toupdateyourcode
1. Replaceyouroldcodeforthegp:drawOutlinefunctionwiththefollowing:
;;;---------------------------------------------------------------
;;;Function:gp:drawOutline
;;;---------------------------------------------------------------
;;;Description:Thisfunctionwilldrawtheoutlineofthegarden
;;;path.
;;;---------------------------------------------------------------
;;;Note:Noerrorcheckingorvalidationisperformedonthe
;;;BoundaryDataparameter.Thesequenceofitemswithinthis
;;;parameterdoesnotmatter,butitisassumedthatallsublists
;;;arepresentandcontainvaliddata.
;;;--------------------------------------------------------------
(defungp:drawOutline(BoundaryData/VLADataPtsPathAngle
WidthHalfWidthStartPtPathLength
angm90angp90p1p2
p3p4polypointspline
)
;;extractthevaluesfromthelistBoundaryData
(setqPathAngle(cdr(assoc50BoundaryData))
Width(cdr(assoc40BoundaryData))
HalfWidth(/Width2.00)
StartPt(cdr(assoc10BoundaryData))
PathLength(cdr(assoc41BoundaryData))
angp90(+PathAngle(Degrees->Radians90))
angm90(-PathAngle(Degrees->Radians90))
p1(polarStartPtangm90HalfWidth)
p2(polarp1PathAnglePathLength)
p3(polarp2angp90Width)
p4(polarp3(+PathAngle(Degrees->Radians180))PathLength)
polypoints(apply'append
(mapcar'3dPoint->2dPoint(listp1p2p3p4))
)
)
;;*****dataconversion*****
;;Notice,polypointsisinAutoLISPformat,consistingofalist
;;ofthe4cornerpointsforthegardenpath.
;;Thevariableneedstobeconvertedtoaformofinputparameter
;;acceptabletoActiveXcalls.
(setqVLADataPts(gp:list->variantArraypolypoints))
;;AddpolylinetothemodelspaceusingActiveXautomation.
(setqpline(vla-addLightweightPolyline
*ModelSpace*;GlobalDefinitionforModelSpace
VLADataPts
);_endofvla-addLightweightPolyline
);_endofsetq
(vla-put-closedplineT)
;;ReturntheActiveXobjectnamefortheoutlinepolyline
;;Thereturnvalueshouldlooksomethinglikethis:
;;#<VLA-OBJECTIAcadLWPolyline02351a34>
pline
);_endofdefun
Notethatgp:drawOutlinenowreturnsthevariablepline,notthequotedsymbol'SomeEnameusedinthestubbed-outversionofthefunction.
2. FormatthecodeyoujustenteredbyselectingitandchoosingtheFormatSelectionbuttonontheVLISPtoolbar.
3. EnableActiveXandaddtheglobalvariableassignmentforthepointertomodelspace,asdescribedearlier.Scrolltothetopofthetexteditorwindowandaddthefollowingcodebeforethefirstdefun:
;;;--------------------------------------------------------------
;;;FirststepistoloadActiveXfunctionality.IfActiveXsupport
;;;alreadyexistsindocument(canoccurwhenBonustoolshavebeen
;;;loadedintoAutoCAD),nothinghappens.Otherwise,ActiveX
;;;supportisloaded.
;;;---------------------------------------------------------------
(vl-load-com)
;;;InLesson4,thefollowingcommentandcodeismovedtoutils.lsp
;;;---------------------------------------------------------------
;;;ForActiveXfunctions,weneedtodefineaglobalvariablethat
;;;"points"totheModelSpaceportionoftheactivedrawing.This
;;;variable,named*ModelSpace*willbecreatedatloadtime.
;;;---------------------------------------------------------------
(setq*ModelSpace*
(vla-get-ModelSpace
(vla-get-ActiveDocument(vlax-get-acad-object))
);_endofvla-get-ModelSpace
);_endofsetq
Notehowtheabovecodelivesoutsideofanydefun.Becauseofthis,VLISPautomaticallyexecutesthecodeatthetimeyouloadthefile.
4. LookforthefollowinglineintheC:GPathfunction:
(setqPolylineName(gp:drawOutline))
Changeittothefollowing:
(setqPolylineName(gp:drawOutlinegp_PathData))
Thegp:drawOutlinefunctionisnowexpectingaparameter—thelistcontainingthepolylineboundarydata—andthischangefulfillsthatrequirement.
5. Addthegp:list->variantArrayfunctionshowninConstructingaVariantfromaListofPointstotheendofgpmain.lsp.Tryloadingandrunningtherevisedprogram.VLISPtakescontrolawayfromAutoCADbeforeyouseetheendresult,soswitchbacktotheAutoCADwindowaftercontrolreturnstoVLISP.Iftheprogramrancorrectly,youshouldseeaborderforthegardenpath.Ifyoufinderrors,debugthecodeandtryagain.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>DrawingthePathBoundary>
WrappingUpLesson3
Inthislesson,you
Wroteutilityfunctionsthatcanbereusedinotherapplications.
Addedentitycreationlogictoyourprogram.
LearnedhowtouseActiveXfunctions.
Learnedhowtoworkwithassociationlists.
Enabledyourprogramtodrawagardenpathborder.
Ifyou'reconfusedaboutanythingfromthislesson,itisrecommendedyougothroughitonceagainbeforemovingontoLesson4.(Ifyoudecidetodoso,copythecompletedcodefromyourLesson2directorysothatyoustartthelessonfromthecorrectplace.)Andifallelsefails,youcanalwayscopythecodefromtheTutorial\VisualLISP\Lesson3directory.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>
CreatingaProjectandAddingtheInterface
Inthislesson,youwillaccomplishtwomajortasks:creatingaVisualLISP®projectandaddingadialog-basedinterfacetoyourapplication.Intheprocess,youwillsplitthesingleAutoLISP®fileyouworkedwithsofar(gpmain.lsp)intoseveralsmallerfiles,reinforcingtheconceptofcodemodularity.
Fromthislessonon,thetutorialprovidesmoregeneraldescriptionsofthetasksyouneedtoperform,unlessnewtopicsarecovered.Also,thecodefragmentswillbeminimallydocumentedtosavespace.
ModularizingYourCodeUsingVisualLISPProjectsAddingtheDialogBoxInterfaceInteractingwiththeDialogBoxfromAutoLISPCodeProvidingaChoiceofBoundaryLineTypeCleaningUpRunningtheApplicationWrappingUpLesson4
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>CreatingaProjectandAddingtheInterface>
ModularizingYourCode
AsaresultoftheworkyoudidinLesson3,yourgpmain.lspfilewasgettingratherlarge.ThisisnotaproblemforVLISP,butitiseasiertomaintainthecodeifyousplitthingsupintofilescontaininglogicallyrelatedfunctions.It'salsoeasiertodebugyourcode.Forexample,ifyouhaveasinglefilewith150functions,asinglemissingparenthesiscanbedifficulttofind.
Inthetutorial,thefileswillbeorganizedasfollows:
Tutorialfileorganization
Filename Contents
gp-io.lsp Allinputandoutput(I/O)functions)suchasgettinguserinput.AlsocontainstheAutoLISPcoderequiredforthedialogboxinterfaceyouwillbeadding.
utils.lsp Includesallgenericfunctionsthatcanbeusedagainonotherprojects.Alsocontainsload-timeinitializations.
gpdraw.lsp Alldrawingroutines—thecodethatactuallycreatestheAutoCADentities.
gpmain.lsp ThebasicC:GPathfunction.
Tosplitgpmain.lspintofourfiles
1. Createanewfile,thencutandpastethefollowingfunctionsfromgpmain.lspintothenewfile:
gp:getPointInput
gp:getDialogInput
Savethenewfileinyourworkingdirectoryasgp-io.lsp.
2. Createanewfile,thencutandpastethefollowingfunctionsfromgpmain.lspintothenewfile:
Degrees->Radians
3Dpoint->2Dpoint
gp:list->variantArray
Also,atthebeginningofthefile,insertthelinesofcodetoestablishActiveXfunctionality(vl-load-com)andcommitglobalvariableassignment(*ModelSpace*).Savethefileasutils.lsp.
3. Createanewfile,thencutandpastethefollowingfunctionfromgpmain.lspintothenewfile:
gp:drawOutline
Savethisfileasgpdraw.lsp.
4. Afterstrippingthecodeoutofgpmain.lsp,saveitandcheckit.Onlytheoriginalfunction,C:GPath,shouldremaininthefile.
YourVLISPdesktopisstartingtogetcrowded.YoucanminimizeanywindowwithinVLISPanditstaysaccessible.ChoosetheSelectWindowbuttononthetoolbartochooseawindowfromalist,orchooseWindowfromtheVLISPmenuandselectawindowtoview.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>CreatingaProjectandAddingtheInterface>
UsingVisualLISPProjects
TheVLISPprojectfeatureprovidesaconvenientwaytomanagethefilesthatmakeupyourapplication.Andwiththeprojectfeature,youcanopenasingleprojectfileinsteadofindividuallyopeningeveryLISPfileintheapplication.Oncetheprojectisopen,gettingtoitsconstituentfilesisadouble-clickaway.
TocreateaVLISPproject
1. ChooseProject NewProjectfromtheVLISPmenu.
2. SavethefileinyourLesson4directory,usingthenamegpath.prj.Afteryousavethefile,VLISPdisplaystheProjectPropertiesdialogbox.
3. Choosethe[Un]SelectAllbuttonontheleftintheProjectPropertiesdialogbox.
4. Choosethebuttoncontaininganarrowpointingtotheright.Thisaddsalltheselectedfilestoyourproject.
IntheProjectPropertiesdialogbox,thelistboxontheleftshowsallLISPfilesthatresideinthesamedirectoryasyourprojectfileandarenotincludedinthatproject.Thelistboxontherightlistsallthefilesthatmakeuptheproject.Whenyouaddtheselectedfilestotheproject,thosefilenamesmovefromtheleftboxtotherightbox.
5. Inthelistboxontherightsideofthedialogbox,selectgpmain,thenchoosetheBottombutton.Thismovesthefiletothebottomofthelist.VLISPloadsprojectfilesintheordertheyarelisted.Becausethepromptthattellsusersthenameofthecommandislocatedattheendofthegpmain.lspfile,youneedtomovethisfiletothebottomofthelist.
Loadingthisfilelastresultsinthepromptdisplayedtousers.Theutils.lspfileshouldbeloadedfirstbecauseitcontainsinitializationcodefortheapplication.Therefore,selectutilsinthedialog'slistboxandchoosetheTopbutton.
6. ChooseOK.
VLISPaddsasmallprojectwindowtoyourVLISPdesktop.Thewindowliststhefilesinyourproject.Double-clickonanyfiletoopenthefileintheVLISPtexteditor(ifitisnotalreadyopen)andmakeittheactiveeditorwindow.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>CreatingaProjectandAddingtheInterface>
AddingtheDialogBoxInterface
Thenextpartofthislessonconcernsaddingadialogboxinterfacetothegardenpathapplication.Todothis,youwillbeworkingwithanotherlanguage,dialogcontrollanguage(DCL).
Currently,yourgpathfunctiononlyacceptsinputonthecommandline.Youincludedastubbed-outfunction(gp:getDialogInput)withtheintentionofaddingadialogboxinterface.Nowisthetimetoaddtheinterface.
Therearetwostepsincreatingafunctionaldialoginterface:
Definetheappearanceandcontentsofthedialogboxes.
Addprogramcodetocontroldialogbehavior.
Thedescriptionandformatofadialogboxisdefinedina.dclfile.IntheAutoLISPDeveloper'sGuide,DCLisdescribedinchapter11,“DesigningDialogBoxes”chapter12,“ManagingDialogBoxes”,andchapter13,“ProgrammableDialogBoxReference”.
Programcodethatinitializesdefaultsettingsandrespondstouserinteractionwillbeaddedtogp:getDialogInput.
DefiningtheDialogBoxwithDCLSavingaDCLFilePreviewingaDialogBox
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>CreatingaProjectandAddingtheInterface>AddingtheDialogBoxInterface>
DefiningtheDialogBoxwithDCL
Beginbytakingalookatthedialogboxyouneedtocreate.
Thedialogboxcontainsthefollowingelements:
Twosetsofradiobuttons.Onesetofbuttonsdeterminesthepolylinestyleoftheboundary,andtheothersetofbuttonsspecifiesthetileentitycreationmethod(ActiveX,entmake,orcommand).Onlyoneradiobuttoninasetcanbeselectedatonetime.
Editboxesforspecifyingtheradiusoftilesandthespacingbetweentiles.
AstandardsetofOKandCancelbuttons.
DialogboxcomponentsarereferredtoastilesinDCL.WritingthecompletecontentsofadialogboxDCLfilemayseemoverwhelming.Thetrickisto
sketchoutwhatyouwant,breakitdownintosections,thenwriteeachsection.
Todefinethedialogbox
1. OpenanewfileintheVLISPtexteditorwindow.
2. Enterthefollowingstatementinthenewfile:
label="GardenPathTileSpecifications";
ThisDCLstatementdefinesthetitleofthedialogboxwindow.
3. Definetheradiobuttonsforspecifyingpolylinetypebyaddingthefollowingcode:
:boxed_radio_column{//definestheradiobuttonareas
label="OutlinePolylineType";
:radio_button{//definesthelightweightradiobutton
label="&Lightweight";
key="gp_lw";
value="1";
}
:radio_button{//definestheold-stylepolylineradiobutton
label="&Old-style";
key="gp_hw";
}
}
Theboxed_radio_columnDCLdirectivedefinesaboxboundaryandallowsyoutospecifyalabelforthesetofbuttons.Withintheboundary,youspecifytheradiobuttonsyouneedbyaddingradio_buttondirectives.Eachradiobuttonrequiresalabelandakey.ThekeyisthenamebywhichyourAutoLISPcodecanrefertothebutton.Noticethattheradiobuttonlabeled“lightweight”isgivenavalueof1.Avalueof1(astring,notaninteger)assignedtoabuttonmakesitthedefaultchoiceinarowofbuttons.Inotherwords,whenyoufirstdisplaythedialog,thisbuttonwillbeselected.AlsonoticethatinDCLfiles,double-slashcharacters,notsemicolonsasinAutoLISP,indicateacomment.
4. Definetheradiocolumnfortheselectionoftheentitycreationstylebyaddingthefollowingcode:
:boxed_radio_column{//definestheradiobuttonareas
label="TileCreationMethod";
:radio_button{//definestheActiveXradiobutton
label="&ActiveXAutomation";
key="gp_actx";
value="1";
}
:radio_button{//definesthe(entmake)radiobutton
label="&Entmake";
key="gp_emake";
}
:radio_button{//definesthe(command)radiobutton
label="&Command";
key="gp_cmd";
}
}
5. Addthefollowingcodetodefinetheeditboxtilesthatallowuserstoenterthenumbersspecifyingtilesizeandspacing:
:edit_box{//definestheRadiusofTileeditbox
label="&Radiusoftile";
key="gp_trad";
edit_width=6;
}
:edit_box{//definestheSpacingBetweenTileseditbox
label="S&pacingbetweentiles";
key="gp_spac";
edit_width=6;
}
Noticethatthisdefinitiondoesnotsetanyinitialvaluesfortheeditboxes.YouwillsetdefaultvaluesforeacheditboxinyourAutoLISPprogram.
6. AddthefollowingcodefortheOKandCancelbuttons:
:row{//definestheOK/Cancelbuttonrow
:spacer{width=1;}
:button{//definestheOKbutton
label="OK";
is_default=true;
key="accept";
width=8;
fixed_width=true;
}
:button{//definestheCancelbutton
label="Cancel";
is_cancel=true;
key="cancel";
width=8;
fixed_width=true;
}
:spacer{width=1;}
}
Bothbuttonsaredefinedwithinarow,sotheylineuphorizontally.
7. ScrolltothebeginningofthetexteditorwindowandinsertthefollowingstatementasthefirstlineinyourDCL:
gp_mainDialog:dialog{
8. Thedialogdirectiverequiresaclosingbrace,soscrolltotheendofthefileandaddthebraceasthelastlineofDCLcode:
}
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>CreatingaProjectandAddingtheInterface>AddingtheDialogBoxInterface>
SavingaDCLFile
BeforesavingthefilecontainingyourDCL,considerthefactthatAutoCAD®
mustbeabletolocateyourDCLfileduringruntime.Forthisreason,thefilemustbeplacedinoneoftheAutoCADSupportFileSearchPathlocations.(Ifyouareunsureabouttheselocations,chooseTools OptionsfromtheAutoCADmenuandexaminetheSupportFileSearchPathlocationsundertheFilestab.)
Fornow,youcansavethefileintheAutoCADSupportdirectory.
TosaveyourDCLfile
1. ChooseFile SaveAsfromtheVLISPmenu.
2. IntheSaveAsTypefieldoftheSaveAsdialogbox,chooseDCLSourceFilesfromthepull-downmenu.
3. ChangetheSaveInpathto<AutoCADdirectory>\Support.
4. Enterthefilenamegpdialog.dcl.
5. ChooseSave.
NoticeVLISPchangesthesyntaxcoloringschemeafteryousavethefile.VLISPisdesignedtorecognizeDCLfilesandhighlightthedifferenttypesofsyntacticalelements.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>CreatingaProjectandAddingtheInterface>AddingtheDialogBoxInterface>
PreviewingaDialogBox
VLISPprovidesapreviewfeatureforcheckingtheresultsofyourDCLcoding.
TopreviewadialogboxdefinedwithDCL
1. ChooseTools InterfaceTools PreviewDCLinEditorfromtheVLISPmenu.
2. ChooseOKwhenpromptedtospecifyadialogname.Inthiscase,yourDCLfiledefinesjustasingledialogbox,sothereisnochoicetobemade.Asyoucreatelargerandmorerobustapplications,however,youmayendupwithDCLfilescontainingmultipledialogboxes.Thisiswhereyoucanselectwhichonetopreview.
3. Ifthedialogboxdisplayssuccessfully,chooseanybuttontoendthedialog.
VLISPpassescontroltoAutoCADtodisplaythedialogbox.IfAutoCADfindssyntacticalerrors,itdisplaysoneormoremessagewindowsidentifyingtheerrors.
IfAutoCADdetectsDCLerrorsandyouareunabletofigureouthowtofixthem,copythegpdialog.dclfileinyourTutorial\VisualLISP\Lesson4directoryandsaveitintheSupportdirectory.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>CreatingaProjectandAddingtheInterface>
InteractingwiththeDialogBoxfromAutoLISPCode
YounowneedtoprogramyourAutoLISPfunctiontointeractwiththedialogbox.Thestubbed-outfunction,gp:getDialogInput,iswherethisactivitywilltakeplace.Thisfunctionnowlivesinthegp-io.lspfile,whichyouearlierextractedfromgpmain.lsp.
Developingadialogboxinterfacecanbeconfusingthefirstfewtimesyoudoit.Itinvolvesplanningaheadandaskingyourselfsuchquestionsas:
Doesthedialogboxneedtobesetupwithdefaultvalues?
Whathappenswhentheuserchoosesabuttonorentersavalue?
WhathappenswhentheuserchoosesCancel?
Ifthedialog(.dcl)fileismissing,whatneedstooccur?SettingUpDialogValuesLoadingtheDialogFileLoadingaSpecificDialogintoMemoryInitializingtheDefaultDialogValuesAssigningActionstoTilesStartingtheDialogUnloadingtheDialogDeterminingWhattoDoNextPuttingtheCodeTogetherUpdatingaStubbed-OutFunction
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>CreatingaProjectandAddingtheInterface>InteractingwiththeDialogBoxfromAutoLISPCode>
SettingUpDialogValues
Whenyourunthecompletegardenpathapplication,noticethatthedialogboxalwaysstartsupwithActiveXasthedefaultobjectcreationmethodandLightweightasthepolylinestyle.Somethingmoreinterestingoccurswiththedefaulttilesize—thevalueschangedependingonthewidthofthepath.Thefollowingcodefragmentshowshowtosetupthedefaultvaluestobedisplayedinthedialogbox:
(setqobjectCreateMethod"ACTIVEX"
plineStyle"LIGHT"
tilerad(/pathWidth15.0)
tilespace(/tilerad5.0)
dialogLoadedT
dialogShowT
);_endofsetq
Forthemoment,don'tworryaboutwhatpurposethedialogLoadedanddialogShowvariablesserve.Thisbecomesapparentinthenexttwosections.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>CreatingaProjectandAddingtheInterface>InteractingwiththeDialogBoxfromAutoLISPCode>
LoadingtheDialogFile
YourprogramfirstneedstoloadtheDCLfileusingtheload_dialogfunction.ThisfunctionsearchesfordialogfilesaccordingtotheAutoCADsupportfilesearchpath,unlessyouspecifyafullpathname.
Foreveryload_dialogfunctionthereshouldbeacorrespondingunload_dialogfunctionlaterinthecode.Youwillseethisinamoment.Fornow,takealookathowyouneedtoloadinyourdialog:
;;Loadthedialogbox.Setuperrorcheckingtomakesure
;;thedialogfileisloadedbeforecontinuing
(if(=-1(setqdcl_id(load_dialog"gpdialog.dcl")))
(progn
;;There'saproblem-displayamessageandsetthe
;;dialogLoadedflagtonil
(princ"\nCannotloadgpdialog.dcl")
(setqdialogLoadednil)
);_endofprogn
);_endofif
ThedialogLoadedvariableindicatesifthedialogloadedsuccessfully.Inthecodewhereyousetuptheinitialvaluesforthedialogbox,yousetdialogLoadedtoaninitialvalueofT.Asyoucanseeinthecodefragmentabove,dialogLoadedissettonilifthereisaproblemwiththeload.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>CreatingaProjectandAddingtheInterface>InteractingwiththeDialogBoxfromAutoLISPCode>
LoadingaSpecificDialogintoMemory
ItwasnotedearlierthatasingleDCLfilemaycontainmultipledialogboxdefinitions.Thenextstepinusingadialogistospecifywhichdialogboxdefinitiontodisplay.Thefollowingcodedemonstratesthis:
(if(anddialogLoaded
(not(new_dialog"gp_mainDialog"dcl_id))
);_endofand
(progn
;;There'saproblem...
(princ"\nCannotshowdialoggp_mainDialog")
(setqdialogShownil)
);_endofprogn
);_endofif
Noticehowtheandfunctionisusedtotestifthedialogwasloadedandifthecalltonew_dialogwassuccessful.Iftherearemultipleexpressionsevaluatedwithinanandfunctioncall,evaluationofsubsequentexpressionsisterminatedwiththefirstexpressionthatevaluatestonil.Inthiscase,ifthedialogLoadedflagisnil(meaningtheloadfunctionintheprevioussectionfailed),VLISPdoesnotattempttoperformthenew_dialogfunction.
NoticethatthecodealsoaccountsforthepossibilitythatsomethingmightnotbeworkingproperlywiththeDCLfile,andsetsthedialogShowvariabletonilifthatisthecase.
Thenew_dialogfunctionsimplyloadsthedialogintomemory—itdoesnotdisplayit.Thestart_dialogfunctiondisplaysthedialogbox.Alldialogboxinitialization,suchassettingtilevalues,creatingimagesorlistsforlistboxes,andassociatingactionswithspecifictilesmusttakeplaceafterthenew_dialogcallandbeforethestart_dialogcall.
AutoLISPTutorial>CreatingaProjectandAddingtheInterface>InteractingwiththeDialogBoxfromAutoLISPCode>
InitializingtheDefaultDialogValues
Ifeverythingworkedsuccessfullyinloadingthedialog,youarereadytostartsettingupthevaluesthatwillbedisplayedtousers.AsuccessfulloadisindicatediftheflagvariablesdialogLoadedanddialogShowarebothT(true).
Nowsettheinitialvaluesforthetileradiusandspacing.Theset_tilefunctionassignsavaluetoatile.Aneditboxdealswithstringsratherthannumbers,soyouneedtousethertos(convertRealTOString)functiontoconvertyourtilesizevariablevaluesintostringsindecimalformatwithaprecisionoftwodigits.Thefollowingcodehandlesthisconversion:
(if(anddialogLoadeddialogShow)
(progn
;;Settheinitialstateofthetiles
(set_tile"gp_trad"(rtostileRad22))
(set_tile"gp_spac"(rtostileSpace22))
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>CreatingaProjectandAddingtheInterface>InteractingwiththeDialogBoxfromAutoLISPCode>
AssigningActionstoTiles
ADCLdefinitiondoesnothingmorethandefinealifelessdialogbox.YouconnectthislifelessdialogboxtoyourdynamicAutoLISPcodewiththeaction_tilefunction,asdemonstratedbythefollowingcode:
;;Assignactions(thefunctionstobeinvoked)todialogbuttons
(action_tile
"gp_lw"
"(setqplineStyle\"Light\")"
)
(action_tile
"gp_hw"
"(setqplineStyle\"Pline\")"
)
(action_tile
"gp_actx"
"(setqobjectCreateMethod\"ActiveX\")"
)
(action_tile
"gp_emake"
"(setqobjectCreateMethod\"Entmake\")"
)
(action_tile
"gp_cmd"
"(setqobjectCreateMethod\"Command\")"
)
(action_tile"cancel""(done_dialog)(setqUserClicknil)")
(action_tile
"accept"
(strcat"(progn(setqtileRad(atof(get_tile\"gp_trad\")))"
"(setqtileSpace(atof(get_tile\"gp_spac\")))"
"(done_dialog)(setqUserClickT))"
)
)
NoticeallthequotesaroundtheAutoLISPcode.WhenyouwriteanAutoLISPaction_tilefunction,yourcodeisessentiallytellingatile,“here,remember
thisstring,thenpassitbacktomewhentheuserselectsyou.”Thestring(anythingwithindouble-quotationmarks)isdormantuntiltheuserselectsthetile.Atthattime,thetilepassesthestringtoAutoCAD,whichconvertsthestringintofunctioningAutoLISPcodeandexecutesthecode.
Forexample,considerthefollowingaction_tileexpression,whichisconnectedtothelightweightpolylineradiobutton:
(action_tile
"gp_lw"
"(setqplineStyle\"Light\")"
)
Thecodeassignsthestring"(setqplineStyle\"Light\")"totheradiobutton.Whenauserpicksthebutton,thestringispassedbacktoAutoCADandtransformeddirectlyintothefollowingAutoLISPexpression:
(setqplineStyle"Light")
Lookatonemorecodefragment.Thefollowingistheaction_tileexpressionassignedtotheOKbutton:
(action_tile
"accept"
(strcat"(progn(setqtileRad(atof(get_tile\"gp_trad\")))"
"(setqtileSpace(atof(get_tile\"gp_spac\")))"
"(done_dialog)(setqUserClickT))"
)
WhenauserchoosestheOKbutton,thelengthystringassignedtothebuttonispassedtoAutoCADandturnedintothefollowingAutoLISPcode:
(progn
(setqtileRad(atof(get_tile"gp_trad")))
(setqtileSpace(atof(get_tile"gp_spac")))
(done_dialog)
(setqUserClickT)
)
Thiscodedoesseveralthings:Itretrievesthecurrentvaluesfromthetileswhosekeyvaluesaregp_trad(thetileradius)andgp_spac(thetilespacingvalue).Thenatofconvertsthenumberstringintoarealnumber.Thedialogisterminatedwiththedone_dialogfunction,andavalueofT,ortrue,is
assignedtothevariableUserClick.
You'redoneassigningactionstothebuttons.Thenextthingtodoistoputitallinmotion.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>CreatingaProjectandAddingtheInterface>InteractingwiththeDialogBoxfromAutoLISPCode>
StartingtheDialog
Thestart_dialogfunctiondisplaysadialogboxandacceptsuserinput.Thestart_dialogfunctionrequiresnoarguments.
(start_dialog)
Controlpassestouserswhenyouissuestart_dialog.Userscanmakechoiceswithinthedialogbox,untiltheychoosetheOKorCancelbuttons.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>CreatingaProjectandAddingtheInterface>InteractingwiththeDialogBoxfromAutoLISPCode>
UnloadingtheDialog
WhenauserchoosestheOKorCancelbutton,youneedtounloadthedialog.Likestart_dialog,unload_dialogisanothersimplefunction.
(unload_dialogdcl_id)
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>CreatingaProjectandAddingtheInterface>InteractingwiththeDialogBoxfromAutoLISPCode>
DeterminingWhattoDoNext
IftheuserchoseOK,youmustbuildalistcontainingthevaluessetbytheuser'sinteractionwiththedialog.Thislistiswhatgp:getDialogInputwillreturntoitscallingfunction.IftheuserchoseCancel,thefunctionreturnsnil:
(ifUserClick;UserclickedOk
;;Buildtheresultingdata
(progn
(setqResult(list
(cons42tileRad)
(cons43TileSpace)
(cons3objectCreateMethod)
(cons4plineStyle)
)
)
)
)
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>CreatingaProjectandAddingtheInterface>InteractingwiththeDialogBoxfromAutoLISPCode>
PuttingtheCodeTogether
Withtheexamplesabove,andafewadditionallines,youhavethecodeneededtocompletethegp:getDialogInputfunction.
Toputgp:getDialogInputtogether
1. Openyourcopyofgp-io.lspinaVLISPtexteditorwindow.
2. Deletethecodeingp:getDialogInput(thedefungp:getDialogInputstatementandeverythingafterit).
3. Enterthefollowingdefunstatementasthefirstlineofcodeinthegp:getDialogInputfunction:
(defungp:getDialogInput(pathWidth/dcl_idobjectCreateMethod
plineStyletileradtilespaceresultUserClick
dialogLoadeddialogShow)
Thefunctionexpectsasingleargument(pathwidth),andestablishesanumberoflocalvariables.
4. Followingthecodeyouaddedinstep3,enterthesamplecodefromeachofthefollowingsections:
SettingUpDialogValues
LoadingtheDialogFile
LoadingaSpecificDialogintoMemory
InitializingtheDefaultDialogValues
AssigningActionstoTilesNote EnterjustthefirstcodeexamplefromAssigningActionstoTiles
notthefragmentsintheexplanationsthatfollow.Thosefragmentsjustrepeatpiecesoftheexample.
StartingtheDialog
UnloadingtheDialog
DeterminingWhattoDoNext
5. Afterthelastlineofcode,addthefollowing:
)
)
Result;
);_endofdefun
6. FormatthecodeyouenteredbychoosingTools FormatCodeinEditorfromtheVLISPmenu.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>CreatingaProjectandAddingtheInterface>InteractingwiththeDialogBoxfromAutoLISPCode>
UpdatingaStubbed-OutFunction
Youhavenowrevisedthegp:getDialogInputfunction.Wheneveryoumodifyastubbed-outfunction,youshouldalwayscheckacoupleofthings:
Hasthedefunstatementchanged?Thatis,doesthefunctionstilltakethesamenumberofarguments?
Doesthefunctionreturnsomethingdifferent?
Inthecaseofgp:getDialogInput,theanswertobothquestionsisyes.Thefunctionnowacceptstheparameterofthepathwidth(tosetthedefaulttilesizeandspacing).AndinsteadofreturningT,whichisthevaluethestubbed-outversionofthefunctionreturned,gp:getDialogInputnowreturnsanassociationlistcontainingfournewvalues.
Bothchangesaffectthecodethatcallsthefunctionandthecodethathandlesthereturnvaluesfromthefunctions.ReplaceyourpreviousversionoftheC:GPathfunctioningpmain.lspwiththefollowingcode:
(defunC:GPath(/gp_PathDatagp_dialogResults)
;;Asktheuserforinput:firstforpathlocationand
;;direction,thenforpathparameters.Continueonlyifyou
;;havevalidinput.Storethedataingp_PathData.
(if(setqgp_PathData(gp:getPointInput))
(if(setqgp_dialogResults(gp:getDialogInput(cdr(assoc40
gp_PathData))))
(progn
;;Nowtaketheresultsofgp:getPointInputandappendthis
;;totheaddedinformationsuppliedbygp:getDialogInput.
(setqgp_PathData(appendgp_PathDatagp_DialogResults))
;;Atthispoint,youhavealltheinputfromtheuser.
;;Drawtheoutline,storingtheresultingpolyline
;;"pointer"inthevariablecalledPolylineName.
(setqPolylineName(gp:drawOutlinegp_PathData))
);_endofprogn
(princ"\nFunctioncancelled.")
);_endofif
(princ"\nIncompleteinformationtodrawaboundary.")
);_endofif
(princ);exitquietly
);_endofdefun
TakealookattheboldfacelinesintherevisionofthemainC:GPathfunction.Therearetwoessentialchangestomaketheprogramworkcorrectly:
Whenthegp:getDialogInputfunctionisinvoked,thepathwidthispassedtoit.Thisisdonebyextractingthevalueassociatedwiththekey40indexofthegp_PathDataassociationlist.
Theassociationlistreturnedbygp:getPointInputisassignedtoavariablecalledgp_dialogResults.Ifthisvariablehasavalue,itscontentneedstobeappendedtotheassociationlistvaluesalreadystoredingp_PathData.
Thereareadditionalchangesinthecoderesultingfromthereplacementofplaceholdersinthestubbed-outversion.Theeasiestthingtodoiscopythiscodefromtheonlinetutorialandpasteitintoyourfile.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>CreatingaProjectandAddingtheInterface>
ProvidingaChoiceofBoundaryLineType
Onerequirementspecifiedforthegardenpathapplicationwastoallowuserstodrawtheboundaryoutlineaseitheralightweightpolylineoranold-stylepolyline.Thefirstversionofgp:drawOutlineyouwrotealwaysusedalightweightpolylinetodrawtheboundary.Nowthatthedialogboxinterfaceisreadytogo,youcanbuildintheoptionfordrawinganold-stylepolylineaswell.Toaccomplishthis,gp:drawOutlinemustdeterminewhatkindofpolylinetodraw,andthenitmustdrawit.
Thenecessarychangestogp:drawOutlineareincludedinthefollowingcodefragment.Makethemodificationfromthegpdraw.lspfileindicatedinbold:
(setqPathAngle(cdr(assoc50BoundaryData))
Width(cdr(assoc40BoundaryData))
HalfWidth(/Width2.00)
StartPt(cdr(assoc10BoundaryData))
PathLength(cdr(assoc41BoundaryData))
angp90(+PathAngle(Degrees->Radians90))
angm90(-PathAngle(Degrees->Radians90))
p1(polarStartPtangm90HalfWidth)
p2(polarp1PathAnglePathLength)
p3(polarp2angp90Width)
p4(polarp3(+PathAngle(Degrees->Radians180))
PathLength)
poly2Dpoints(apply'append
(mapcar'3dPoint->2dPoint(listp1p2p3p4))
)
poly3Dpoints(mapcar'float(appendp1p2p3p4))
;;getthepolylinestyle
plineStyle(strcase(cdr(assoc4BoundaryData)))
);_endofsetq
;;AddpolylinetothemodelspaceusingActiveXautomation
(setqpline(if(=plineStyle"LIGHT")
;;createalightweightpolyline
(vla-addLightweightPolyline
*ModelSpace*;GlobalDefinitionforModelSpace
(gp:list->variantArraypoly2Dpoints);dataconversion
);_endofvla-addLightweightPolyline
;;orcreateanold-stylepolyline
(vla-addPolyline
*ModelSpace*
(gp:list->variantArraypoly3Dpoints);dataconversion
);_endofvla-addPolyline
);_endofif
);_endofsetq
Typingthechangesintoyourcodecanbeverytricky,asyounotonlyneedtoaddcodebutalsotodeletesomeexistinglinesandrearrangeothers.Itisrecommendedyoucopytheentiresetqstatementfromtheonlinetutorialandpasteitintoyourcode.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>CreatingaProjectandAddingtheInterface>
CleaningUp
Ifyouhavenotdonesoalready,deletethefollowingcodefromtheC:GPathfunctioningpmain.lsp:
(princ"\nThegp:drawOutlinefunctionreturned<")
(princPolylineName)
(princ">")
(Alert"Congratulations-yourprogramiscomplete!")
Youhadbeenusingthiscodeasaplaceholder,butnowthatgp:drawOutlineisfunctioning,younolongerneedit.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>CreatingaProjectandAddingtheInterface>
RunningtheApplication
Beforerunningyourprogram,saveallthefilesyouchanged,ifyouhavenotalreadydoneso.YoucanchooseFile SaveAllfromtheVLISPmenu,orusetheALT+SHIFT+Skeyboardshortcuttosaveallyouropenfiles.
ThenextthingyoumustdoisreloadallthefilesinVLISP.
Toloadandrunallthefilesinyourapplication
1. Iftheprojectfileyoucreatedearlierinthislessonisnotalreadyopen,chooseProject OpenProjectfromtheVLISPmenu,thenentertheprojectfilenamegpath;donotincludethe.prjextension.IfVLISPdoesnotfindtheprojectfile,choosetheBrowsebuttonandchoosethefilefromtheOpenProjectdialogbox.ClickOpen.
2. ChoosetheLoadSourceFilesbuttonintheprojectwindow.
3. Enterthe(C:GPath)commandattheVLISPConsoleprompttoruntheprogram.Ifyouhavesomedebuggingtodo,tryusingthetoolsyoulearnedinLessons2and3.Andremember,ifallelsefails,youcanalwayscopythecodefromtheTutorial\VisualLISP\Lesson4directory.
Also,trydrawingthepathusingbothlightweightandold-stylepolylines.Afterdrawingthepaths,usetheAutoCADlistcommandtodeterminewhetherornotyourprogramisdrawingthecorrectentitytypes.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>CreatingaProjectandAddingtheInterface>
WrappingUpLesson4
Inthislesson,you
Modularizedyourcodebydividingitamongfourfiles.
OrganizedyourcodemodulesinaVLISPproject.
Learnedtodefineadialogboxwithdialogcontrollanguage(DCL).
AddedAutoLISPcodetosetupandhandleinputinthedialogbox.
Modifiedyourcodetoprovideuserswithachoiceofboundarylinetype.
Nowyouhaveaprogramthatdrawsagardenpathboundary.Inthenextlesson,youwilladdthetilestothegardenpath.Intheprocess,youwillbeintroducedtomoreVLISPprogramdevelopmenttools.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>
DrawingtheTiles
Bytheendofthislesson,yourapplicationwillmeetthebasicrequirementsstatedinLesson1.Youwilladdthefunctionalityfordrawingtileswithintheboundaryofthegardenpathandprovidethisfunctionusingseveraldifferentmethodsofentitycreation.Youwillalsolearnsomekeyboardshortcutsandneweditingtools.
IntroducingMoreVisualLISPEditingToolsAddingTilestotheGardenPathTestingtheCodeWrappingUpLesson5
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>DrawingtheTiles>
IntroducingMoreVisualLISPEditingTools
Openyourcopyofgpdraw.lspinaVisualLISP®texteditorwindow,ifthefileisnotalreadyopen.ThereareacoupleofthingsaboutthiscodethataretypicalofmuchofthecodeyouwillbedevelopingwithVLISP.First,therearemanyparenthesesandparentheseswithinparentheses.Second,therearemanyfunctioncalls,andsomeofthosefunctionshaveverylongnames(vla-addLightweightPolyline,forexample).VLISPprovidessomeeditingtoolstohelpyoudealwiththesecommonfeaturesofAutoLISP®code.
MatchingParenthesesCompletingaWordAutomaticallyCompletingaWordbyAproposGettingHelpwithaFunction
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>DrawingtheTiles>IntroducingMoreVisualLISPEditingTools>
MatchingParentheses
VLISPprovidesaparenthesismatchingfeaturetohelpyoufindthecloseparenthesisthatcorrespondstoanopenparenthesis.
Tomatchanopenparenthesiswithitscorrespondingcloseparenthesis
1. Placeyourcursorinfrontoftheopeningparenthesisthatprecedesthesetqfunctioncall.
2. PressCTRL+SHIFT+].(Double-clickingalsodoesthetrick.)
VLISPfindstheclosingparenthesisthatmatchestheoneyouchose,andselectsallthecodeinbetween.Notonlydoesthisensureyoutypedinthecorrectnumberofparentheses,italsomakesiteasytocopyorcuttheselectedtext.ThismighthavecomeinhandywhenyouupdatedthiscallattheendofLesson4.
Whyelsemightyouwanttodothis?YoucancopysomecodetotheVLISPConsolewindow,pasteitthere,andtryitout.Ormaybeyouhavefiguredouthowtoreplace50linesofcodewiththreereallymarvelouslinesofmuchbettercode.Youcanquicklyselecttheoldcodeusingtheparenthesesmatchingtool,theneliminateitwithasinglekeystroke.ItisalotquickertoletVLISPfindanentireblockthanforyoutohuntdowneverylastclosingparenthesis.
Thereisacorrespondingkeycommandformatchingandselectingbackward.Totrythis,putyourcursorafteraclosingparenthesis,theneitherdouble-clickorpressCTRL+SHIFT+[.VLISPsearchesforthecorrespondingopeningparenthesis,andselectsitalongwiththeenclosedcode.
BothcommandsarealsoavailablebychoosingEdit ParenthesesMatchingfromtheVLISPmenu.
AutoLISPTutorial>DrawingtheTiles>IntroducingMoreVisualLISPEditingTools>
CompletingaWordAutomatically
Imagineyouareaddingsomenewfunctionalitytoyourprogramusingthefollowingcode:
ObjectCreationStyle(strcase(cdr(assoc3BoundaryData)))
(if(equalObjectCreationStyle"COMMAND")
(progn
(setqfirstCenterPt(polarrowCenterPt(Degrees->Radians45)distanceOnPath))
(gp:Create_activeX_Circle)
)
)
(Don'tworryaboutwhatthiscodeactuallydoes,ifanything.Itisonlyanexamplethatincludesseverallongvariableandfunctionnames.)
VLISPcansaveyousomekeystrokesbycompletingwordsforyou.
TousetheVisualLISPCompleteWordbyMatchfeature
1. Scrolltothebottomofthegpdraw.lspfileandenterthefollowingcode:
ObjectCreationStyle(strcase(cdr(assoc3BoundaryData)))
(if(equalOb
2. PressCTRL+SPACEBAR.VLISPjustsavedyouseventeenkeystrokesasitsearchedwithinthecurrentfileandfoundtheclosestmatchtothelasttwolettersyoutyped.
3. Completethelineofcodesothatitlookslikethefollowing:
(if(equalObjectCreationStyle"COMMAND")
4. Addthefollowinglines:
(progn
(setqfirstCenterPt(p
5. PressCTRL+SPACEBAR.VLISPmatchesthemostrecent“p”word,whichhappenstobeprogn.However,thewordyouneedispolar.IfyoukeeppressingCTRL+SPACEBAR,VLISPcyclesthroughallthepossiblematchesinyourcode.Eventually,itwillcomearoundtopolar.
6. Deleteallthecodeyoujustentered;itwasfordemonstrationpurposesonly.TheCompleteWordbyMatchfeatureisalsoavailablefromtheVLISPSearchmenu.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>DrawingtheTiles>IntroducingMoreVisualLISPEditingTools>
CompletingaWordbyApropos
IfyouhaveworkedwithAutoLISPbefore,youmayhavehadtotypeinanexpressionsimilartotheoneshownbelow:
(setqmyEnt(ssnamemySelectionSetssIndex))
Often,itisconfusingtokeeptrackofalltheselectionsetfunctions:ssname,ssget,sslength,andsoon.VLISPcanhelp,usingitsCompleteWordbyAproposfeature.
TousetheVisualLISPCompleteWordbyAproprosfeature
1. Scrolltothebottomofthegpdraw.lspfileandenterthefollowingonablankline:
(setqmyEnt(ent
2. PressCTRL+SHIFT+SPACEBAR.VLISPdisplaysalistofallAutoLISPsymbolsthatbeginwiththelettersent.Usethecursorkeys(theupanddownarrowkeys)tomovethroughthelist.SelectENTGET,thenpressENTER.VLISPreplacestheentyoutypedwithENTGET.
3. Deletethecode.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>DrawingtheTiles>IntroducingMoreVisualLISPEditingTools>
GettingHelpwithaFunction
Thecodethataddsalightweightpolylinetothedrawingcallsafunctionnamedvla-addLightweightPolyline.Notonlyisthatalengthytermtowrite,butthereareseveralfunctionswhosenamesbeginwithvla-addthatyouwillusetocreateentities.Ratherthanconsultingamanualtolookupthefunctionnameeverytimeyoucreateaprogram,letVLISPhelp.
Togethelpwithusingafunction
1. Enterthefollowingonablankline:
(vla-add
2. PressCTRL+SHIFT+SPACEBAR.
3. Scrollthroughthelistuntilyoufindvla-addLightweightPolyline.
4. Double-clickonvla-addLightweightPolyline.VLISPdisplaystheSymbolServicedialogboxfortheselectedfunction.
5. ChoosetheHelpbuttonintheSymbolServicedialogbox.(ForActiveX®functions,youwillbedirectedtotheActiveXandVBAReference.)
6. Deletethechangesyoumadetogpdraw.lsp;thesewerefordemonstrationpurposesonly.Also,closetheSymbolServiceandAproposwindows.
AutoLISPTutorial>DrawingtheTiles>
AddingTilestotheGardenPath
Younowhaveapathboundaryandarereadytofillitwithtiles.Youwillneedtoapplysomelogicandworkthroughalittlegeometry.
ApplyingSomeLogicApplyingSomeGeometryDrawingtheRowsDrawingtheTilesinaRowLookingattheCode
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>DrawingtheTiles>AddingTilestotheGardenPath>
ApplyingSomeLogic
Onethingyouneedtodoisdeterminehowtospaceoutthetilesanddrawthem.Ifthiswereasimplerectilineargridoftiles,youcouldusetheAutoCAD®
ARRAYcommandtofillinthetiles.Butforthegardenpath,youneedtohaveeachrowoftilesoffsetfromthepreviousrow.
Thisrow-offsetpatternisarepeatingpattern.Thinkofhowyoumightgoaboutlayingthetilesifyouwerebuildingtheactualpath.Youwouldprobablybeinclinedtostartatoneendandjustkeeplayingdownrowsuntiltherewasn'tanymorespaceleft.
Hereisthelogicinpseudo-code:
Atthestartingpointofthepath
Figureouttheinitialrowoffsetfromcenter(eithercenteredon
thepathoroffsetbyone"tilespace").
Whilethespaceoftheboundaryfilledislessthanthespaceto
fill,
Drawarowoftiles.
Resetthenextstartpoint(incrementedbyone"tilespace").
Addthedistancefilledbythenewrowtotheamountofspace
filled.
Toggletheoffset(ifitiscentered,setitupoff-center,or
viceversa).
Gobacktothestartoftheloop.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>DrawingtheTiles>AddingTilestotheGardenPath>
ApplyingSomeGeometry
Thereareonlyafewdimensionsyouneedtoknowtodrawthegardenpath.Thehalf-widthiseasy:itisjusthalfthewidthofthepath.Youalreadydefinedthecodetoobtainthiswidthfromusersandsaveditinanassociationlist.
Tilespacingisalsoeasy;itistwicetheradius(thatis,thediameter)plusthespacebetweenthetiles.Thedimensionsarealsoobtainedfromusers.
Rowspacingisalittletrickier,unlessyouarereallysharpwithtrigonometry.Hereistheformula:
RowSpacing=(TileDiameter+SpacebetweenTiles)*(thesine
of60degrees)
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>DrawingtheTiles>AddingTilestotheGardenPath>
DrawingtheRows
Seeifyoucanmakesenseofthefollowingfunction.Compareittothepseudo-codeandtrytocatchthegeometriccalculationsjustdescribed.TheremaybeafewAutoLISPfunctionsthatarenewtoyou.Ifyouneedhelpwiththesefunctions,refertotheAutoLISPReference.Fornow,justreadthecode;donotwriteanything.
(defungp:Calculate-and-Draw-Tiles(BoundaryData/PathLength
TileSpaceTileRadiusSpaceFilledSpaceToFill
RowSpacingoffsetFromCenter
rowStartPointpathWidthpathAngle
ObjectCreationStyleTileList)
(setqPathLength(cdr(assoc41BoundaryData))
TileSpace(cdr(assoc43BoundaryData))
TileRadius(cdr(assoc42BoundaryData))
SpaceToFill(-PathLengthTileRadius)
RowSpacing(*(+TileSpace(*TileRadius2.0))
(sin(Degrees->Radians60))
);_endof*
SpaceFilledRowSpacing
offsetFromCenter0.0
offsetDistance(/(+(*TileRadius2.0)TileSpace)2.0)
rowStartPoint(cdr(assoc10BoundaryData))
pathWidth(cdr(assoc40BoundaryData))
pathAngle(cdr(assoc50BoundaryData))
ObjectCreationStyle(strcase(cdr(assoc3BoundaryData)))
);_endofsetq
;;Compensateforthefirstcalltogp:calculate-Draw-tileRow
;;intheloopbelow.
(setqrowStartPoint
(polarrowStartPoint
(+pathAnglepi)
(/TileRadius2.0)
);_endofpolar
);_endofsetq
;;Draweachrowoftiles.
(while(<=SpaceFilledSpaceToFill)
;;Getthelistoftilescreated,addingthemtoourlist.
(setqtileList(appendtileList
(gp:calculate-Draw-TileRow
(setqrowStartPoint
(polarrowStartPoint
pathAngle
RowSpacing
);_endofpolar
);_endofsetq
TileRadius
TileSpace
pathWidth
pathAngle
offsetFromCenter
ObjectCreationStyle
);_endofgp:calculate-Draw-TileRow
);_endofappend
;;Calculatethedistancealongthepathforthenextrow.
SpaceFilled(+SpaceFilledRowSpacing)
;;Alternatebetweenazeroandapositiveoffset
;;(causesalternaterowstobeindented).
offsetFromCenter
(if(=offsetFromCenter0.0)
offsetDistance
0.0
);_endofif
);_endofsetq
);_endofwhile
;;Returnthelistoftilescreated.
tileList
);_endofdefun
Acoupleofsectionsfromthecodemayneedalittleextraexplanation.
Thefollowingcodefragmentoccursrightbeforethewhileloopbegins:
;;Compensatefortheveryfirststartpoint!!
(setqrowStartPoint(polarrowStartPoint
(+pathAnglepi)(/TileRadius2.0)))
Therearethreepiecestothepuzzleoffiguringoutthelogicbehindthisalgorithm:
TherowStartPointvariablestartsitslifewithinthegp:Calculate-and-Draw-Tilesfunctionbybeingassignedthepointtheuserselectedasthestartpointofthepath.
Theveryfirstargumentpassedtothegp:calculate-Draw-TileRowfunctiondoesthefollowing:
(setqrowStartPoint(polarrowStartPoint
pathAngleRowSpacing))
Anotherwayofstatingthisis:Atthetimethegp:calculate-Draw-TileRowfunctioniscalled,therowStartPointvariableissettooneRowSpacingdistancebeyondthecurrentrowStartPoint.
TherowStartPointargumentisusedwithingp:calculate-Draw-TileRowasthestartingpointforthecentersofthecirclesintherow.
TocompensatefortheinitialforwardshiftingoftherowStartPointduringthedrawingofthefirstrow(thatis,thefirstcyclethroughthewhileloop),youwillwanttoshiftrowStartPointslightlyintheoppositedirection.Theaimistoavoidtheappearanceofalargemarginofemptyspacebetweenthepathboundaryandthefirstrow.HalftheTileRadiusisasufficientamountbywhichtomovethepoint.ThiscanbeachievedbyusingpolartoprojectrowStartPointalongavectororiented180degreesfromthePathAngle.Ifyouthinkaboutit,thisplacesthepointtemporarilyoutsidethepathboundary.
Thenextfragment(modifiedforreadability)maybealittlepuzzling:
(setqtileList(appendtileList
(gp:calculate-Draw-TileRow
(setqrowStartPoint
(polarrowStartPointpathAngleRowSpacing)
);_endofsetq
TileRadiusTileSpacepathWidthpathAngle
offsetFromCenterObjectCreationStyle
)))
Inessence,thereissetqwrappedaroundanappendwrappedaroundthecalltogp:calculate-Draw-TileRow.
Thegp:calculate-Draw-TileRowfunctionwillreturntheObjectIDsforeachtiledrawn.(TheObjectIDpointstothetileobjectinthedrawing.)Youaredrawingthetilesrowbyrow,sothefunctionreturnstheObjectIDsofonerowatatime.TheappendfunctionaddsthenewObjectIDstoanyexistingObjectIDsstoredintileList.
Neartheendofthefunction,youcanfindthefollowingcodefragment:
(setqoffsetFromCenter
(if(=offsetFromCenter0.0)
offsetDistance
0.0
)
)
Thisistheoffsettoggle,whichdetermineswhethertherowbeingdrawnshouldbeginwithacirclecenteredonthepathoroffsetfromthepath.Thepseudo-codeforthisalgorithmfollows:
Settheoffsetamounttothefollowing:
Iftheoffsetiscurrentlyzero,setittotheoffsetdistance;
Otherwise,setitbacktozero.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>DrawingtheTiles>AddingTilestotheGardenPath>
DrawingtheTilesinaRow
Nowthatyouhavethelogicfordrawingthepath,thenextstepistofigureouthowtodrawthetilesineachrow.Inthefollowingdiagram,therearetwocasesshown:arowwheretheoffsetfromthecenterofthepathisequalto0.0,andacasewheretheoffsetisnotequaltozero.Takealookatthediagram,thenreadthepseudo-codethatfollows.
SetupvariablesforStartPoint,angp90,angm90,andsoon.
SetthevariableFirstCenterPointtotheStartPoint+offsetamount
(whichmaybe0.0).
SettheinitialvalueofTileCenterPttoFirstCenterPoint.
(Comment:Beginbydrawingthecirclesintheangp90direction.)
WhilethedistancefromtheStartPointtotheTileCenterPtislessthantheHalfWidth:
Drawacircle(addingtotheaccumulatinglistofcircles).
SetTileCenterPttothenexttilespaceincrementintheangp90
direction.
EndWhile
ResettheTileCenterPointtotheFirstCenterPoint+thetilespaceincrementatangm90.
WhilethedistancefromtheStartPointtotheTileCenterPtislessthantheHalfWidth:
Drawacircle(addingtotheaccumulatinglistofcircles).
SetTileCenterPttothenexttilespaceincrementintheangm90
direction.
EndWhile
Returnthelistofcircles.
AutoLISPTutorial>DrawingtheTiles>AddingTilestotheGardenPath>
LookingattheCode
Nowlookatthecodeforthegp:calculate-Draw-TileRowfunction:
(defungp:calculate-Draw-TileRow(startPointTileRadius
TileSpacepathWidthpathAngleoffsetFromCenter
ObjectCreationStyle/HalfWidthTileDiameter
ObjectCreationFunctionangp90angm90
firstCenterPtTileCenterPtTileList)
(setqHalfWidth(-(/pathWidth2.00)TileRadius)
Tilespacing(+(*TileRadius2.0)TileSpace)
TileDiameter(*TileRadius2.0)
angp90(+PathAngle(Degrees->Radians90))
angm90(-PathAngle(Degrees->Radians90))
firstCenterPt(polarstartPointangp90offsetFromCenter)
tileCenterPtfirstCenterPt
ObjectCreationStyle(strcaseObjectCreationStyle)
ObjectCreationFunction
(cond
((equalObjectCreationStyle"ACTIVEX")
gp:Create_activeX_Circle
)
((equalObjectCreationStyle"ENTMAKE")
gp:Create_entmake_Circle
)
((equalObjectCreationStyle"COMMAND")
gp:Create_command_Circle
)
(T
(alert(strcat"ObjectCreationStyleinfunction
gp:calculate-Draw-TileRow"
"\nisinvalid.Contactdeveloperforassistance."
"\nObjectCreationStylesettoACTIVEX"
)
)
setqObjectCreationStyle"ACTIVEX")
)
)
)
;;Drawthecirclestotheleftofthecenter.
(while(<(distancestartPointtileCenterPt)HalfWidth)
;;Addeachtiletothelisttoreturn.
(setqtileList
(cons
(ObjectCreationFunctiontileCenterPtTileRadius)
tileList
)
)
;;Calculatethecenterpointforthenexttile.
(setqtileCenterPt
(polartileCenterPtangp90TileSpacing)
)
);_endofwhile
;;Drawthecirclestotherightofthecenter.
(setqtileCenterPt
(polarfirstCenterPtangm90TileSpacing))
(while(<(distancestartPointtileCenterPt)HalfWidth)
;;Addeachtiletothelisttoreturn.
(setqtileList
(cons
(ObjectCreationFunctiontileCenterPtTileRadius)
tileList
)
)
;;Calculatethecenterpointforthenexttile.
(setqtileCenterPt(polartileCenterPtangm90TileSpacing))
);_endofwhile
;;Returnthelistoftiles.
tileList
);_endofdefun
TheAutoLISPcodelogicfollowsthepseudo-code,withthefollowingaddition:
(setqObjectCreationFunction
(cond
((equalObjectCreationStyle"ACTIVEX")
gp:Create_activeX_Circle
)
((equalObjectCreationStyle"ENTMAKE")
gp:Create_entmake_Circle
)
((equalObjectCreationStyle"COMMAND")
gp:Create_command_Circle
)
(T
(alert
(strcat
"ObjectCreationStyleinfunctiongp:calculate-Draw-TileRow"
"\nisinvalid.Contactthedeveloperforassistance."
"\nObjectCreationStylesettoACTIVEX"
);_endofstrcat
);_endofalert
(setqObjectCreationStyle"ACTIVEX")
)
);_endofcond
);_endofsetq
Rememberthespecificationtoallowuserstodrawthetiles(circles)usingeitherActiveX,theentmakefunction,orthecommandfunction?TheObjectCreationFunctionvariableisassignedoneofthreefunctions,dependingontheObjectCreationStyleparameter(passedfromC:GPathandthroughgp:Calculate-and-Draw-Tiles).Herearethethreefunctionsastheywillbedefinedingpdraw.lsp:
(defungp:Create_activeX_Circle(centerradius)
(vla-addCircle*ModelSpace*
(vlax-3d-pointcenter);converttoActiveX-compatible3Dpoint
radius
)
);_endofdefun
(defungp:Create_entmake_Circle(centerradius)
(entmake
(list(cons0"CIRCLE")(cons10center)(cons40radius))
)
(vlax-ename->vla-object(entlast))
)
(defungp:Create_command_Circle(centerradius)
(command"_CIRCLE"centerradius)
(vlax-ename->vla-object(entlast))
)
ThefirstfunctiondrawsacircleusinganActiveXfunctionandreturnsanActiveXobject.
Thesecondfunctiondrawsacircleusingentmake.ItreturnsanentitynameconvertedintoanActiveXobject.
Thethirdfunctiondrawsacircleusingcommand.ItalsoreturnsanentitynameconvertedintoanActiveXobject.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>DrawingtheTiles>
TestingtheCode
Ifyou'vemadeitthisfar,youhaveearnedashortcut.
Totestthecode
1. ClosealltheactivewindowswithinVLISP,includinganyopenprojectwindows.
2. CopytheentirecontentsoftheTutorial\VisualLISP\Lesson5directorytoyourMyPathtutorialdirectory.
3. Opentheprojectfilegpath5.prjusingSelectProject OpenProjectfromtheVLISPmenubar.
4. Loadtheprojectsourcefiles.
5. Activate(switchto)theAutoCAD®windowandissuethegpathcommandtoruntheprogram.
6. Rungpathtodrawthegardenpaththreetimes,eachtimeusingadifferententitycreationmethod.Doyounoticeadifferenceinthespeedwithwhichthepathisdrawnwitheachmethod?
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>DrawingtheTiles>
WrappingUpLesson5
YoustartedthislessonbylearningVLISPeditingfeaturesthathelpedyou
Matchtheparenthesesinyourcode.
Findandcompleteafunctionname.
ObtaininformationinHelpforafunction.
Youfinishedthelessonbybuildingcodethatdrawsthetilesinthegardenpath.Younowhaveaprogramthatmeetstherequirementsestablishedattheverybeginningofthistutorial.
Atthispoint,youprobablyhaveacquiredenoughexperiencewithVLISPtoventureoffonyourown.Butifyouareuptoit,therearetwomorelessonsinthistutorialthatdemonstratetheuseofreactorfunctionsandotheradvancedfeaturesoftheVLISPenvironment.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>
ActingwithReactors
Inthislesson,youwilllearnaboutreactorsandhowtoattachthemtodrawingeventsandentities.ReactorsallowyourapplicationtobenotifiedbyAutoCAD®
whenparticulareventsoccur.Forexample,ifausermovesanentitythatyourapplicationhasattachedareactorto,yourapplicationwillreceivenotificationthattheentityhasmoved.Youcanprogramthistotriggeradditionaloperations,suchasmovingotherentitiesassociatedwiththeonetheusermoved,orperhapsupdatingatexttagthatrecordsrevisioninformationonthealtereddrawingfeature.Ineffect,itislikesettingupyourapplicationwithapagerandtellingAutoCADtobeeptheapplicationwhensomethinghappens.
ReactorBasicsDesigningReactorsfortheGardenPathTestDrivingYourReactorsWrappingUpLesson6
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>ActingwithReactors>
ReactorBasics
Areactorisanobjectyouattachtothedrawingeditor,ortospecificentitieswithinadrawing.Extendingthemetaphorofthepager,thereactorobjectisanautomaticdialerthatknowshowtocallyourpagerwhensomethingsignificanthappens.ThepagerwithinyourapplicationisanAutoLISP®functioncalledbythereactor;suchafunctionisknownasacallbackfunction.
Note ThecomplexityoftheapplicationcodeandthelevelofexpertiserequiredforthesefinaltwolessonsismuchhigherthanLessons1through5.Thereisagreatdealofinformationpresented,butitisnotallexplainedatthesamelevelofdetailasinthepreviouslessons.Ifyouareabeginner,don'tworryifyoudon'tgetitthefirsttime.ConsiderthisjustafirsttasteofsomeoftheverypowerfulbutmoretechnicallydifficultfeaturesofVisualLISP®.
ReactorTypes
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>ActingwithReactors>ReactorBasics>
ReactorTypes
TherearemanytypesofAutoCAD®reactors.EachreactortyperespondstooneormoreAutoCADevents.Reactorsaregroupedintothefollowingcategories:
EditorReactors
NotifyyourapplicationeachtimeanAutoCADcommandisinvoked.
LinkerReactors
NotifyyourapplicationeverytimeanObjectARX®applicationisloadedorunloaded.
DatabaseReactors
Correspondtospecificentitiesorobjectswithinadrawingdatabase.
DocumentReactors
NotifyyourapplicationinMDImodeofachangetothecurrentdrawingdocument,suchasopeningofanewdrawingdocument,activatingadifferentdocumentwindow,andchangingadocument'slockstatus.
ObjectReactors
Notifyyoueachtimeaspecificobjectischanged,copied,ordeleted.
Withtheexceptionofeditorreactors,thereisonetypeofreactorforeachreactorcategory.Editorreactorsencompassabroadclassofreactors:forexample,DXF™reactorsthatnotifyanapplicationwhenaDXFfileisimportedorexported,andMousereactorsthatnotifyofmouseeventssuchasdouble-clicks.
Withinthereactorcategories,therearemanyspecificeventstowhichyoucanattachareactor.AutoCADallowsuserstoperformmanydifferentkindsofactions,anditisuptoyoutodeterminetheactionsthatyouareinterestedin.Onceyouhavedonethis,youcanattachyourreactor“auto-dialer”totheevent,
AutoLISPTutorial>ActingwithReactors>
DesigningReactorsfortheGardenPath
Toimplementreactorfunctionalityinthegardenpathapplication,startbyhandlingjustafewevents,ratherthantryingtocoverallpossibleuseractions.
SelectingReactorEventsfortheGardenPathPlanningtheCallbackFunctionsPlanningforMultipleReactorsAttachingtheReactorsStoringDatawithaReactorUpdatingtheC:GPathFunctionAddingReactorCallbackFunctionsCleaningUpAfterYourReactors
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>ActingwithReactors>DesigningReactorsfortheGardenPath>
SelectingReactorEventsfortheGardenPath
Forthetutorial,setthefollowinggoals:
Whenacornerpoint(vertex)ofthegardenpathboundaryisrepositioned,redrawthepathsothattheoutlineremainsrectilinear.Inaddition,redrawthetilesbasedonthenewsizeandshape.
Whenthegardenpathboundaryiserased,erasethetilesaswell.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>ActingwithReactors>DesigningReactorsfortheGardenPath>
PlanningtheCallbackFunctions
Foreachreactorevent,youmustplanthefunctionthatwillbeinvokedwhentheeventoccurs.Thefollowingpseudo-codeoutlinesthelogicalsequenceofeventsthatshouldoccurwhenusersdragoneofthepolylineverticestoanewlocation:
Defungp:outline-changed
Erasethetiles.
Determinehowtheboundarychanged.
Straightenuptheboundary.
Redrawnewtiles.
Endfunction
Thereisacomplication,though.Whentheuserbeginsdraggingtheoutlineofapolylinevertex,AutoCADnotifiesyourapplicationbyissuinga:vlr-modifiedevent.However,atthispointtheuserhasjustbegundraggingoneofthepolylinevertices.Ifyouimmediatelyinvokethegp:outline-changedfunction,youwillinterrupttheactionthattheuserisinthemidstof.Youwouldnotknowwherethenewvertexlocationwillbe,becausetheuserhasnotyetselecteditsposition.Andfinally,AutoCADwillnotallowyourfunctiontomodifythepolylineobjectwhiletheuserisstilldraggingit.AutoCADhasthepolylineobjectopenformodification,andleavesitopenuntiltheuserfinishesrepositioningtheobject.
Youneedtochangeyourapproach.Hereistheupdatedlogic:
Whentheuserbeginsrepositioningapolylinevertex,
Invokethegp:outline-changedfunction
Defungp:outline-changed
Setaglobalvariablethatstoresapointertothepolyline
beingmodifiedbytheuser
Endfunction
Whenthecommandcompletes,
Invokethegp:command-endedfunction
Defungp:command-ended
Erasethetiles
Getinformationonthepreviouspolylinevertexlocations
Getinformationonthenewpolylinevertexlocations
Redefinethepolyline(straightenitup)
Redrawthetiles
Endfunction
Whenausercompletesmodifyingapathoutline,AutoCADnotifiesyourapplicationbyissuinga:vlr-commandEndedevent,ifyouhaveestablishedaneditorreactor.
Theuseofaglobalvariabletoidentifythepolylinetheuserchangedisnecessarybecausethereisnocontinuitybetweenthegp:outline-changedandgp:command-endedfunctions.Inyourapplication,bothfunctionsareinvokedandexecutedindependentlyofoneanother.Theglobalvariablestoresimportantinformationsetupinonefunctionandaccessedintheother.
Nowconsiderwhattodoiftheusererasesthegardenpathboundary.Theultimateobjectiveistoeraseallthetiles.Thefollowingpseudo-codeoutlinesthelogic:
Whentheuserbeginstoerasetheboundary,
Invokethegp:outline-erasedfunction
Defungp:outline-erased
Setaglobalvariablethatstoresapointertothereactor
attachedtothepolylinecurrentlybeingerased
Endfunction
Whentheeraseiscompleted,
Invokethegp:command-endedfunction
Defungp:command-ended
Erasethetilesthatbelongedtothenow-deletedpolyline
Endfunction
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>ActingwithReactors>DesigningReactorsfortheGardenPath>
PlanningforMultipleReactors
Usersmayhaveseveralgardenpathsonthescreen,andmaybeerasingmorethanone.Youneedtoplanforthispossibility.
Thereactorassociatedwithanentityisanobjectreactor.Ifthereareseveralentitiesinthedrawing,theremayalsobeseveralobjectreactors,oneforeachentity.Aspecificeditingevent,suchastheerasecommand,cantriggermanycallbacks,dependingonhowmanyoftheselectedentitieshavereactorsattached.Editorreactors,ontheotherhand,aresingularinnature.Yourapplicationshouldonlyattachasingle:vlr-commandEndedeventreactor.
Theeventsequenceforbothmodifications—changingavertexlocationanderasingapolyline—endsupwithactionsthatneedtobeperformedwithinthegp:command-endedfunction.Determinewhichsetofactionstoperformforeachcondition.Thefollowingpseudo-codeoutlinesthelogic:
Defungp:command-ended(2ndversion)
Retrievethepointertothepolyline(fromaglobalvariable)
Conditional:
Ifthepolylinehasbeenmodifiedthen:
Erasethetiles
Getinformationonthepreviouspolylinevertexlocations
Getinformationonthenewpolylinevertexlocations
Redefinethepolyline(straightenitup)
Redrawthetiles
Endconditionalexpression
Ifthepolylinehasbeenerasedthen:
Erasethetiles
Endconditionalexpression
EndConditional
Endfunction
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>ActingwithReactors>DesigningReactorsfortheGardenPath>
AttachingtheReactors
Thenextstepinplanningareactor-basedapplicationistodeterminehowandwhentoattachreactors.Youneedtoattachtwoobjectreactorsforthepolylineborderofanygardenpath(onetorespondtoamodificationevent,theothertorespondtoerasure),andoneeditorreactortoalertyourapplicationwhenuserscompletetheirmodificationtothepolyline.Objectreactorsareattachedtoentities,whileeditorreactorsareregisteredwithAutoCAD.
Thereisanotherconsiderationtoaccountfor.Torecalculatethepolylineoutline—returnittoarectilinearshape—aftertheusermodifiesit,youmustknowwhatthevertexconfigurationwasbeforethemodification.Thisinformationcannotbedeterminedoncethepolylinehasbeenmodified.Bythattimeyoucanonlyretrieveinformationonwhatthenewconfigurationis.Sohowdoyousolvethis?Youcouldkeepthisinformationinaglobalvariable,butthereisamajorproblemwiththatidea.Userscandrawasmanygardenpathsastheywant,andeverynewpathwouldrequireanewglobalvariable.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>ActingwithReactors>DesigningReactorsfortheGardenPath>
StoringDatawithaReactor
YoucansolvetheproblemofsavingtheoriginalconfigurationbytakingadvantageofanotherfeatureofVLISPreactors—theabilitytostoredatawithinareactor.Whentheuserfirstdrawsapathboundary,youattachareactortotheboundary,alongwiththedatayouneedtosave.Thisentailsmodifyingyourmainprogramfunction,C:GPath,asfollows:
DefunC:GPath
Doeverythingthatisalreadydoneinthegardenpath
(anddon'tbreakanything)
Attachanobjectreactortothepolylineusingtheseparameters:
Apointertothepolylinejustdrawn,
Alistofdatathatyouwantthereactortorecord,
Alistofthespecificpolylineobjecteventstobetracked,
alongwiththeLISPcallbackfunctionstobeinvoked
Endoftheobjectreactorsetup
Attacheditorreactortothedrawingeditorusingthe
followingparameters:
Anydatayouwantattachedtothereactor(inthiscase,none)
Alistofthespecificeditorreactoreventstobetracked,
alongwiththeLISPcallbackfunctionstobeinvoked
Endoftheeditorreactorsetup
Endfunction
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>ActingwithReactors>DesigningReactorsfortheGardenPath>
UpdatingtheC:GPathFunction
UpdatetheC:GPathfunctionbyaddingreactorcreationlogic.
ToaddreactorcreationlogictoC:GPath
1. Replaceyourversionofgpmain.lspwiththeupdatedversionshownbelow.Copythiscodefromthe<AutoCADdirectory>\Tutorial\VisualLISP\Lesson6directory:
(defunC:GPath(/
gp_PathData
gp_dialogResults
PolylineName
tileList
)
(setvar"OSMODE"0);;Turnoffobjectsnaps
;|
;;Lesson6addsastubbed-outcommandreactortoAutoCAD
;;However,itwouldbeundesirabletoreacttoevery
;;drawingofacircleshouldtheCOMMANDtilecreation
;;methodbechosenbytheuser.So,disablethe
;;*commandReactor*incaseitexists.
|;
(if*commandReactor*
(progn
(setq*commandReactor*nil)
(vlr-remove-all:VLR-Command-Reactor)
)
)
;;Asktheuserforinput:firstforpathlocationand
;;direction,thenforpathparameters.Continueonlyifyou
;;havevalidinput.Storethedataingp_PathData.
(if(setqgp_PathData(gp:getPointInput))
(if(setqgp_dialogResults
(gp:getDialogInput
(cdr(assoc40gp_PathData))
);_endofgp:getDialogInput
);_endofsetq
(progn
;;Nowtaketheresultsofgp:getPointInputandappendthisto
;;theaddedinformationsuppliedbygp:getDialogInput
(setqgp_PathData(appendgp_PathDatagp_DialogResults))
;;Atthispoint,youhavealltheinputfromtheuser
;;Drawtheoutline,storingtheresultingpolyline"pointer"
;;inthevariablecalledPolylineName
(setqPolylineName(gp:drawOutlinegp_PathData))
;;Next,itistimetodrawthetileswithintheboundary.
;;Thegp_tileListcontainsalistoftheobjectpointersfor
;;thetiles.Bycountingupthenumberofpoints(usingthe
;;lengthfunction),wecanprintouttheresultsofhowmany
;;tilesweredrawn.
(princ"\nThepathrequired")
(princ
(length
(setqtileList(gp:Calculate-and-Draw-Tilesgp_PathData))
);_endoflength
);_endofprinc
(princ"tiles.")
;;Addthelistofpointerstothetiles(returnedby
;;gp:Calculate-and-Draw-Tiles)togp_PathData.Thiswill
;;bestoredinthereactordataforthereactorattached
;;totheboundarypolyline.Withthisdata,thepolyline
;;"knows"whattiles(circles)belongtoit.
(setqgp_PathData
(append(list(cons100tileList))
;allthetiles
gp_PathData
);_endofappend
);_endofsetq
;;Beforeweattachreactordatatoanobject,let'slookat
;;thefunctionvlr-object-reactor
;;vlr-object-reactorhasthefollowingarguments:
;;(vlr-object-reactorowner'sdatacallbacks)
;;ThecallbacksArgumentisalistcomprised
;;'(event_name.callback_function)
;;
;;Forthisexercisewewilluseallarguments
;;associatedwithvlr-object-reactor
;;Thesereactorfunctionswillexecuteonlyif
;;thepolylineinPolylineNameismodifiedorerased
(vlr-object-reactor
;;Thefirstargumentforvlr-object-reactoris
;;the"Owner'sList"argument.Thisiswhereto
;;placetheobjecttobeassociatedwiththe
;;reactor.Inthiscase,itisthevlaObject
;;storedinPolylineName.
(listPolylineName)
;;Thesecondargumentcontainsthedataforthepath
gp_PathData
;;Thethirdargumentisthelistofspecificreactor
;;typesthatweareinterestedinusing
'
(
;;reactorthatiscalleduponmodificationoftheobject
(:vlr-modified.gp:outline-changed)
;;reactorthatiscalleduponerasureoftheobject
(:vlr-erased.gp:outline-erased)
)
);_endofvlr-object-reactor
;;Next,registeracommandreactortoadjustthepolyline
;;whenthechangingcommandisfinished
(if(not*commandReactor*)
(setq*commandReactor*
(VLR-Command-Reactor
nil;Nodataisassociatedwiththecommandreactor
'(
(:vlr-commandWillStart.gp:command-will-start)
(:vlr-commandEnded.gp:command-ended)
)
);_endofvlr-command-reactor
)
)
;;Thefollowingcoderemovesallreactorswhenthedrawingis
;;closed.Thisisextremelyimportant!!!!!!!!!
;;Withoutthisnotification,AutoCADmaycrashuponexiting!
(if(not*DrawingReactor*)
(setq*DrawingReactor*
(VLR-DWG-Reactor
nil;Nodataisassociatedwiththedrawingreactor
'((:vlr-beginClose.gp:clean-all-reactors)
)
);_endofvlr-DWG-reactor
)
)
);_endofprogn
(princ"\nFunctioncancelled.")
);_endofif
(princ"\nIncompleteinformationtodrawaboundary.")
);_endofif
(princ);exitquietly
);_endofdefun
;;;Displayamessagetolettheuserknowthecommandname.
(princ"\nTypeGPATHtodrawagardenpath.")
(princ)
2. Reviewthecodemodificationsandcommentsdescribingwhateachnewstatementdoes.Thistutorialshowsallmodifiedcodeinboldface.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>ActingwithReactors>DesigningReactorsfortheGardenPath>
AddingReactorCallbackFunctions
Thereactorcallbackfunctionsaddasubstantialamountofcodetoyourapplication.ThiscodeisprovidedforyouintheLesson6directory.
Toaddthereactorcallbackfunctionstoyourprogram
1. Copythegpreact.lspfilefromtheTutorial\VisualLISP\Lesson6directorytoyourMyPathworkingdirectory.
2. OpentheGPathproject(ifitisnotalreadyopen),andchoosetheProjectPropertiesbuttoninthegpathprojectwindow.
3. Addthegpreact.lspfiletoyourproject.
4. Thegpreact.lspfilecanresideanywhereintheorderoffilesbetweenutils.lsp,whichmustremainfirst,andgpmain.lsp,whichshouldremainasthelastfile.Moveanyfiles,ifnecessary,thenchooseOK.
5. Openthegpreact.lspfilebydouble-clickingonthefilenamewithinthegpathprojectwindow.
Readthroughthecommentsinthefiletohelpyouunderstandwhatitisdoing.Notethatallthecallbackfunctionsarestubbedout;theonlyfunctionalitytheyperformistodisplayalertmessageswhentheyarefired.
Thelastfunctioninthefileissoimportantitdeservesaheadingofitsown.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>ActingwithReactors>DesigningReactorsfortheGardenPath>
CleaningUpAfterYourReactors
Reactorsareindeedveryactive.Whenyoudesignanapplicationthatreliesonthem,youcouldverywellspendagreatdealoftimecrashingyourprogramandpossiblycrashingAutoCADaswell.Ithelpstohaveatoolavailabletoremoveallthereactorsyouhaveadded,ifnecessary.
Thegpreact.lspfileincludesafunctiongp:clean-all-reactorsthatdoesn'tdomuchonitsown.Instead,itmakesacalltotheCleanReactorsfunction.Addthisfunctiontoyourutils.lspfilebycopyingthefollowingcodetotheendofthefile:
;;;--------------------------------------------------------------;
;;;Function:CleanReactors;
;;;--------------------------------------------------------------;
;;;Description:Generalutilityfunctionusedforcleaningup;
;;;reactors.Itcanbeusedduringdebugging,as;
;;;wellascleaningupanyopenreactorsbefore;
;;;adrawingisclosed.;
;;;--------------------------------------------------------------;
(defunCleanReactors()
(mapcar'vlr-remove-all
'(:VLR-AcDb-reactor
:VLR-Editor-reactor
:VLR-Linker-reactor
:VLR-Object-reactor
)
)
)
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>ActingwithReactors>
TestDrivingYourReactors
Bynow,youshouldhaveallthenecessarypiecesinplacetousesomelivereactors.
Totestthereactorcode
1. Loadallthesourcecodefromyourproject.(ChoosetheLoadSourceFilesbuttoninthegpathprojectwindow.)
2. StarttheC:GPathfunction.Theprogramwilldrawagardenpathforyou,justasyouwereabletoinLesson5.Youwon'tseeanythinginterestingatfirst.
3. Trythefollowingactionsafteryoudrawthepath:
Moveapolylinevertex.Pickthepolylineandturnonitsgrips,thendragavertextoanewlocation.
Stretchthepolyline.
Movethepolyline.
Erasethepolyline.
Examinethemessagesthatappear.Youarewatchingthebehind-the-scenesactivitiesofapowerfulcapability.
(Ifyourapplicationisnotworkingcorrectlyandyoudonotwanttotakethetimetodebugitrightnow,youcanrunthesamplecodeprovidedintheTutorial\VisualLISP\Lesson6directory.UsetheGpath6projectinthatdirectory.)
Note Becauseofthereactorbehavior,youmaynoticethataftertestingareactorsequenceinAutoCAD,youcannotreturntoVLISPbypressingALT+TAB,or
byclickingtoactivatetheVLISPwindow.Ifthishappens,simplyentervlispattheAutoCADCommandprompttoreturntoVLISP.
ExaminingReactorBehaviorinDetail
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>ActingwithReactors>TestDrivingYourReactors>
ExaminingReactorBehaviorinDetail
Withastackofscrappaper,starttracingthereactoreventswithintheapplication.Hereisanexampleofthekindsofthingsyoushouldtrack:
Drawtengardenpaths,thentrackthefollowingCommand/Objectcombinations,selectingthepolylinesinsuccession:
Erase/Polylineborder(path1)
Erase/Circlewithinapolyline(path2)
Erase/Twopolylines(paths3and4)
Move/Polylineborder(path5)
Move/Circlewithinapolyline(path6)
Move/Twopolylinesandseveralcircles(paths7and8)
MoveVertex(viagrips)/Polylineborder(path9)
Stretch/Polylineborder(path10)
Thisexercisewillgiveyouagoodunderstandingofwhatishappeningbehindthescenes.AtanytimethroughoutLesson7whenthereactorfunctionality
AutoLISPTutorial>ActingwithReactors>
WrappingUpLesson6
Inthislesson,youwereintroducedtoAutoCADreactorsandhowtoimplementthemusingVLISP.Youdesignedaplanforaddingreactorstothegardenpathapplication,andaddedsomeofthecodeyourprogramwillneedtoimplementtheplan.
Reactorscanaddagreatdealoffunctionalitytoanapplication,butremember—themorepowerfulyourprogramscanbe,thehardertheycancrash.
Anotherthingtokeepinmindisthatthewayyourapplicationisdesigned,thereactorfunctionalityisnotpersistentfromonedrawingsessiontothenext.Ifyousaveadrawingthatcontainsagardenpathhookeduptoreactors,thereactorswillnotbetherethenexttimeyouopenthedrawing.Youcanlearnaboutaddingpersistentreactorsbyreviewingthe“TransientversusPersistentReactors”topicintheAutoLISPDeveloper'sGuide,andthenreadingaboutthereferencedfunctionsintheAutoLISPReference.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>
PuttingItAllTogether
InLesson6,youlearnedthebasicmechanicsbehindreactor-basedapplications.InLesson7,youwilladdfunctionalitytothisknowledgeandcreateagardenpaththatknowshowandwhentomodifyitself.Aftertestingyourapplicationanddeterminingthatitworkssatisfactorily,youwillcreateaVisualLISP®applicationfromyourVLISPproject.
Youshouldconsiderthispartofthetutorialastheadvancedtopicssection.Ifyouareabeginner,youmaynotunderstandalltheAutoLISP®codepresentedhere.ThereareseveralAutoLISPbookslistedattheendofthislessonthatprovidemorethoroughinformationonsomeoftheadvancedAutoLISPconceptspresentedhere.
PlanningtheOverallReactorProcessAddingtheNewReactorFunctionalityRedefiningthePolylineBoundaryWrappingUptheCodeBuildinganApplicationWrappingUptheTutorialLISPandAutoLISPBooks
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>PuttingItAllTogether>
PlanningtheOverallReactorProcess
Youneedtodefineseveralnewfunctionsinthislesson.Ratherthanpresentyouwithdetailsonallaspectsofthenewcode,thislessonpresentsanoverviewandpointsouttheconceptsbehindthecode.Attheendofthelesson,youwillhaveallthesourcecodenecessarytocreateagardenpathapplicationidenticaltothesampleprogramyouraninLesson1.
Note Whenyouareinthemidstofdevelopinganddebuggingreactor-basedapplications,thereisalwaysthepotentialofleavingAutoCAD®inanunstablestate.Thiscanbecausedbyseveralsituations,suchasfailingtoremoveareactorfromdeletedentities.Forthisreason,itisrecommendedthatbeforebeginningLesson7,youshouldcloseVLISP,saveanyopenfilesasyoudoso,exitAutoCAD,thenrestartbothapplications.
BeginbyloadingtheprojectasitexistedattheendofLesson6.
Twoobviouspiecesofworkremaintobedoneinthegardenpathapplication:
Writingtheobjectreactorcallbacks.
Writingtheeditorreactorcallbacks.
Youalsoneedtoconsiderhowtohandletheglobalvariablesinyourprogram.Often,itisdesirabletohaveglobalsretainavaluethroughoutanAutoCADdrawingsession.Inthecaseofreactors,however,thisisnotthecase.Toillustratethis,imagineauserofyourgardenpathapplicationhasdrawnseveralgardenpathsinasingledrawing.Afterdoingthis,theusererasesthem,firstoneatatime,thentwoatatime,andsoon,untilallbutonepathiserased.
Lesson5introducedaglobalvariable*reactorsToRemove*,responsibleforstoringpointerstothereactorsforthepolylinesabouttobeerased.When*reactorsToRemove*isdeclaredingp:outline-erased,theeventletsyouknowthepolylineisabouttobeerased.Thepolylineisnotactuallyremoveduntilthegp:command-endedeventfires.
Thefirsttimetheuserdeletesapolyline,thingsworkjustasyouwouldexpect.Ingp:outline-erased,youstoreapointertothereactor.Whengp:command-endedfires,youremovethetilesassociatedwiththepolylinetowhichthereactorisattached,andalliswell.Then,theuserdecidestoerasetwopaths.Asaresult,yourapplicationwillgettwocallstogp:outline-erased,oneforeachpolylineabouttobeerased.Therearetwopotentialproblemsyoumustanticipate:
Whenyousetqthe*reactorsToRemove*variable,youmustaddapointertoareactortotheglobal,makingsurenottooverwriteanyvaluesalreadystoredthere.Thismeans*reactorsToRemove*mustbealiststructure,soyoucanappendreactorpointerstoit.Youcanthenaccumulateseveralreactorpointerscorrespondingtothenumberofpathstheuseriserasingwithinasingleerasecommand.
Everytimegp:command-will-startfires,indicatinganewcommandsequenceisbeginning,youshouldreinitializethe*reactorsToRemove*variabletonil.Thisisnecessarysothattheglobalisnotstoringreactorpointersfromthepreviouserasecommand.Ifyoudonotreinitializetheglobalvariableorusethecorrectdatastructure(inthiscase,alist),youwillgetunexpectedbehavior.Inthecaseofreactors,unexpectedbehaviorcanbefataltoyourAutoCADsession.
Hereisthechainofeventsthatneedstooccurforuserstoerasetwogardenpathswithasingleerasecommand.Notehowglobalvariablesarehandled:
Initiatetheerasecommand.Thistriggersthegp:command-will-startfunction.Set*reactorsToRemove*tonil.
Selecttwopolylines;yourapplicationisnotyetnotified.
PressENTERtoerasethetwoselectedpolylines.Yourapplicationgetsacallbacktogp:outline-erasedforoneofthepolylines.Additsreactorpointertothenullglobal,*reactorsToRemove*.Yourapplicationgetsacallbacktogp:outline-erasedforthesecondofthepolylines.Appenditsreactorpointertothe*reactorsToRemove*globalthatalreadycontainsthefirstreactor
pointer.
AutoCADdeletesthepolylines.
Yourcallbackfunctiongp:command-endedfires.Eliminateanytilesassociatedwiththereactorpointersstoredin*reactorsToRemove*.
Inadditiontothe*reactorsToRemove*global,yourapplicationalsoincludesa*polyToChange*global,whichstoresapointertoanypolylinethatwillbemodified.Twoadditionalglobalsfortheapplicationwillbeintroducedlaterinthislesson.
ReactingtoMoreUser-InvokedCommandsStoringInformationWithintheReactorObjects
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>PuttingItAllTogether>PlanningtheOverallReactorProcess>
ReactingtoMoreUser-InvokedCommands
Whenwritingareactor-basedapplication,youneedtohandleanycommandthataffectsyourobjectsinasignificantway.OneofyourprogramdesignactivitiesshouldbetoreviewallpossibleAutoCADeditingcommandsanddeterminehowyourapplicationshouldrespondtoeachone.Theformatofthereactor-tracesheetshownneartheendofLesson6isverygoodforthispurpose.Invokethecommandsyouexpectyourusertouse,andwritedownthekindofbehaviorwithwhichyourapplicationshouldrespond.Otheractionstoplanforinclude
DeterminewhattodowhenusersissueUNDOandREDOcommands.
DeterminewhattodowhenusersissuetheOOPScommandaftererasingentitieslinkedwithreactors.
Topreventaverycomplexsubjectfrombecomingvery,verycomplex,thetutorialdoesnottrytocoverallthepossibilitiesthatshouldbecovered,andthefunctionalitywithinthislessoniskepttoanabsoluteminimum.
Eventhoughyouwon'tbebuildinginthecompletefunctionalityfortheseextracommands,examinewhatafewadditionaleditingfunctionswouldrequireyoutodo:
Ifusersstretchapolylineboundary(usingtheSTRETCHcommand)severalthingsshouldhappen.Itcouldbestretchedinanydirection,notjustonthemajororminoraxis,sotheboundarymayendupinaveryoddshape.Inaddition,youneedtotakeintoconsiderationhowmanyverticeshavebeenstretched.Asituationwhereonlyonevertexisstretchedwillresultinapolylinequitedifferentfromoneinwhichtwoverticesaremoved.Inanycase,thetilesmustbeerasedandnewpositionsrecalculatedonceyoudeterminetheadjustmentsneededtotheboundary.
Ifusersmoveapolylineboundary,allthetilesshouldbeerased,then
redrawninthenewlocation.Thisisafairlysimpleoperation,becausethepolylineboundarydidnotchangeitssizeorshape.
Ifusersscaleapolylineboundary,youneedtomakeadecision.Shouldthetilesbescaledupaswell,sothatthepathcontainsthesamenumberoftiles?Or,shouldthetilesizeremainthesameandtheapplicationaddorremovetiles,dependingonwhetherthepolylinewasscaledupordown?
Ifusersrotateapolylineboundary,allthetilesshouldbeerased,thenredrawnintheneworientation.
Tobegin,though,justplanforthefollowing:
Warntheuseruponcommand-startthattheselectededitcommand(suchasstretch,move,orrotate)willhavedetrimentaleffectsonagardenpath.
Iftheuserproceeds,erasethetilesanddonotredrawthem.
Removethereactorsfromthepathoutline.
Note Inadditiontouser-invokedAutoCADcommands,entitiesmayalsobemodifiedordeletedthroughAutoLISPorObjectARX®applications.TheexampleprovidedintheGardenPathtutorialdoesnotcoverprogrammaticmanipulationofthegardenpathpolylineboundary,suchasthrough(entdel<polylineentity>).Inthiscase,theeditorreactorevents:vlr-commandWillStartand:vlr-commandEndedwillnotbetriggered.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>PuttingItAllTogether>PlanningtheOverallReactorProcess>
StoringInformationWithintheReactorObjects
Oneotherimportantaspectoftheapplicationyouneedtothinkaboutiswhatkindofinformationtoattachtotheobjectreactorthatiscreatedforeachpolylineentity.InLesson6,youaddedcodethatattachedthecontentsofgp_PathData(theassociationlist)tothereactor.Youexpandedthedatacarriedwithingp_PathDatabyaddinganewkeyedfield(100)totheassociationlist.Thisnewsublistisalistofpointerstoallthecircleentitiesassignedtoapolylineboundary.
Becauseoftheworkthatneedstobedonetorecalculatethepolylineboundary,fouradditionalkeyvaluesshouldbeaddedtogp_pathData:
;;;StartingPoint;
;;;(12.BottomStartingPoint)15------------------------14;
;;;(15.TopStartingPoint)||;
;;;EndingPoint10----pathAngle--->11;
;;;(13.BottomEndingPoint)||;
;;;(14.TopEndingPoint)12------------------------13;
;;;;
Theseorderedpointsarenecessarytorecalculatethepolylineboundarywhenevertheuserdragsacornergriptoanewlocation.Thisinformationalreadyexistswithinthegp:drawOutlinefunctioningpdraw.lsp.Butlookatthereturnvalueofthefunction.Currently,onlythepointertothepolylineobjectisreturned.Soyouneedtodothreethings:
Assembletheperimeterpointsintheformatrequired.
Modifythefunctionsothatitreturnstheperimeterpointlistsandthepointertothepolyline.
ModifytheC:GPathfunctionsothatitcorrectlydealswiththenewformatofthevaluesreturnedfromgp:drawOutline.
Assemblingtheperimeterpointlistsissimple.Lookatthecodeingp:drawOutline.Thelocalvariablep1correspondstothekeyvalue12,p2to13,p3to14,andp4to15.Youcanaddthefollowingfunctioncalltoassemblethisinformation:
(setqpolyPoints(list
(cons12p1)
(cons13p2)
(cons14p3)
(cons15p4)
))
Modifyingthefunctionsothatitreturnsthepolylineperimeterpointsandthepolylinepointerisalsoeasy.Asthelastexpressionwithingp:drawOutline,assemblealistofthetwoitemsofinformationyouwanttoreturn.
(listplinepolyPoints)
Toaddprogramlogictosavethepolylineperimeterpoints
1. Modifygp:drawOutlinebymakingthechangesshowninboldfaceinthefollowingcode(don'toverlooktheadditionofthepolyPointslocalvariabletothedefunstatement):
(defungp:drawOutline(BoundaryData/PathAngle
WidthHalfWidthStartPtPathLength
angm90angp90p1p2
p3p4poly2Dpoints
poly3DpointsplineStylepline
polyPoints
)
;;extractthevaluesfromthelistBoundaryData.
(setqPathAngle(cdr(assoc50BoundaryData))
Width(cdr(assoc40BoundaryData))
HalfWidth(/Width2.00)
StartPt(cdr(assoc10BoundaryData))
PathLength(cdr(assoc41BoundaryData))
angp90(+PathAngle(Degrees->Radians90))
angm90(-PathAngle(Degrees->Radians90))
p1(polarStartPtangm90HalfWidth)
p2(polarp1PathAnglePathLength)
p3(polarp2angp90Width)
p4(polarp3(+PathAngle
(Degrees->Radians180))PathLength)
poly2Dpoints(apply'append
(mapcar'3dPoint->2dPoint(listp1p2p3p4))
)
poly3Dpoints(mapcar'float(appendp1p2p3p4))
;;getthepolylinestyle.
plineStyle(strcase(cdr(assoc4BoundaryData)))
;;AddpolylinetothemodelspaceusingActiveXautomation.
pline(if(=plineStyle"LIGHT")
;;createalightweightpolyline.
(vla-addLightweightPolyline
*ModelSpace*;GlobalDefinitionforModelSpace
(gp:list->variantArraypoly2Dpoints)
;dataconversion
);_endofvla-addLightweightPolyline
;;orcreatearegularpolyline.
(vla-addPolyline
*ModelSpace*
(gp:list->variantArraypoly3Dpoints)
;dataconversion
);_endofvla-addPolyline
);_endofif
polyPoints(list
(cons12p1)
(cons13p2)
(cons14p3)
(cons15p4)
)
);_endofsetq
(vla-put-closedplineT)
(listplinepolyPoints)
);_endofdefun
2. ModifytheC:GPathfunction(ingpmain.lsp).Lookforthelineofcodethatcurrentlylookslikethis:
(setqPolylineName(gp:drawOutlinegp_PathData))
Changeitsoitappearsasfollows:
(setqPolylineList(gp:drawOutlinegp_PathData)
PolylineName(carPolylineList)
gp_pathData(appendgp_pathData(cadrPolylineList))
);_endofsetq
Thegp_PathDatavariablenowcarriesalltheinformationrequiredbythereactorfunction.
3. AddPolylineListtothelocalvariablessectionofthe
AutoLISPTutorial>PuttingItAllTogether>
AddingtheNewReactorFunctionality
InLesson6,youhookedupcallbackfunctiongp:command-will-starttothereactorevent:vlr-commandWillStart.Asitcurrentlyexists,thefunctiondisplayssomemessagesandinitializestwoglobalvariables,*polyToChange*and*reactorsToRemove*,tonil.
Toaddfunctionalitytothegp:command-will-startcallbackfunction
1. Openyourgpreact.lspfile.
2. Inthegp:command-will-startfunction,addtwovariablestothesetqfunctioncallbymodifyingitasfollows:
;;Resetallfourreactorglobalstonil.
(setq*lostAssociativity*nil
*polyToChange*nil
*reactorsToChange*nil
*reactorsToRemove*nil)
3. Replacetheremainingcodeingp:command-will-start,uptothelastprincfunctioncall,withthefollowingcode:
(if(member(setqcurrentCommandName(carcommand-list))
'("U""UNDO""STRETCH""MOVE"
"ROTATE""SCALE""BREAK""GRIP_MOVE"
"GRIP_ROTATE""GRIP_SCALE""GRIP_MIRROR")
);_endofmember
(progn
(setq*lostAssociativity*T)
(princ"\nNOTE:The")
(princcurrentCommandName)
(princ"commandwillbreakapath'sassociativity.")
);_endofprogn
);_endofif
Thiscodecheckstoseeiftheuserissuedacommandthatbreaksthe
associativitybetweenthetilesandthepath.Iftheuserissuedsuchacommand,theprogramsetsthe*lostAssociativity*globalvariableandwarnstheuser.Asyouexperimentwiththegardenpathapplication,youmaydiscoveradditionaleditingcommandsthatcanmodifythegardenpathandcausethelossofassociativity.Addthesecommandstothequotedlistsothattheuserisawareofwhatwillhappen.Whenthisfunctionfires,theuserhasstartedacommandbuthasnotselectedanyentitiestomodify.Theusercouldstillcancelthecommand,leavingthingsunchanged.AddingActivitytotheObjectReactorCallbackFunctionsDesigningthegp:command-endedCallbackFunctionHandlingMultipleEntityTypesUsingActiveXMethodsinReactorCallbackFunctionsHandlingNonlinearReactorSequencesCodingthecommand-endedFunctionUpdatinggp:Calculate-and-Draw-TilesModifyingOtherCallstogp:Calculate-and-Draw-Tiles
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>PuttingItAllTogether>AddingtheNewReactorFunctionality>
AddingActivitytotheObjectReactorCallbackFunctions
InLesson6,youregisteredtwocallbackfunctionswithobjectreactorevents.Thegp:outline-erasedfunctionwasassociatedwiththe:vlr-erasedreactorevent,andgp:outline-changedwasassociatedwiththe:vlr-modifiedevent.Youneedtomakethesefunctionsdowhattheyareintendedtodo.
Tomaketheobjectreactorcallbackfunctionsdowhattheyareintendedtodo
1. Ingpreact.lsp,changegp:outline-erasedsoitappearsasfollows:
(defungp:outline-erased(outlinePolyreactorparameterList)
(setq*reactorsToRemove*
(consreactor*reactorsToRemove*))
(princ)
);_endofdefun
Thereisjustoneoperationperformedhere.Thereactorattachedtothepolylineissavedtoalistofallreactorsthatneedtoberemoved.(Remember:thoughreactorsareattachedtoentities,theyareseparateobjectsentirely,andtheirrelationshipstoentitiesneedtobemanagedjustascarefullyasregularAutoCADentities.)
2. Changegp:outline-changedtoreflectthefollowingcode:
(defungp:outline-changed(outlinePolyreactorparameterList)
(if*lostAssociativity*
(setq*reactorsToRemove*
(consreactor*reactorsToRemove*))
(setq*polytochange*outlinePoly
*reactorsToChange*(consreactor*reactorsToChange*))
)
(princ)
)
Therearetwocategoriesoffunctionsthatcanmodifythepolylineoutline.Thefirstcategorycontainsthosecommandsthatwillbreakthepath'sassociativitywithitstiles.Youcheckedforthisconditioningp:command-will-startandsetthe*lostAssociativity*globalvariableaccordingly.Inthiscase,thetilesneedtobeerased,andthepathisthenintheuser'shands.TheothercategoryisthegripmodeoftheSTRETCHcommand,whereassociativityisretainedandyouneedtostraightenouttheoutlineaftertheuserhasfinisheddraggingavertextoanewlocation.The*polyToChange*variablestoresaVLA-Objectpointertothepolylineitself.Thiswillbeusedinthegp:command-endedfunctionwhenitcomestimetorecalculatethepolylineborder.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>PuttingItAllTogether>AddingtheNewReactorFunctionality>
Designingthegp:command-endedCallbackFunction
Thegp:command-endededitorreactorcallbackfunctioniswheremostactiontakesplace.Untilthisfunctioniscalled,thegardenpathborderpolylinesare“openformodify;”thatis,usersmaystillbemanipulatingthebordersinAutoCAD.Withinthereactorsequence,youhavetowaituntilAutoCADhasdoneitspartoftheworkbeforeyouarefreetodowhatyouwanttodo.
Thefollowingpseudo-codeillustratesthelogicofthegp:command-endedfunction:
Determinetheconditionofthepolyline.
CONDITION1-POLYLINEERASED(Erasecommand)
Erasethetiles.
CONDITION2-LOSTASSOCIATIVITY(Move,Rotate,etc.)
Erasethetiles.
CONDITION3-GRIP_STRETCH-REDRAWANDRE-TILE
Erasethetiles.
Getthecurrentboundarydatafromthepolyline.
Ifitisalightweightpolyline,
Processboundarydataas2D
Else
Processboundarydataas3D
Endif
Redefinethepolylineborder(passinparametersofthecurrent
boundaryconfiguration,aswellastheold).
Getthenewboundaryinformationandputitintotheformat
requiredforsettingbackintothepolylineentity.
Regeneratethepolyline.
Redrawthetiles(forceActiveXdrawing).
Puttherevisedboundaryinformationbackintothereactor
namedin*reactorsToChange*.
Endfunction
Thepseudo-codeisrelativelystraightforward,butthereareseveralimportantdetailsburiedinthepseudo-code,andtheyarethingsyouwouldnotbeexpectedtoknowatthispoint.
AutoLISPTutorial>PuttingItAllTogether>AddingtheNewReactorFunctionality>
HandlingMultipleEntityTypes
Thefirstdetailisthatyourapplicationmaydrawtwokindsofpolylines:old-styleandlightweight.Thesedifferentpolylinetypesreturntheirentitydataindifferentformats.Theold-stylepolylinereturnsalistoftwelvereals:foursetsofX,Y,andZpoints.Thelightweightpolyline,though,returnsalisteightreals:foursetsofXandYpoints.
Youneedtodosomecalculationstodeterminetherevisedpolylineboundaryafterausermovesoneofthevertices.Itwillbealoteasiertodothecalculationsifthepolylinedatahasaconsistentformat.
TheLesson7versionoftheutils.lspfilecontainsfunctionstoperformthenecessaryformatconversions:xyzList->ListOfPointsextractsandformats3Dpointlistsintoalistoflists,andxyList->ListOfPointsextractsandformats2Dpointlistsintoalistoflists.
Toaddthecodeforconvertingpolylinedataintoaconsistentformat
1. Ifyouhaveacopyofutils.lspopeninaVLISPtexteditorwindow,closeit.
2. Copytheversionofutils.lspfromtheTutorial\VisualLISP\Lesson7directoryintoyourworkingdirectory.Inadditiontothetwofunctionsthatreformatpolylinedata,utils.lspcontainsadditionalutilityfunctionsneededinhandlinguseralterationstothegardenpath.
3. Openutils.lspinaVLISPtexteditorwindowandreviewthenewcode.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>PuttingItAllTogether>AddingtheNewReactorFunctionality>
UsingActiveXMethodsinReactorCallbackFunctions
Theseconddetailappearinginthepseudo-codeshowsupneartheend,atthestepforredrawingthetiles.Hereisthepseudo-codestatement:
Redrawthetiles(forceActiveXdrawing)
Theparentheticalphrasesaysitall:forceActiveXdrawing.Whyisthisrequired?Whycan'ttheapplicationusetheobjectcreationpreferencestoredintheassociationsublist?
Theanswerisyoucannotusethecommandfunctionforentitycreationwithinareactorcallbackfunction.ThishastodowithsomeinternalworkingsofAutoCAD.YouneedtoforcethetiledrawingroutinetouseActiveX.Youwillhearmoreaboutthisissuelaterinthislesson.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>PuttingItAllTogether>AddingtheNewReactorFunctionality>
HandlingNonlinearReactorSequences
Thefinalimportantdetaildealswithaquirkinthecommand/reactorsequenceinAutoCADwhenusersmodifyapolylinebyusingthespecializedGRIPcommands.Thesecommands,suchasGRIP_MOVEandGRIP_ROTATE,areavailablefromashortcutmenuafteryouselectthegripofanobjectandright-click.ThereactorsequenceisnotaslinearasasimpleMOVEorERASEcommand.Ineffect,theuserischangingtoadifferentcommandwhileinthemidstofanother.Todemonstratethissituation,youcanloadthecodefromLesson6thattracesthesequenceofreactorevents.OrsimplyreviewthefollowingannotatedVLISPConsolewindowoutputtoseewhathappens:
;;Tostart,selectthepolylineandsomeofthecirclesbyusinga
;;crossingselectionbox.Theitemsintheselectionset--
;;thechosencirclesandthepolyline--arenowshownwithgripson.
;;Toinitiatethesequence,clickononeofthepolylinegrips:
(GP:COMMAND-WILL-START#<VLR-Command-reactor>(GRIP_STRETCH))
;;Nowchangethecommandtoamovebyright-clickingandchoosing
;;MOVEfromthepop-upmenu.Noticethatthecommand-ended
;;reactorfiresinordertocloseouttheGRIP_STRETCHcommand
;;withouthavingfiredanobjectreactorevent:
(GP:COMMAND-ENDED#<VLR-Command-reactor>(GRIP_STRETCH))
(GP:COMMAND-WILL-START#<VLR-Command-reactor>(GRIP_MOVE))
;;Nowdragtheoutline(andtheselectedcircles)toanewlocation.
(GP:OUTLINE-CHANGED#<VLA-OBJECTIAcadLWPolyline028f3188>
#<VLR-Object-reactor>nil)
(GP:COMMAND-ENDED#<VLR-Command-reactor>(GRIP_MOVE))
Thisdemonstratesthatyoucannotbecertainyourobjectreactorcallbackswillbecalledinallcases.
Thereisarelatedquirkinthissequence.Evenduringthefinalcommand-endedcallback,thecirclesthatarestillpartofthegripselectionsetcannotbedeleted.ThesecirclesarestillopenbyAutoCAD.Ifyouattempttoerasethemduringthecommand-endedcallback,youcancrashAutoCAD.Togetaroundthis,youcan
useanotherglobalvariabletostorealistofpointerstothetileobjectsuntiltheycanbedeleted.
Toprocessnonlinearreactorsequences
1. Addthefollowingfunctiontothegpreact.lspfile:
(defungp:erase-tiles(reactor/reactorDatatilestile)
(if(setqreactorData(vlr-datareactor))
(progn
;;Tilesinthepatharestoredasdatainthereactor.
(setqtiles(cdr(assoc100reactorData)))
;;Erasealltheexistingtilesinthepath.
(foreachtiletiles
(if(and(null(membertile*Safe-to-Delete*))
(not(vlax-erased-ptile))
)
(progn
(vla-put-visibletile0)
(setq*Safe-to-Delete*(constile*Safe-to-Delete*))
)
)
)
(vlr-data-setreactornil)
)
)
)
Thisnewfunctionwillbeusedinthefirstphaseoferasingtiles.Noticethatthetilesarenotactuallyerased:theyaremadeinvisibleandareaddedtoaglobalvariablenamed*Safe-to-Delete*.
2. Addthefollowingfunctiontothegpreact.lspfile:
(defunGp:Safe-Delete(activeCommand)
(if(not(equal
(strcase(substractiveCommand15))
"GRIP_"
)
)
(progn
(if*Safe-to-Delete*
(foreachItem*Safe-to-Delete*
(if(not(vlax-erased-pItem))
(vla-eraseitem)
)
)
)
(setq*Safe-to-Delete*nil)
)
)
)
ThisfunctioncanbeinvokedatatimewhenaGRIP_MOVEorGRIP_STRETCHcommandisnotbeingexecuted.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>PuttingItAllTogether>AddingtheNewReactorFunctionality>
Codingthecommand-endedFunction
Nowthatyouhaveseenthepseudo-codeandhandledsomeimportantdetails,replacethestubbed-outcodeinthegp:command-endedreactorcallbackwiththefollowing:
(defungp:command-ended(reactorcommand-list
/objReactor
reactorToChangereactorData
coordinateValuescurrentPoints
newReactorDatanewPts
tileList
)
(cond
;;CONDITION1-POLYLINEERASED(Erasecommand)
;;Ifoneormorepolylinebordersarebeingerased(indicated
;;bythepresenceof*reactorsToRemove*),erasethetiles
;;withintheborder,thenremovethereactor.
(*reactorsToRemove*
(foreachobjReactor*reactorsToRemove*
(gp:erase-tilesobjReactor)
)
(setq*reactorsToRemove*nil)
)
;;CONDITION2-LOSTASSOCIATIVITY(Move,Rotate,etc.)
;;Ifassociativityhasbeenlost(undo,move,etc.),then
;;erasethetileswithineachborder
;;
((and*lostassociativity**reactorsToChange*)
(foreachreactorToChange*reactorsToChange*
(gp:erase-tilesreactorToChange)
)
(setq*reactorsToChange*nil)
)
;;CONDITION3-GRIP_STRETCH
;;Inthiscase,theassociativityofthetilestothepathis
;;kept,butthepathandthetileswillneedtobe
;;recalculatedandredrawn.AGRIP_STRETCHcanonlybe
;;performedonasinglePOLYLINEatatime.
((and(not*lostassociativity*)
*polytochange*
*reactorsToChange*
(member"GRIP_STRETCH"command-list)
;;foraGRIP_STRETCH,therewillbeonlyonereactorin
;;theglobal*reactorsToChange*.
(setqreactorData
(vlr-data(setqreactorToChange
(car*reactorsToChange*)
)
)
)
)
;;First,erasethetileswithinthepolylineborder.
(gp:erase-tilesreactorToChange)
;;Next,getthecurrentcoordinatevaluesofthepolyline
;;vertices.
(setqcoordinateValues
(vlax-safearray->list
(vlax-variant-value
(vla-get-coordinates*polyToChange*)
)
)
)
;;Iftheoutlineisalightweightpolyline,youhave
;;2dpoints,souseutilityfunctionxyList->ListOfPoints
;;toconvertthecoordinatedataintolistsof
;;((xy)(xy)...)points.Otherwise,usethe
;;xyzList->ListOfPointsfunctionthatdeals
;;with3dpoints,andconvertsthecoordinatedatainto
;;listsof((xyz)(xyz)...)points.
(setqCurrentPoints
(if(=(vla-get-ObjectName*polytochange*)"AcDbPolyline")
(xyList->ListOfPointscoordinateValues)
(xyzList->ListOfPointscoordinateValues)
)
)
;;SendthisnewinformationtoRedefinePolyBorder--this
;;willreturnthenewPolylineBorder
(setqNewReactorData
(gp:RedefinePolyBorderCurrentPointsreactorData)
)
;;GetalltheborderPointsand...
(setqnewpts(list(cdr(assoc12NewReactorData))
(cdr(assoc13NewReactorData))
(cdr(assoc14NewReactorData))
(cdr(assoc15NewReactorData))
)
)
;;...updatetheoutlineofthepolylinewiththenewpoints
;;calculatedabove.Ifdealingwithalightweightpolyline,
;;convertthesepointsto2D(sinceallthepointsin
;;newptsare3D),otherwiseleavethemalone.
(if(=(cdr(assoc4NewReactorData))"LIGHT")
(setqnewpts(mapcar'(lambda(point)
(3dPoint->2dPointPoint)
)
newpts
)
)
)
;;Nowupdatethepolylinewiththecorrectpoints.
(vla-put-coordinates
*polytochange*
;;Fordescriptionofthelist->variantArrayseeutils.lsp.
(gp:list->variantArray(apply'appendnewpts))
)
;;NowusethecurrentdefinitionoftheNewReactorData,
;;whichisreallythesameasthegardenpathdata
;;structure.Theonlyexceptionisthatthefield(100)
;;containingthelistoftilesisnil.ThisisOKsince
;;gp:Calculate-and-Draw-Tilesdoesnotrequirethisfield
;;todrawthetiles.Infactthisfunctioncreatesthetiles
;;andreturnsalistofdrawntiles.
(setqtileList(gp:Calculate-and-Draw-Tiles
;;pathdatalistwithoutcorrecttilelist
NewReactorData
;;Objectcreationfunction
;;Withinareactorthis*MUST*beActiveX
"ActiveX"
)
)
;;Nowthatyouhavereceivedallthetilesdrawn,rebuild
;;thedatastructurewiththecorrecttileListvalueand
;;resetthedatapropertyinthereactor.
;;Updatethetilesassociatedwiththepolylineborder.
(setqNewReactorData
(subst(cons100tileList)
(assoc100NewReactorData)
NewReactorData
)
)
;;Bynowyouhavethenewdataassociatedwiththepolyline.
;;Allthereislefttodoisassociateitwiththereactor
;;usingvlr-data-set.
(vlr-data-set(car*reactorsToChange*)NewReactorData)
;;Removeallreferencestothetemporary
;;variables*polytochange*and*reactorsToChange*.
(setq*polytochange*nil
*reactorsToChange*nil
)
)
)
;;Deleteanyitemsinthe*Safe-to-Delete*globalifyoucan!!!
(Gp:Safe-Delete(carcommand-list))
(princ)
)
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>PuttingItAllTogether>AddingtheNewReactorFunctionality>
Updatinggp:Calculate-and-Draw-Tiles
Earlierinthislesson,itwasnotedthatyouneedtoforcegp:Calculate-and-Draw-TilestouseActiveXtocreateobjectswheninvokedfromareactorcallback.Thismeansoverridingtheobjectcreationstyle(ActiveX,entmake,orcommand)chosenbytheuser,ifnecessary.Thecodeyoujustupdated,inthegp:command-endedfunction,containsthefollowinginvocationofthetiledrawingroutine:
(setqtileList(gp:Calculate-and-Draw-Tiles
;;pathdatalistwithoutcorrecttilelist.
NewReactorData
;;Objectcreationfunction.
;;Withinareactorthis*MUST*beActiveX.
"ActiveX"
)
)
Twoparametersarepassedtogp:Calculate-and-Draw-Tiles:NewReactorData(whichisalistintheformoftheoriginalgp_PathDataassociationlist)andthestring"ActiveX"(whichwillsettheobjectcreationstyle).Buttakealookatthecurrentdefinitionofgp:Calculate-and-Draw-Tiles.(Incaseyouhaveforgotten,thisfunctionisdefinedingpdraw.lsp.)Hereisthepartofthefunctionthatdeclarestheparametersandlocalvariables:
(defungp:Calculate-and-Draw-Tiles(BoundaryData/
PathLengthTileSpace
TileRadiusSpaceFilled
SpaceToFillRowSpacing
offsetFromCenterrowStartPoint
pathWidthpathAngle
ObjectCreationStyleTileList)
Noticeonlythatoneparameteriscurrentlyspecified,and
ObjectCreationStyleisidentifiedasalocalvariable.ReviewhowtheObjectCreationStylevariableisset,whichisalittlefartherintothefunction:
(setqObjectCreationStyle(strcase(cdr(assoc3BoundaryData))))
TheObjectCreationStyleiscurrentlysetinternallywithinthefunctionbyretrievingthevaluetuckedawayintheBoundaryDatavariable(theassociationlist).Butnowyouneedtobeabletooverridethatvalue.
Tomodifygp:Calculate-and-Draw-Tilestoacceptanobjectcreationstyleargument
1. AddtheObjectCreationStylevariabletothefunctionargument.
2. RemoveObjectCreationStylefromthelocalvariables.Thedefunstatementforthefunctionshouldlooklikethefollowing:
(defungp:Calculate-and-Draw-Tiles(BoundaryData
ObjectCreationStyle
/PathLengthTileSpace
TileRadiusSpaceFilled
SpaceToFileRowSpacing
offsetFromCenterrowStartPoint
pathWidthpathAngle
TileList);removeObjectCreationStylefromlocals
Notethatifyoudeclareavariablebothasaparameter(beforetheslash)andasalocalvariable(aftertheslash),VLISPwillpointthisouttoyou.Forexample,ifyoudeclareObjectCreationStyleasbothaparameterandavariable,thenusetheVLISPsyntaxcheckingtoolonthegp:Calculate-and-Draw-Tilesfunction,thefollowingmessagewillappearintheBuildOutputwindow:
;***WARNING:samesymbolbeforeandafter/inargumentslist:OBJECTCREATIONSTYLE
3. Modifythefirstsetqexpressionwithingp:Calculate-and-Draw-Tilessothatitlookslikethefollowing:
(setq
PathLength(cdr(assoc41BoundaryData))
TileSpace(cdr(assoc43BoundaryData))
TileRadius(cdr(assoc42BoundaryData))
SpaceToFill(-PathLengthTileRadius)
RowSpacing(*(+TileSpace(*TileRadius2.0))
(sin(Degrees->Radians60))
)
SpaceFilledRowSpacing
offsetFromCenter0.0
offsetDistance/(+(*TileRadius2.0)TileSpace)2.0)
rowStartPointcdr(assoc10BoundaryData))
pathWidthcdr(assoc40BoundaryData))
pathAnglecdr(assoc50BoundaryData))
);_endofsetq
(if(notObjectCreationStyle)
(setqObjectCreationStyle(strcase(cdr(assoc3BoundaryData))))
)
TheoriginalassignmentstatementforObjectCreationStylehasbeenremoved.ThecodenowcheckstoseeifavaluehasbeenprovidedforObjectCreationStyle.IfObjectCreationStyleisnotset(thatis,thevalueisnil),thefunctionassignsitavaluefromtheBoundaryDatavariable.Thereisonemoreseriesofchangesyouneedtomaketogp:Calculate-and-Draw-Tiles.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>PuttingItAllTogether>AddingtheNewReactorFunctionality>
ModifyingOtherCallstogp:Calculate-and-Draw-Tiles
Inthereactorcallback,ahard-codedstring"ActiveX"ispassedtogp:Calculate-and-Draw-TilesastheObjectCreationStyleargument.Butwhatabouttheothertimesgp:Calculate-and-Draw-Tilesisinvoked?
IfyourememberbacktoLesson4,itwaspointedoutthatwheneveryouchangeastubbed-outfunction,youneedtoaskthefollowingquestions:
Hasthefunctioncall(invocation)changed?Thatis,doesthefunctionstilltakethesamenumberofarguments?
Doesthefunctionreturnsomethingdifferent?
Thesamequestionsneedtobeaskedanytimeyoumakeasignificantchangetoaworkingfunctionasyoubuild,refine,andupdateyourapplications.Inthiscase,youneedtofindanyotherfunctionsinyourprojectthatinvokegp:Calculate-and-Draw-Tiles.VLISPhasafeaturethathelpsyoudothis.
Tofindallcallstogp:Calculate-and-Draw-Tilesinyourproject
1. IntheVLISPtexteditorwindow,double-clickonthewordgp:Calculate-and-Draw-Tileswithinthegpdraw.lspfile.
2. ChooseSearch FindfromtheVLISPmenu.
Becauseyoupreselectedthefunctionname,itisalreadylistedasthestringtosearchfor.
3. SelecttheProjectbuttonlistedunderSearchintheFinddialogbox.
Whenyouselectthisoption,theFinddialogboxexpandsatthebottom,andyoucanselecttheprojecttobesearched.
4. Specifyyourcurrentprojectname,thenchoosetheFindbutton.VLISPdisplaystheresultsintheFindoutputwindow:
5. LookattheresultsintheFindOutputwindowanddeterminewhetherthereareanyotherlocationsinyourcodewhereyoumakeacalltogp:Calculate-and-Draw-Tiles.Thereshouldonlybeone:alocationwithingpmain.lsp.
6. IntheFindOutputwindow,double-clickonthelineofcodecallinggp:Calculate-and-Draw-Tiles.VLISPactivatesatexteditorwindowandtakesyourighttothatlineofcodeingpmain.lsp.Thecodecurrentlyappearsasfollows:
(setqtilelist(gp:Calculate-and-Draw-Tilesgp_PathData))
7. Replacethelineofcodewiththefollowing:
(setqtilelist(gp:Calculate-and-Draw-Tilesgp_PathDatanil))
Whynil?Takeanotherlookatthepseudo-code:
IfObjectCreationStyleisnil,assignitfromtheBoundaryData.
Passingnilasaparametertogp:Calculate-and-Draw-Tilescausesthatfunctiontochecktheuser'schoiceofhowtodrawthetiles(asdeterminedbythedialogboxselectionandstoredingp_PathData).Subsequentcallsfromthecommand-endedreactorcallback,however,willoverridethisbehaviorbyforcingtheuseofActiveX.
Congratulations!Younowhavethebasicreactorfunctionalityinplace.Ifyouprefer,copythegpmain.lspandgpdraw.lspfilesfromtheTutorial\VisualLISP\Lesson7intoyourworkingdirectoryandexaminethecompleted,debuggedcode.
Thereisstillalotofworktobedone,anditisalltriggeredfromthisfragmentofcodeinthegp:Command-endedfunction:
(setqNewReactorData
AutoLISPTutorial>PuttingItAllTogether>
RedefiningthePolylineBoundary
Youhaveworkedhardtogettothispoint,andyourbrainhasprobablyhadenoughnewconcepts,terms,commands,andimperativesforawhile.Withthatinmind,itisrecommendedthatyoucopythesamplecodesuppliedwiththetutorial,ratherthanenteringitonyourown.
Tocopythecodeusedtoredefinethepolylineboundary
1. Copythefilegppoly.lspfromtheTutorial\VisualLISP\Lesson7directoryintoyourworkingdirectory.
2. Intheprojectwindowforyourproject,choosetheProjectPropertiesbutton.
3. Addthegppoly.lspfiletotheproject.
4. ChooseOKtoaccepttheprojectwiththeadditionalfile.
5. Intheprojectwindow,double-clickthegppoly.lspfiletoopenit.LookingattheFunctionsingppoly.lspUnderstandingthegp:RedefinePolyBorderFunctionUnderstandingthegp:FindMovedPointFunctionUnderstandingthegp:FindPointInListFunctionUnderstandingthegp:recalcPolyCornersFunctionUnderstandingthegp:pointEqual,gp:rtos2,andgp:zeroSmallNumFunctions
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>PuttingItAllTogether>RedefiningthePolylineBoundary>
LookingattheFunctionsingppoly.lsp
Thefilegppoly.lspcontainsanumberoffunctionsrequiredforstraighteningapolylinewhenasinglegriphasbeenstretched.Onlysomeofthesefunctionswillbeexplainedindepthinthistutorial.
Note ThissectionoftheGardenPathtutorialcontainssomeofthemostcomplexcodeandconceptsintheentirelesson.Ifyouareabeginner,youmaywanttojumpaheadtotheBuildinganApplicationsection.
Thefunctionswithinthegppoly.lspfileareorganizedinawaythatyoumayhavenoticedinotherAutoLISPsourcecodefiles.Thehighest-levelfunction,oftenthemain,orC:function(inthiscase,gp:Redefine-PolyBorder),islocatedatthebottomofthefile.Thefunctionscalledwithinthemainfunctionaredefinedaboveitwithinthesourcefile.Thisconventiongoesbacktotheolddaysofprogramming,whensomedevelopmentenvironmentsrequiredthatfilesbeorganizedthisway.WithVLISP,thisisamatterofpersonalstyle;thereisnorequirementthatyouorganizeyourfunctionsinanyspecificsequence.
Beforedivingintothedetails,stepbackandlookatwhatneedstobedonetorecalculateanddrawthegardenpathboundary.Thefollowingillustrationshowsanexampleofagardenpath,alongwiththeassociationlistkeypointsstoredinthereactordata:
Inthisexample,the12keypointisthelower-leftcorner,13islower-right,andsoon.Iftheusermovestheupper-rightpoint(the14keypoint),theprogramwillneedtorecalculatetwoexistingpoints—thelower-right(13)andupper-left(15).
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>PuttingItAllTogether>RedefiningthePolylineBoundary>
Understandingthegp:RedefinePolyBorderFunction
Thefollowingpseudo-codeshowsthelogicbehindthemainfunction,gp:RedefinePolyBorder:
Functiongp:RedefinePolyBorder
Extractthepreviouspolylinecornerpoints(12,13,14,and15
keyvalues).
Findthemovedcornerpointbycomparingtheprevious
polylinecornerpointswiththecurrentcornerpoints.
(Theone"misfit"pointwillbethepointthatmoved.)
Setthenewcornerpointsbyrecalculatingthetwopoints
adjacenttothemovedpoint.
Updatethenewcornerpointsinthereactordata(thatwill
bestoredbackinthereactorforthemodifiedpolyline).
Updateotherinformationinthereactordata.(Startpoint,
endpoint,width,andlengthofpathneedtoberecalculated.)
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>PuttingItAllTogether>RedefiningthePolylineBoundary>
Understandingthegp:FindMovedPointFunction
Thegp:FindMovedPointfunctioncontainssomeverypowerfulLISPexpressionsdealingwithlistmanipulation.Essentially,whatthisfunctiondoesiscomparethelistofthecurrentpolylinepoints(aftertheuserdraggedonetoanewlocation)tothepreviouspoints,andreturnthekeyedlist(the13<xvalue><yvalue>)forthemovedpoint.
Thebestwaytofigureouthowthisfunctionworksistostepthroughthecodeandwatchthevaluesthatitmanipulates.Setabreakpointrightatthefirstexpression(setqresult...)andwatchthefollowingvariableswhileyoustepthroughthefunction:
KeyListToLookFor
PresentPoints
KeyedList
Result
KeyListStatus
MissingKey
MovedPoint
Themapcarandlambdafunctionswillbeexaminedinthefollowingsection.Fornow,however,followthecommentsinthecodetoseeifyoucanunderstandwhatishappeningwithinthefunctions.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>PuttingItAllTogether>RedefiningthePolylineBoundary>
Understandingthegp:FindPointInListFunction
Thefunctionheaderinthesourcecodeexplainshowgp:FindPointInListtransformstheinformationitworkswith.Likethepreviousfunction,Gp:FindMovedPoint,thisfunctionusesLISP'slistmanipulationcapabilitiestoperformthework.Whenoperatingwithlists,youwilloftenseethemapcarandlambdafunctionsusedtogetherastheyarehere.Atfirst,thesearestrangeandconfusingfunctions,withnamesthatdonotindicatewhattheydo.Onceyoulearnhowtousethem,however,youwillfindthemtobetwoofthemostpowerfulfunctionswithintheAutoLISPrepertoire.Whatfollowsisabriefoverviewofmapcarandlambda.
Themapcarfunctionapplies(maps)anexpressiontoeveryiteminalist.Forexample,givenalistoftheintegers1,2,3,and4,mapcarcanbeusedtoapplythe1+functiontoadd1toeachnumberinthelist:
_$(mapcar'1+'(1234))(2345)
Aninitialdefinitionformapcaristhatitmapsthefunctiongiveninthefirstparametertothesuccessiveitemsinthesecondparameter—thelist.Theresultingvaluefromamapcaroperationisthelisttransformedbywhateverfunctionorexpressionwasappliedtoit.(Actually,mapcarcandomorethanthat,butfornowthisdefinitionwillsuffice.)
Inthesuppliedexample,everyvalueinthelist'(1234)waspassedtothe1+function.Essentially,mapcarperformedthefollowingoperations,assemblingtheresultingvaluesinalist:
(1+1)->2
(1+2)->3
(1+3)->4
(1+4)->5
Hereisanotherexampleofmapcar,thistimeusingthenullfunctiontotestwhetherornotthevaluesinalistarenull(nottrue)values:
_$(mapcar'null(list1
(=3"3")nil"Steve"))(nilTTnil)
Whathappenedinthiscodewasessentiallythefollowing:
(null1)->nil
(null(=3"3")->T
(nullnil)->T
(null"Steve")->nil
YoucanusemanyexistingAutoLISPfunctionswithinamapcar.Youcanalsouseyourownfunctions.Forexample,imagineyouhavejustcreatedaverypowerfulfunctionnamedequals2:
_$(defunequals2(num)(=
num2))EQUALS2
_$(mapcar'equals2'(1
234))(nilTnilnil)
Okay,soequals2isnotallthatpowerful.Butitisinsuchcasesthatlambdacomesinhandy.Youcanuselambdaincaseswhereyoudonotwantorneedtogothroughtheoverheadofdefiningafunction.Youwillsometimesseelambdadefinedasananonymousfunction.Forexample,insteadofdefiningafunctioncalledequals2,youcouldwritealambdaexpressiontoperformthesameoperationwithouttheoverheadofthefunctiondefinition:
_$(mapcar'(lambda(num)
(=num2))'(1234))(nilTnilnil)
Whathappenedinthecodewasthis:
(=12)->nil
(=22)->T
(=32)->nil
(=42)->nil
Withthisknowledge,seeifthegp:FindPointInListfunctionmakessense.Again,reviewthecommentswithinthesourcecode.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>PuttingItAllTogether>RedefiningthePolylineBoundary>
Understandingthegp:recalcPolyCornersFunction
Thekeytounderstandinghowgp:recalcPolyCornersworksistorevisitthediagramshowingwhatthekeyvaluesof12through15represent:
Inthediagram,theusermovedthecornerpointassociatedwiththekeyvalueof14.Thismeansthecornerpointsassociatedwith13and15needtoberecalculated.
Point15needstobemovedalongthecurrentvectordefinedbypoint12topoint15untilitlinesupwiththenewpoint14.Thevectorsfrom12to15,andfrom14to15,mustbeperpendiculartoeachother.Thesameoperationmustbeappliedtorecalculatethenewlocationforpoint13.
Nowtakeanotherlookatthecodetoseeifitmakessense.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>PuttingItAllTogether>RedefiningthePolylineBoundary>
Understandingthegp:pointEqual,gp:rtos2,andgp:zeroSmallNumFunctions
ThesethreefunctionsarerequiredtogetaroundoneofthequirksofprogramminginanAutoCADsystem,which,asyouarewellaware,allowsyouagreatdealofprecision.Occasionally,though,numbersarenotquitepreciseenough,duetotheroundingupordownoffloatingpointvaluesdefininggeometricpositions.Youmustbeabletocompareonesetofpointswithotherpoints,soyoumustdealwiththesecases.
Haveyouevernoticedthatoccasionally,whenyoulisttheinformationassociatedwithanAutoCADentity,youseeavaluesuchas1.0e-017?Thisnumberisalmostzero,butwhenyouarecomparingittozerowithinaLISPprogram,almostdoesnotcount.
Withinthegardenpath,youneedtobeabletocomparenumberswithouthavingtoworryaboutthefactthat1.0e-017isnotquitezero.Thegp:pointEqual,gp:rtos2,andgp:zeroSmallNumfunctionshandleanydiscrepanciesinroundingwhencomparingpointlists.
Thiscompletesyourtourofthefunctionsingppoly.lsp.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>PuttingItAllTogether>
WrappingUptheCode
Sofar,youhavedonethefollowinginthislesson:
Modifiedthegp:drawOutlinefunctionsothatitreturnsthepolylineperimeterpointsinadditiontothepointertothepolyline.Youaddedthisinformationtothegp_PathDatavariable.Thisvariableisstoredwiththereactordataintheobjectreactorattachedtoeverygardenpath.
Updatedthereactorfunctionsingpreact.lsp.
AddedfunctionsxyzList->ListOfPoints, xyList->ListOfPoints,andotherutilityfunctionstotheutils.lspfile.
Updatedthegp:Calculate-and-Draw-TilesfunctionsothatObjectCreationStyleisnowaparametertothefunctionratherthanalocalvariable.
Modifiedthecalltogp:Calculate-and-Draw-TilesintheC:GPathfunctionwithinthegpmain.lspfile.
Addedgppoly.lsptoyourproject,andexaminedthefunctionswithinit.
Givethecompletedapplicationatry.Saveyourwork,thenloadintheprojectsources,runtheGpathfunction,andtrystretchingandmovingthegardenpathboundary.Remember:ifsomethingisnotworkingandyouareunabletodebugtheproblem,youcanloadthecompletedcodefromtheTutorial\VisualLISP\Lesson7directory.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>PuttingItAllTogether>
BuildinganApplication
Thefinalactwithinthistutorialistotakeyourgardenpathcodeandturnitintoastand-aloneapplication.Thisway,itcanbedistributedasasingleexecutabletoanyuserorcustomer.Fortunately,thislastsetoftasksisprobablytheeasiestinthisentiretutorial,asVLISPdoespracticallyalltheworkforyou.
Note Itisrecommendedthatyouproceedwithbuildinganapplicationonlyifyourcodeisingoodworkingform.Makesurethatyouhavetestedyourapplicationusingtheoriginalsourcefiles,andthatyouaresatisfiedwiththeresults.
StartingtheMakeApplicationWizard
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>PuttingItAllTogether>BuildinganApplication>
StartingtheMakeApplicationWizard
Toassistyouincreatingstandaloneapplications,VLISPprovidestheMakeApplicationwizard.
ToruntheMakeApplicationwizard
1. Tostartthewizard,chooseFile MakeApplication NewApplicationWizardfromtheVLISPmenu.
2. SelectExpertmodeandchooseNext.ThewizardpromptsyoutospecifythedirectoryinwhichtostorefilescreatedbyMakeApplication,andtonameyourapplication.MakeApplicationproducestwooutputfiles:a.vlxfilecontainingyourprogramexecutable,anda.prvfilecontainingtheoptionsyouspecifytoMakeApplication.The.prvfileisalsoknownasamakefile.Youcanusethemakefiletorebuildyourapplication,whennecessary.
3. SpecifyyourTutorial\VisualLISP\MyPathdirectoryastheapplicationlocation,andcalltheapplicationgardenpath.VLISPusestheapplicationnameintheoutputfilenames(inthisinstance,gardenpath.vlxandgardenpath.prv.)ChooseNexttocontinue.
4. Theapplicationoptionsarenotcoveredinthistutorial.AcceptthedefaultsandchooseNext.(Forinformationonseparatenamespaceapplications,see“RunninganApplicationinItsOwnNamespace”intheAutoLISPDeveloper'sGuide.)
5. Inthisstep,thewizardispromptingyoutoidentifyalltheAutoLISPsourcecodefilesthatmakeuptheapplication.YoucouldindividuallyselecttheLISPsourcefiles,butthereisaneasierway.Changethepull-downfiletypeselectionboxsothat“VisualLISPProjectfile”isshown,
thenchoosetheAddbutton.SelecttheGpathprojectfileandchooseOpen.Note Dependingonhowyouworkedthroughthetutorial,youmayhaveseveralGpathprojectfilesshowingup.Selectthemostrecentfile.IfyoucopiedinthecompletedsourcecodefromLesson7,theprojectnametoselectshouldbeGpath7.prj.
Afterselectingtheprojectfile,chooseNexttocontinue.OneadvantageofcompiledVLXapplicationsisthatyoucancompileyourdialogcontrolfiles(.dcl)intothecompleteapplication.Thisreducesthenumberofindividualsourcefilesyourendusersneedtodealwith,andeliminatesanyofthesearchpathproblemswhenloadingaDCLfile.
6. Changethepull-downfiletypeselectionboxsothat“DCLfiles”isshown,thenchoosetheAddbutton.Selectthegpdialog.dclfile,thenchooseOpen.ChooseNexttocontinuebuildingtheapplication.
7. Compilationoptionsarenotcoveredinthistutorial.AcceptthedefaultsandchooseNext.(Forinformationoncompileoptions,see“OptimizingApplicationCode”intheAutoLISPDeveloper'sGuide.)
8. Thefinalstepisusedtoreviewtheselectionsyou'vemade.Atthispoint,youcanselectFinish.VLISPwillbeginthebuildprocess,displayingtheresultsintheBuildOutputwindow.Severalintermediatefileswillbeproduced,asyourindividualsourcecodefilesarecompiledintoaformatthatcanbelinkedintothesingleVLXapplication.
Whenitisallcomplete,you'llhaveanexecutablefilenamedgardenpath.vlx.Totestit,dothefollowing:
FromtheToolsmenuinAutoCAD,selectLoadApplication.
Loadthegardenpath.vlxapplicationthatwasjustcreatedandisfoundintheTutorial\VisualLISP\MyPathdirectory.
Runthegpathcommand.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>PuttingItAllTogether>
WrappingUptheTutorial
Youhavefinallymadeittotheendofthepath!Asyouhavediscovered,alotofmaterialwascoveredinthistutorial.BothAutoLISPconceptsandVLISPoperationshavebeenintroduced.The“gardenpathrevisited”wasdesignedtogiveyouasamplingofmanytopicsandconcepts.Youmightbeinterestedinmoreinformation.ThefollowingisabriefbibliographyofsomecommonLISPandAutoLISPbooks.
Pleasesendusyourcommentaboutthispage
AutoLISPTutorial>PuttingItAllTogether>
LISPandAutoLISPBooks
UnderstandingAutoLISP:ProgrammingforProductivity,WilliamKramer,AutodeskPress,ISBN0-8273-5832-6.
AutoLISPinPlainEnglish:APracticalGuideforNon-Programmers,GeorgeO.Head,VentanaPress,ISBN:1566041406.
LISP,3rdEdition,PatrickHenryWinstonandBertholdKlausPaulHorn,Addison-WesleyPublishingCompany,ISBN0-201-08319-1.
ANSICommonLisp,PaulGraham,PrenticeHall,ISBN0-13-370875-6.
CommonLISP,TheLanguage,SecondEdition,GuyL.Steele,Jr.,DigitalPress,ISBN1-55558-041-6.
Pleasesendusyourcommentaboutthispage