Jak automatycznie wczytać lispy do AutoCAD-a?
Do automatycznego wczytywania aplikacji służą dwie funkcje: autoload oraz load. Autoload powinno się stosować do wywoływania funkcji poprzedzonych przedrostkiem c:, load do wszystkich pozostałych. Argumentami formalnymi funkcji autoload są nazwa wczytywanego pliku, ewentualnie także ścieżka dostępu, oraz lista zawierająca nazwy nowych poleceń zdefiniowanych w pliku. Nazwa pliku nie wymaga rozszerzenia. Jeżeli w pliku "aplikacja.lsp" zostały zdefiniowane funkcje (defun c:xxx () ...) oraz (defun c:yyy () ...), wyrażenie, które należy umieścić w pliku acad200*doc.lsp przyjmie postać (autoload "aplikacja.lsp" '("xxx" "yyy")). Wczytanie i uruchomienie programu "aplikacja" nastąpi dopiero po wywołaniu po raz pierwszy jednego z poleceń xxx lub yyy. Funkcje load można stosować w pliku acad200*doc.lsp lub w linii poleceń menu. W celu zaoszczędzenia pamięci operacyjnej należałoby stosować warunek sprawdzający, czy funkcja, którą chcemy wywołać, nie została już wczytana do rysunku, np.
(cond
((not nazwa_funkcji)
(if (load "aplikacja.lsp")
(nazwa_funkcji)
(prompt "\nNie mogę znaleˇć pliku aplikacja.lsp")
)
);end if
(T (nazwa_funkcji))
);end cond
Innym rozwiązaniem jest wykorzystanie opcji Load application z menu górnego Tools. W oknie dialogowym tej opcji znajduje się pole Startup Suite z przyciskiem Contents, w którym możemy definiować listę programów (nie tylko lispowych) automatycznie wprowadzanych podczas otwierania rysunków. Lista plików przyporządkowana jest tylko bieżącemu profilowi. Informacje o plikach, a ściślej o ścieżkach dostępu do plików, AutoCAD gromadzi w systemowym rejestrze w kluczu: "HKEY_CURRENT_USER\(ścieżka dostępu do systemowego rejestru AutoCADa)\Profiles\nazwa profilu\Appload\Startup". Ścieżkę dostępu do rejestru AutoCAD-a uzyskamy, wywołując wyrażenie (vlax-product-key).
Czy mogę spowodować, aby napisane przeze mnie programy były uruchamiane automatycznie tylko podczas wczytywania konkretnych rysunków?
Oczywiście! Załóżmy, że chcemy, aby program (o nazwie "mój_program") uruchamiał się po wczytaniu rysunku ("mój_rysunek"). Wystarczy, że do pliku acad200*doc.lsp dopiszemy odpowiednie wyrażenie. Pamiętajmy, by nazwę pliku zapisać tylko dużymi literami, a jako separatorów katalogów użyć znaków "\\". Jeżeli w pliku została
zdefiniowana "moja_funkcja", którą chcemy uruchomić, możemy ją wywołać wyrażeniem (moja_funkcja).
(cond
((= (strcase (strcat (getvar "dwgprefix") (getvar "dwgname")))
"D:\\MÓJ_RYSUNEK.DWG")
(if (load "mój_program.lsp")
(moja_funkcja)
(alert "\nNie znalazłem programu Mój_program.lsp ")
)
)
);end cond
Uwaga! Funkcje z przedrostkiem c:, służące do definiowania nowych poleceń AutoCAD-a, można wywołać tak jak normalną funkcję AutoLISP-u, np. (c:moje_polecenie). Dzięki temu obejdziemy ograniczenie polegające na niemożności wywołania nowych poleceń w plikach AutoLISP-u za pomocą wyrażenia command.
Warunek uruchamiania pliku da się rozszerzyć o dodatkowe elementy, np. sprawdzenie, czy blok potrzebny aplikacji jest w rysunku. Jeżeli istnieje, funkcja zostaje uruchomiona. Kontrola polega na przeszukaniu tablicy bloków rysunku za pomocą wyrażenia (tblsearch "block" "nazwa_bloku").
Chciałbym określić współrzędne początku Lokalnego Układu Współrzędnych oraz kąt obrotu jego osi X względem Globalnego Układu Współrzędnych.
Wszystkie potrzebne dane możemy uzyskać, analizując zmienne systemowe AutoCAD-a. Współrzędne początku LUW zapisane są w zmiennej "ucsorg". Wyrażenie (setq Pkt (getvar "ucsorg")) zapisuje do zmiennej
Pkt współrzędne początku LUW. Ponieważ kąt obrotu osi X zapisany jest w postaci wektora, musimy najpierw jego wartość zamienić na wartość kątową wyrażoną w radianach (setq RadAng (angle '(0 0 0) (getvar "ucsxdir"))). Aby przekształcić radiany na stopnie dziesiętne, możemy posłużyć się wyrażeniem (/ (* 180 RadAng) pi).
Dlaczego jeżeli używam słowa kluczowego "ostatni" nie zawsze zostaje zaznaczony ostatnio narysowany element? Czy jest to błąd AutoCAD-a?
Nie jest to błąd AutoCAD-a. Wybierany jest zawsze ostatni element, ale tylko z widocznych na ekranie! Gdy ekran jest pusty, słowo kluczowe "ostatni" nie wybierze żadnego elementu, nawet jeżeli w rysunku zostały narysowane jakieś elementy. Ponieważ funkcje AutoLISPu można wywoływać w linii poleceń, jeżeli chcemy mieć pewność, że wybrany element jest ostatnim z narysowanych, użyjmy funkcji entlast, np. (command "_.explode" (entlast)), (command "_move" (entlast) "" "0,0" "@10,5"). W poleceniu "_.explode" nie musimy stosować znaku "" jako potwierdzenia użycia funkcji Enter.
Muszę przesłać koledze kilkadziesiąt własnych rysunków. Szkopuł w tym, że ja używam AutoCAD-a 2000, a on korzysta jeszcze z wersji czternastej. Wiem, że bezpośrednio nie odczyta moich rysunków i że muszę je wszystkie zapisać w formacie R14. Czy mogę tę czynność jakoś zautomatyzować?
AutoCAD zapewnia definiowanie skryptów poleceń zapisanych w plikach tekstowych. Ponieważ AutoLISP dysponuje funkcjami operującymi na takich plikach, może napisać funkcję, która zapisywałaby na dysku plik skryptu. Składnia poleceń w skrypcie zapewniałaby otwarcie pliku, zapisanie go w wersji czternastej programu, zamknięcie pliku itd. Funkcja acet-ui-pickdir służy do lokalizacji katalogu. Dodatkowo można wprowadzić kryterium dla nazw plików. Kod programu znajduje się w Internecie na stronie www.cadcamforum.pl/lisp.
Jak uzyskać współrzędne wskazanego segmentu polilinii?
Posłużymy się funkcjami na krzywych. Jak pamiętamy, każdy punkt krzywej może być określony parametrem. W przypadku polilinii parametr jest ściśle związany z indeksowaniem wierzchołków. Wykorzystując tę właściwość, można zdefiniować parametr polilinii w punkcie wskazania. Współrzędne punktu należy wcześniej zamienić na współrzędne GUW, poza tym punkt musi leżeć na krzywej. Jeśli dysponujemy określonym parametrem, zdefiniujemy współrzędne najbliższych wierzchołków, zaokrąglając uzyskaną wartość w górę i w dół.
(defun vla-getPolySegment (Obj Point / Param)
(if (= (type Obj) 'ENAME)
(setq Obj (vlax-ename->vla-object Obj))
)
(setq Point (trans (osnap Point "_nea") 1 0)
Param (vlax-curve-getParamAtPoint VLAPoly PickPt)
)
(list (vlax-curve-getPointAtParam Obj (fix Param))
(vlax-curve-getPointAtParam Obj (1+ (fix Param))))
);end vla-getPolySegment
Jak uzyskać nazwę aktualnego profilu oraz listę wszystkich profili w systemie?
Nazwa aktualnego profilu zapisana jest w zmiennej "cprofile". W uzyskaniu nazw wszystkich profili pomogą nam mechanizmy ActiveX. Funkcja vla-ProfileList inicjuje środowisko ActiveX, uzyskuje wskaˇnik aplikacji AutoCAD-a, preferencji oraz obiektu profili. Obiekt ten zawiera metodę uzyskania nazwy wszystkich profili. Na koniec wskaˇniki obiektów zostają zwolnione, a zmienna typu tablicowego zamieniona na listę.
(defun vla-ProfileList (/ Acad Prefs Profiles Names)
(vl-load-com)
(setq Acad (vlax-get-acad-object)
Prefs (vlax-get-property Acad "preferences")
Profiles (vlax-get-property Prefs "profiles")
)
(vla-getallprofilenames Profiles 'Names)
(mapcar 'vlax-release-object (list Acad Prefs Profiles))
(vlax-safearray->list Names)
);end vla-ProfileList
Nazwę aktualnego profilu możemy także uzyskać, korzystając z właściwości (vlax-get-property Profiles "ActiveProfile").
Muszę dołączyć do rysunku kilkanaście odnośników. Każdy z nich będzie wstawiany na początku układu współrzędnych, w jednorodnej skali równej. Czy można ten proces zautomatyzować?
Aby rozwiązać to zagadnienie, możemy utworzyć nowy katalog, w którym będą znajdować się wszystkie pliki odnośników. Visual LISP dysponuje gotowymi wyrażeniami do zarządzania katalogami i plikami. W określeniu katalogu pomoże nam funkcja acet-ui-pickdir. Aby z niej skorzystać, należy wprowadzić plik acetutil.arx znajdujący się w pakiecie Express. Program uwzględnia rodzaj wstawianego odnośnika: dołączony lub nakładkowy. Kontrola błędów polega na sprawdzeniu, czy nazwa wczytywanego odnośnika już istnieje oraz czy wczytywany odnośnik nie jest aktywnym rysunkiem. Program umieściliśmy w Internecie na stronie www.cadcamforum.pl/lisp.
Jak w AutoLISPie określić wersję rysunku AutoCAD-a?
Mimo że sam plik rysunku jest zapisywany w formacie binarnym, analizując go w normalnym edytorze tekstu, zauważymy, że pierwsze pola w pliku zawierają informacje o wersji rysunku. Możemy ją odczytać za pomocą standardowych funkcji wejścia-wyjścia. Lisp znajduje się na "internetowych łamach" CADCAM FORUM.
Jak usunąć zbędne spacje z łańcucha?
Jeżeli chcemy usunąć wszystkie spacje z łańcucha, możemy posłużyć się wyrażeniem (vl-list->string (vl-remove 32 (vl-string>list łańcuch))), gdzie łańcuch jest zmienną typu STRING. Do usuwania zbędnych znaków spacji z początku i końca łańcucha służą funkcje:
(vl-string-left-trim charakter-set sting) usuwa określone znaki z początku łańcucha;
(vl-string-right-trim charakter-set sting) usuwa określone znaki z końca łańcucha;
(vl-string-trim charakter-set sting) usuwa określone znaki z początku i końca łańcucha; charakter-set oznacza łańcuch zawierający znaki do usunięcia, string łańcuch, z którego będą usuwane znaki, np. (vl
string-trim "tu wpisujemy dowolny łańcuch znaków", np. "Podążaj za białym króliczkiem.") -> "Podążaj za białym króliczkiem."
Jak zamieniać kropkę na przecinek (i odwrotnie) w łańcuchu?
Niektóre aplikacje zapisują liczby dziesiętne za pomocą kropki, inne zaś przecinka. Aby uniknąć przykrych niespodzianek, związanych z niejednolitym zapisem, można podczas odczytywania danych liczbowych korzystać z dodatkowego filtra, który zamieniałby znak separatora w liczbie, np. (vl-string-translate "." "," "23,45") -> "23.45"
Chciałbym do filtrowania danych z tablic używać znaków zastępczych. Jak tego dokonać?
W rozwiązaniu tego problemu pomoże nam wyrażenie wcmatch, które wykorzystamy w naszej nowo zdefiniowanej funkcji SeekTable. Funkcja podaje nazwy elementów z tablic rysunkowych, zgodnych z zadanym
filtrem. Wymaga trzech argumentów: pierwszy oznacza nazwę tablicy i musi być łańcuchem, drugi określa filtr, trzeci zaznacza, czy wielkość liter ma być uwzględniana.
(defun SeekTable (Table Filter Val / Result Tmp)
(cond
((member (strcase Table)
'("APPID" BLOCK" "DIMSTYLE" "LAYER" "LTYPE"
"STYLE" "UCS" "VIEW" "VPORT"))
(while (setq Tmp (tblnext Table (null Tmp)))
(if (wcmatch (cdr (assoc 2 Tmp)) (if Val (strcase Filter) Filter))
(setq Result (cons (cdr (assoc 2 Tmp)) Result))
);end if
);end while
)
(T (prompt "\nBłędna nazwa tablicy."))
);end cond
(reverse Result)
); SeekTable
(SeekTable "layer" "piętro_##" nil) szuka w bazie rysunku wszystkich warstw o nazwie rozpoczynającej się od klucza "piętro_", po którym następują dwa dowolne znaki numeryczne. Kryterium spełniają nazwy "piętro_01" "Piętro_13" itp.
Mam zdefiniowany blok z kilkoma atrybutami i chciałbym wykorzystać go w swoim programie. Liczba atrybutów przypisanych do tego bloku może być różna. Stosując klasyczne wyrażenie command, nie mogę sobie poradzić z tym zagadnieniem.
Rzeczywiście, używając wyrażenia command, musimy zawsze dokładnie określić liczbę argumentów (opcji) dla wywołanego polecenia (w naszym przykładzie wstawiania bloku). Załóżmy, że blok ma przypisane trzy argumenty. Niech Name oznacza nazwę bloku, Pkt współrzędne wstawienia bloku, Ang kąt obrotu bloku, Sx
współczynnik dla osi X, Sy współczynnik dla osi Y. Wartości tych argumentów zostały zapisane w zmiennych Arg1, Arg2 i Arg3. Wyrażenie powinno wtedy przyjąć postać (command "_.insert" Name Pkt Ang Sx Sy Arg1 Arg2 Arg3). Niestety, jeśli choć jedna wartość któregoś atrybutu wynosi nil, wykorzystywanie wyrażenia command zakończy się niepowodzeniem. Można temu zaradzić może, używając następującej procedury:
(command "_.insert" Name Pkt Ang Sx Sy
(while (/= (strcase (getvar "cmdnames")) "INSERT")
(if (car AtrList)
(progn
(command (car AtrList))
(setq AtrList (cdr AtrList))
);end progn
(command "")
);end if
);end while
);end command insert
Zmienna AtrList to lista wartości atrybutów, czyli (list Atr1 Atr2 Atr3). Innym rozwiązaniem jest wykorzystanie mechanizmów ActiveX:
(vl-load-com) zainicjowanie środowiska ActiveX;
(setq AcadObject (vlax-get-acad-object)) uzyskanie wskaˇnika dostępu do obiektu AutoCAD-a;
(setq AcadDocument (vla-get-ActiveDocument AcadObject)) uzyskanie wskaˇnika dostępu do bieżącego rysunku;
(setq mSpace (vla-get-ModelSpace AcadDocument)) uzyskanie wskaˇnika dostępu do przestrzeni modelu (zakładamy, że w tej przestrzeni będzie wstawiany blok);
(setq Block (vla-insert mspace (vlax-3d-point '(0 0 0)) "AAA" 1.0 1.0 1.0 0.0)) wstawienie bloku o przykładowej nazwie "AAA" w punkcie (0 0 0), kącie obrotu 0 oraz skali równej 1. Do zmiennej Block został przypisany wskaˇnik dostępu do bloku;
(setq AtrVar (vla-getAttributes Block)) uzyskanie tablicy wariantowej wartości atrybutów w bloku;
(setq AtrList (vlax-safearray->list (vlax-variant-value AtrVar)) zamiana tablicy wariantowej na klasyczną listę.
Listę atrybutów możemy poddać edycji: (foreach Item AtrList (Edycja_Atrybutu Item)).
Łuki w poliliniach definiowane są za pomocą tzw. współczynnika wypukłości. Jaka jest zależność tego współczynnika od promienia łuku?
Współczynnik wypukłości łuku oznaczany jest w języku angielskim jako bulge. Zakładając, że P1 oznacza współrzędne początku łuku, P2 współrzędne końca łuku, a P0 współrzędne środka łuku (punktu wodzącego), otrzymujemy następujące zależności:
promień R = odległości od P0 do P1 (lub P0 do P2),
L = 0.5 x odległość od P1 do P2,
Bulge = ( R pierwiastek (R*R L*L) ) / L,
R=Lx(BulgexBulge+1)/2xBulge.
Oto prosta procedura tworzenia polilini składającej się z łuku:
(defun CreateArcPoly (Start End Radius Arg / Bulge MidChord Oper)
(if Arg (setq Oper +) (setq Oper ))
(cond
((< (setq MidChord (* 0.5 (distance Start End))) Radius)
(setq Bulge (/ (Oper Radius
(sqrt ( (* Radius Radius) (* MidChord MidChord))))
Midchord)
)
(entmake
(list
'(0 . "LWPOLYLINE") '(100 . "AcDbEntity") '(100 . "AcDbPolyline")
(cons 90 2) (cons 10 Start) (cons 42 Bulge) (cons 10 End)
)
)
T
)
(T nil)
);end cond
);end CreateArcPoly
Argumentami funkcji są: Start współrzędne początku łuku, End współrzędne końca łuku, Radius wartość
promienia. Argument Arg określa kierunek rysowania (zgodny z ruchem wskazówek zegara lub przeciwny).
Podczas otwierania niektórych rysunków AutoCAD-a pojawia się okienko z informacjami zastępczymi. Czy można spowodować, aby się nie pojawiało?
Tak. Wystarczy ustawić zmienną systemową AutoCAD-a proxynotice na 0.
Czy mogę zautomatyzować czynność kreskowania stożka skarpy?
W CADCAM FORUM nr 8/2001 przedstawiliśmy program do kreskowania skarpy określonej przez dwie linie. Dzisiaj chcemy przedstawić podobną aplikację do kreskowania stożka skarpy, którego spód wyznacza narysowany łuk. Program ten umieściliśmy w Internecie na stronie www.cadcamforum.pl/lisp.
Otrzymuję rysunki MicroStation zapisane w formacie DWG. Niestety, często zamiast polskich liter znajdują się tam inne znaki, które muszę ręcznie poprawiać.
Jeżeli otrzymaliśmy rysunek bez polskich liter, możemy posłużyć się programem, który naprawia tego typu błędy. Kod programu można znaleˇć w Internecie.
Powodem tego typu problemów jest zastosowanie do eksportu rysunku niewłaściwej strony kodowej. Warto zwrócić uwagę na filtr użyty podczas wybierania zbioru wskazań. Uwzględniane są tylko obiekty będące tekstem i te, które mają podwójny znak procentu, po którym występują trzy dowolne liczby. Na czas działania programu wszystkie warstwy zostają odblokowane.
Przy okazji warto zwrócić uwagę na jeszcze jeden błąd MicroStation (naprawiony dopiero w wersji J), jaki pojawia się podczas eksportu rysunku do formatu AutoCAD-a. Otóż w wyeksportowanym rysunku DWG
zostaje utworzony styl tekstu o nazwie "text", zapisany w tablicy symboli małymi literami. Uniemożliwia to edycję wszystkich obiektów (tekstów), do definicji których zastosowano ten styl. Dopiero zmiana nazwy stylu, np. w edytorze stylu tekstu, naprawia powyższy błąd.
Szukam funkcji, która podawałaby wartość T (prawdę), jeśli elementy dwóch wybranych list są takie same, ale niekoniecznie występują w tej samej kolejności.
Oto gotowy program:
(defun CompareLists (FirstList SecondList / Return)
(cond
((= (length FirstList) (length SecondList))
(setq Return T)
(foreach Item FirstList (if (not (member Item SecondList)) (setq Return nil)))
(if Return
(foreach Item SecondList (if (not (member Item FirstList)) (setq Return nil)))
);end if
)
);end cond
Return
);end CompareLists
<WYLICZ>Przykłady:
(CompareLists '(A B C) '(A B C)) -> T
(CompareLists '(A B C) '(B C A)) -> T
(CompareLists '(A B C) '(A B D)) -> nil
Czy mogę skopiować do aktualnie tworzonego rysunku zbiór rodzajów linii zdefiniowanych w innym rysunku?
Oczywiście. To zagadnienie opisaliśmy dokładnie w artykule poświęconym mechanizmom ObjectDBX (CADCAM FORUM nr 6/2001). W Internecie przedstawiamy gotowy program, należy tylko pamiętać o
zarejestrowaniu w systemie biblioteki axdb15.dll.
Chciałbym zapisać w pliku tekstowym informacje o ścieżkach dostępu do odnośników w bieżącym rysunku.
Aby rozwiązać ten problem należy przeszukać tablicę bloków w rysunku, w której umieszczone są informacje o odnośnikach. Kod DXF o indeksie 70 informuje nas, czy dany obiekt z tablicy jest odnośnikiem. Jeżeli tak, w kodzie 1 zapisana zostaje informacja o ścieżce dostępu. Program umieściliśmy na stronie www.cadcamforum.pl/lisp.