AutoLISP Tutorial > Introduction · AutoLISP Tutorial > Designing and Beginning the Program In this...

206
utoLISP Tutorial > ntroduction This tutorial is designed to demonstrate several powerful capabilities of the AutoLISP ® programming environment for AutoCAD ® and introduce features of the AutoLISP language that may be new to you. The purpose of the tutorial is to draw a garden path using an automated drawing tool that minimizes drafting time and shows the power of parametric programming. You will learn to create a drawing routine that automates the generation of a complex shape—the kind of drafting operation you do not want to have to do manually over and over again. Working in Visual LISP Tutorial Overview Please send us your comment about this page

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

StoringtheReturnValueofgp:getPointInputinaVariable

Pleasesendusyourcommentaboutthispage

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.

Pleasesendusyourcommentaboutthispage

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

Pleasesendusyourcommentaboutthispage

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

Pleasesendusyourcommentaboutthispage

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.

Pleasesendusyourcommentaboutthispage

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.

Pleasesendusyourcommentaboutthispage

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.

Pleasesendusyourcommentaboutthispage

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.

Pleasesendusyourcommentaboutthispage

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,

thenwritethecallbackfunctionthatistriggeredwhentheeventoccurs.

Pleasesendusyourcommentaboutthispage

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

becomesconfusing,refertoyour“reactor-tracesheets.”

Pleasesendusyourcommentaboutthispage

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

C:GPathfunctiondefinition.

Pleasesendusyourcommentaboutthispage

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.

Pleasesendusyourcommentaboutthispage

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

(gp:RedefinePolyBorderCurrentPointsreactorData)

);_endofsetq

Pleasesendusyourcommentaboutthispage

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