Nachdem nun in den bisherigen Kapiteln die notwendigen
Grundlagen geklärt wurden, können wir uns nun dem zuwenden,
worum es ja eigentlich geht: AutoCAD-Programmierung mit
AutoLisp. In diesem Kapitel werden wir uns mit der
Geometrie-Datenbank von AutoCAD befassen und dabei einige
neue Funktionen kennenlernen, die uns den Zugriff auf die
Datenbank bzw. auf Zeichnungselemente ermöglichen.
Probieren wir doch gleich mal die erste dieser Funktionen
aus! Sie heisst
(entlast), bekommt keine Argumente
und gibt uns den Entity-Namen des letzten Elementes in der
Zeichnung zurück:
(entlast) => <Objektname: 400acee8>
; oder, wenn die Zeichnung leer ist
(entlast) => nil
Nun, mit diesem Entity-Namen selbst können wir gar nichts
anfangen. Es ist nichts weiter als ein Identifikator für
ein Zeichnungselement, und das Gemisch aus Ziffern und
Buchstaben (nur a-f) ist nichts weiter als eine Speicheradresse
in hexadezimaler Schreibweise.
Allerdings gibt es Funktionen, die einen solchen Entity-Namen
(das Symbol, dass uns die Funktion
(type ...) für diesen
Datentyp zurückgibt, heisst übrigens ENAME) als Argument
erwarten. Wir leiten daher einfach mal die Rückgabe von
(entlast) an
(entget) weiter, aber zuvor
zeichnen wir schnell mit AutoCAD einen Kreis mit Mittelpunkt
bei 12.2,23.4 und Radius 44.3. Dann untersuchen wir, was
nun in Lisp passiert:
(entget(entlast)) =>
((-1 . <Objektname: 40505d58>) (0 . "CIRCLE")
(330 . <Objektname: 40505cf8>) (5 . "2B") (100 . "AcDbEntity")
(67 . 0) (410 . "Model") (8 . "0") (100 . "AcDbCircle")
(10 12.2 23.4 0.0) (40 . 44.3) (210 0.0 0.0 1.0))
Zunächst eines vorweg: Diese Liste kann bei Ihnen ganz
anders aussehen. Je nach AutoCAD-Version können einige Dinge
fehlen, und natürlich werden einige Elemente der Liste
auch andere Werte haben. Die hexadezimalen Zahlen des
Eintity-Namens werden sicher anders sein, aber damit müssen
Sie sich sowieso nie befassen. Eines ist aber sicher: Wenn
Ihre Liste nicht den Eintrag
(0 . "CIRCLE") hat, dann
haben Sie nicht als letztes einen Kreis gezeichnet!
Manche Dinge sind recht offensichtlich:
(0 . "CIRCLE")
kennzeichnet die Tatsache, dass es sich um einen Kreis handelt,
(10 12.2 23.4 0.0) müssen die Koordinaten des
Mittelpunkts sein, und
(40 . 44.3) stellt wohl den
Radius dar. Und insgesamt handelt es sich klar erkennbar um
eine Assoziationsliste, denn es gibt keine Einzelelemente,
sondern Unterlisten, die als erstes Element eine Zahl haben,
die wie ein Assoziationsschlüssel aussieht.
Versuchen wir doch einmal, unser Wissen aus dem Kapitel über
Assoziationslisten anzuwenden:
(cdr(assoc 0(entget(entlast))))
=> "CIRCLE"
(cdr(assoc 10(entget(entlast))))
=> (12.2 23.4 0.0)
(cdr(assoc 10(entget(entlast))))
=> 44.3
Wir sehen, dass die dotted pairs doch gar nicht so schlimm
sind - ganz im Gegenteil: Hier erweisen sie sich plötzlich
als sehr praktisch! Da, wo ein einzelner Wert vorliegt (bei
"CIRCLE" und dem Radius, gibt uns
(cdr(assoc ...))
ein Atom zurück, und da, wo es um eine Liste geht, nämlich
beim Mittelpunkt, bekommen wir mit dem gleichen Ausdruck
auch eine Liste. Wir sollten also spätestens jetzt unsere
Einstellung zu den gepunkteten Paaren doch ein wenig
revidieren und uns mit ihnen anfreunden.
Was sind das für Ziffern, die wir hier als Assoziationschlüssel
verwenden? Es handelt sich um die berühmt-berüchtigten
DXF-Gruppencodes von AutoCAD, die auch (daher der Name) in
DXF-Dateien (= Drawing eXchange Format) verwendet werden.
Eine komplette Auflistung aller Gruppencodes findet man in
der Online-Hilfe zu AutoLisp, aber Achtung: Zum Einen sind
es sehr viele, zum Anderen sind manche recht schwer zu verstehen.
Wir wissen bisher Folgendes: Der Gruppencode 0 regelt den Typ
des Geometrie-Elementes, 10 den Mittelpunkt und 40 den Radius.
Wird das bei einer Linie auch so sein? Wohl kaum: Eine Linie
hat keinen Mittelpunkt und keinen Radius. Vergleichen wir
doch einfach und ziehen dann unsere Schlüsse. Wir zeichnen
schnell eine Linie von (6.2 5.7 0.0) nach (12.7 9.9 0.0)
und rufen dann noch einmal
(entget(entlast ...)) auf.
Halt! Nicht gleich loslegen! Diesmal zeichnen wir die Linie
nicht mit AutoCAD, sondern aus Lisp heraus. Kopieren Sie den
(command ...)-Aufruf einfach in die Kommandozeile!
(command "_line" '(6.2 5.7 0.0) '(12.7 9.9 0.0) "")
(command ...) ist nichts weiter als eine Funktion,
die Befehle von Lisp an AutoCAD weiterleitet. Mit der
Schreibweise an einigen Stellen müssen wir uns aber kurz
befassen: Die Koordinaten für Anfangs- und Endpunkt haben
wir hier als Lisp-Listen formuliert -
(command ...)
sorgt automatisch dafür, dass sie bei AutoCAD richtig
ankommen. Natürlich müssen diese Punktlisten quotiert
werden, sonst gibt's eine Fehlermeldung.
(command)
quotiert übrigens keines seiner Argumente automatisch.
Der Leerstring am Ende des Aufrufs ist ein <Return>, nichts
weiter. Es wird gebraucht, da der Linie-Befehl anders als
der Kreis-Befehl ja einer dieser Endlos-Befehle in AutoCAD
ist, die erst mit einer Leereingabe beendet sind. Bleibt
noch das Kommando selber: "_line" ist die internationale
Version des Linie-Befehls. Einer Frage möchte ich gleich
vorbeugen: Wo ist der Punkt?
Wer schon ein bisschen Erfahrung mit
(command ...)
hat, weiss, dass man in fast allen Lisp-Programmen die
Schreibweise "_.line" findet. Der Punkt sorgt dafür, dass
der originale Linie-Befehl, so wie er in AutoCAD eingebaut
ist, zur Anwendung kommt. Ohne den Punkt wird der Linie-Befehl
so ausgeführt, wie er in diesem Augenblick definiert ist.
Im Augenblick? Ja, man kann sämtliche AutoCAD-Befehle mit
Hilfe von Lisp umfunktionieren!
Ich will hier nicht lange darauf eingehen - es gibt gute
Gründe für den Punkt, aber es gibt genauso gute Gründe
dagegen. Jedenfalls werde ich ihn diesem Tutorial nicht
verwenden. Evtl. werde ich diesem Thema irgendwann auch
noch ein Kapitel widmen.
Zurück zur Sache! Wir haben nun eine Linie aus Lisp heraus
erzeugt, und jetzt werfen wir doch mal einen etwas tieferen
Blick auf das, was dabei herausgekommen ist - den
(entget)-Blick natürlich:
(entget(entlast)) =>
((-1 . <Objektname: 40505d68>) (0 . "LINE")
(330 . <Objektname: 40505cf8>) (5 . "2D")
(100 . "AcDbEntity") (67 . 0) (410 . "Model")
(8 . "0") (100 . "AcDbLine") (10 6.2 5.7 0.0)
(11 12.7 9.9 0.0) (210 0.0 0.0 1.0))
Wir werden uns sicherlich nicht über
(0 . "LINE")
wundern, und dass Anfangs- und Endpunkt unter den Gruppencodes
10 und 11 verbucht wurden. Sicherlich handelt es sich um
eine 2D-Linie, aber der Kreis war auch zweidimensional, und
der hatte den Eintrag "2B" unter Gruppencode 5! Code 5 zeigt
uns also nicht an, ob irgendetwas 2D oder 3D ist - es handelt
sich um die Referenz!
Die Referenzen sind - hatten wir das nicht schon ein bisschen
weiter oben? - hexadezimale Kennungen, die jedem Zeichnungselement
zugeordnet sind. Das sind die Entity-Namen auch, aber mit einem
wesentlichen Unterschied: Die Objektreferenzen, neudeutsch auch
Handles genannt, sind konstant, d.h. sie bleiben für die
zugeordneten Zeichnungselemente immer gleich, auch über
Arbeitssitzungen hinweg. Die Entity-Namen sind aus den derzeitigen
Speicheradressen abgeleitet und ändern sich bei jeder
Arbeitssitzung. Daher darf man Entity-Namen nie abspeichern und
später wiederverwenden!
Der Entity-Name, den wir von
(entlast) an
(entget) übergeben
haben, ist übrigens unter Gruppencode -1 zu finden. Der zweite
Entity-Name, der unter Code 330 verbucht ist, spielt im Moment
noch keine Rolle für uns. Es macht auch gar nichts, wenn der
330er Code auf ihrem Rechner gar nicht erscheint.
Vielleicht haben Sie auch schon gemerkt, dass sich hinter dem
Gruppencode 8 der Layer des Geometrie-Elements verbirgt. Damit
haben wir die wichtigsten Daten für unsere Beispiele erst einmal
besprochen - alles andere lassen wir jetzt einfach stehen und
versuchen ein kleines Experiment: Mit
(entmake ...) kann
man Geometrie erzeugen - ein völlig anderer Weg wie über einen
(command)-Aufruf, aber mit dem gleichen Resultat. Wir
'basteln' uns ein paar Daten für eine Linie:
(setq hausmacher-linie
(list
(cons 0 "LINE")
(cons 8 "0")
(cons 10 '(0 0))
(cons 11 '(25 35))
)
)
Natürlich sind da einige Dinge weniger drin, als in den Listen,
die uns
(entget) zurückgibt, aber es ist auf jeden Fall
das enthalten, was die Linie wirklich ausmacht - und dazu sogar
noch eine Layerangabe. Versuchen wir's einfach:
(entmake hausmacher-linie) =>
((0 . "LINE") (8 . "0") (10 0 0) (11 25 35))
Wir sehen: Die Linie erscheint augenblicklich auf dem Bildschirm,
und
(entmake) gibt uns die eingefütterten Daten unverändert
zurück.
Fassen wir zusammen, was wir in diesem Kapitel gelernt haben:
-
(entlast) gibt uns den Eintity-Namen des zuletzt erzeugten
Geometrie-Elements zurück
-
Mit (entget ...) erhalten wir die Liste mit den Geometriedaten
zu einem eingegebenen Entity-Namen
-
Mit (command ...) und (entmake ...) stehen uns zwei vom Ansatz
her völlig unterschiedliche Wege zur Erzeugung von Geometrie
zur Verfügung
-
Die Entity-Datenlisten sind Assoziationslisten, in denen
DXF-Gruppencodes als Schlüssel verwendet werden
-
Die 'Zauberformel' für den Zugriff auf Geometriedaten im
Einzelnen lautet (cdr(assoc <gruppencode>(entget ...)))
Übungsaufgaben
-
Erläutern Sie noch einmal den Unterschied zwischen
Entity-Namen (Gruppencode -1)und den Objektreferenzen
(Gruppencode 5)
-
Welches sind die Gruppencodes für Layer und Kreisradius?
-
Was wird unter dem Gruppencode 0 verbucht?
-
Welche der folgenden Aussagen sind richtig?
-
(entget ...) gibt einen Entity-Namen zurück
-
(entlast ...) gibt die Objektreferenz des zuletzt
erzeugten Geometrie-Elementes zurück
-
Mit (command ...) kann man AutoCAD-Befehle aus Lisp
heraus ausführen
-
Einen (command ...)-Aufruf muss man immer mit einem
Leerstring beenden
-
(entmake ...) gibt die eingegebene Datenliste zurück,
ohne sie zu verändern, wenn der Aufruf erfolgreich war
-
Keines der Argumente für (command) wird automatisch
quotiert
-
(entmake) und (entget) sind Funktionen mit ausgeprägtem
Seiteneffekt
-
Was gibt (entlast) zurück, wenn das zuletzt erzeugte
Geometrieelement inzwischen wieder gelöscht wurde?
-
In den(entget)-Beispielen auf dieser Seite kam ein
Gruppencode 330 vor, der einen Entity-Namen enthielt.
Wie kann man mit (entget) darauf zugreifen, und was
für ein Entity-Typ ist das?
-
Erzeugen Sie einen Kreis (MP: 64.3,-25.8 R:28.24) mit
(command) und anschliessend mit (entmake). Prüfen Sie
nach, ob beide Kreise deckungsgleich aufeinander liegen.
-
Haben Sie in unseren Beispielen eigentlich die Gruppencodes
entdeckt, die Farbe und Linientyp des Kreises bzw. der
Linie enthalten?
-
Was passiert, wenn Sie in unserem (entmake)-Test einen
Layer (Gruppencode 8) angeben, der gar nicht existiert?
-
Was passiert, wenn Sie in unserem (entmake)-Test gar
keinen Layer angeben, d.h. Gruppencode 8 wird weggelassen?
Lösungen