AutoLISP

Polilinie 2D – konwersje

W  przeszłości wielokrotnie już pisałem o sposobach konwersji różnych obiektów. Zamiana elipsy na polilinię, oraz inne przekształcenia obiektów zostały już wspomniane tutaj. O sposobie konwertowania płaskiej (jednopłaszczyznowej) polilini 3D na polilinię pisałem w tym wpisie. Ogólnie zaś o różnicach pomiędzy polilinią 2D a „lekką” polilinią, a także o poleceniu CONVERTPOLY, mówiłem  tutaj.

Dziś słów parę o przekształceniach polilinii lekkiej w polilinię 2D i odwrotnie, a także o lisp-ej zmianie dopasowania (wygładzania) polilini. Do szybkiej zmiany obiektu typu LWPOLYLINE na POLYLINE (i odwrotnie w zależności o typu obiektu podanego jako argument), napisałem poniżej prezentowaną funkcję jk:ENTConvertPoly. Pomimo tego, że możliwe jest napisanie lisp-a przekształcającego te obiekty bez wykorzystania command, funkcja wykorzystuje polecenie CONVERTPOLY. Ma to dwie zalety: jest stosunkowo szybkie oraz konwertowany obiekt zachowuje swój uchwyt (handle). Warto zwrócić uwagę na fakt, że polecenie CONVERTPOLY przekształca tylko polilinie nie mające dopasowania typu splajn. Stąd jeżeli takie dopasowanie występuje, obiekt ma najpierw usuwaną tę cechę i dopiero wtedy użyte jest polecenie konwersji. Funkcja wygląda tak:


;;; ----------------------------------------------------------- ;
; konwertuje LWPOLYLINE na POLYLINE 2D i odwrotnie
; 
(defun jk:ENT_ConvertPoly (En / d p -conv)
  (defun -conv (o v / h c l)
    (setq h (getvar (setq c "CMDECHO"))
          l (list "_HEAVY" "_LIGHT")
    )
    (setvar c 0)
    (command "_.CONVERTPOLY" (nth v l) o "")
    (setvar c h)
    t
  )
  (setq d (entget En)
        p (cadr (cd:DXF_massoc 100 d))
  )
  (cond
    ( (= p "AcDbPolyline")
      (-conv En 0)
    )
    ( (= p "AcDb2dPolyline")
      (setq m (getpropertyvalue En "Poly2dType"))
      (if
        (not (zerop m))
        (setpropertyvalue En "Poly2dType" 0)
      )
      (-conv En 1)
    )
    (t nil)
  )
)

Poniższy film ilustruje działanie funkcji wywołanej w linii poleceń:

Działanie funkcji nie zmienia grafiki dla obiektów składających się z odcinków i łuków. Ponieważ polilinie mające dopasowanie splajn lub krzywej, są reprezentowane zawsze jako polilinia 2D starego typu, przy przekształceniu na lekką polilinię, dane dopasowania są tracone.

Druga funkcja służy do zmiany dopasowania / wygładzania polilinii. Wymaga podania 2 argumentów: obiektu (ename polilinii) i liczby (od 0 do 3). W zależności od wartości argumentu, możliwe są przekształcenia polilinii dopasowanej do krzywej, do splajnu (dopasowanie kwadratowe i sześcienne) i usuwanie dopasowania. Poniżej kod omawianej funkcji:


;;; ----------------------------------------------------------- ;
; zmienia dopasowanie/wygladzanie obiektu POLYLINE 2D. Jezeli   ;
; obiekt to LWPOLYLINE, przeksztalca na POLYLINE.
; Wartosci argumentu Val:
; 0 - brak
; 1 - krzywa
; 2 - kwadratowe
; 3 - szescienne
;
(defun jk:ENT_SetPolyType (En Val / d p)
  (setq d (entget En)
        p (cadr (cd:DXF_massoc 100 d))
  )
  (cond
    ( (= p "AcDbPolyline")
      (jk:ENT_ConvertPoly En)
      (jk:ENT_SetPolyType En Val)
    )
    ( (= p "AcDb2dPolyline")
      (if
        (/= (getpropertyvalue En "Poly2dType") Val)
        (setpropertyvalue En "Poly2dType" Val)
      )
    )
    (t nil)
  )
)

Na poniższej animacji widać działanie funkcji:

Na koniec omówienia zmiany dopasowania, łyżka dziegciu… Zwykle zmiany dopasowania polilinii działają poprawnie. Zdarzają się jednak przypadki, przy intensywnej wielokrotnej zmianie tego samego elementu, że grafika obiektu zostanie zmieniona, niezgodnie z oczekiwaniami. Ilustruje to obraz poniżej. Prostokąt, dla którego kolejno aplikowane są różne stany dopasowania, na końcu, zamiast powrócić do stanu pierwotnego, zostaje niestety zmieniony – boki prostokąta są zamieniane na łuki.

Taki efekt zdarza się zarówno przy użyciu powyższej funkcji LISP-a, jak również przy ręcznej manipulacji właściwościami obiektu, w palecie Właściwości:

( . . . )

Reklamy
AutoLISP, CADPL-Pack

Niestandardowe skalowanie bloku

Polecenie WSTAW (_INSERT) wstawia bloki z określonym współczynnikiem skali. Zmiana cech wstawionego bloku pozwala na określenie jego skali. Polecenie SKALA (_SCALE) umożliwia skalowanie wstawionego bloku. Do tego miejsca wszystko jest w porządku, jeśli chcemy aby bloki były skalowane, również z atrybutami (jeśli je posiadają). Zwyle tak jest ale czasem jednak zachodzi potrzeba przeskalowania wszystkich graficznych elementów bloku oprócz jego atrybutów (schematy, mapy, itp.).
Dla takich szczególnych przypadków powstał właśnie krótki program. Wykorzystuje pewną cechę bloków z atrybutami – inaczej jak wszystkie inne elementy graficzne wchodzące w skład definicji bloku, atrybuty są osobnymi obiektami. Mogą mieć dla każdego wstawienia inne (niż zadeklarowane w definicji bloku) położenie, kolor, styl tekstu, itp. Wykorzystując tę cechę program zmienia tylko skalę obiektu INSERT (kody DXF 41, 42 i 43), nie zmieniając jednocześnie skali obiektów ATTRIB.

Działanie polecenia BSC na prostym przykładzie ilustruje animacja poniżej:

Program napisałem bardzo dawno temu – pierwsza wersja datowana jest na rok 1999, i co jakiś czas podlegał pewnym modyfikacjom. Poniżej w całości kod programu:


;;; ============================================================ ;
;;; 1999 by kojacek (mod: 2003, 2009, 2017)                      ;
;;; skaluje tylko elementy graficzne bloku (atrybuty bez zmian)  ;
;;; ============================================================ ;
(defun C:BSC ( / s d oF nF)
  (if
    (setq s (entsel "\nWskaz blok: "))
    (progn
      (setq d (entget (car s)))
      (if
        (= (cdr (assoc 0 d)) "INSERT")
        (if
          (vlax-write-enabled-p (car s))
          (progn
            (redraw (car s) 3)
            (setq oF (cdr (assoc 41 d)))
            (initget (+ 2 4))
            (if
              (setq nF
                (getreal
                  (strcat
                    "\nPodaj nowy współczynnik skali (aktualny = "
                    (cd:CON_Real2Str oF 2 nil)
                    "): "
                  )
                )
              )
              (progn
                (cd:SYS_UndoBegin)
                (foreach % '(41 42 43)
                  (setq d (subst (cons % nF)(assoc % d) d))
                )
                (entmod d)
                (cd:SYS_UndoEnd)
              )
              (princ "\nNie podano współczynnika skali. ")
            )
            (redraw (car s) 4)
          )
          (princ "\nObiekt na zablokowanej warstwie. ")
        )
        (princ "\nTo nie jest blok. ")
      )
    )
    (princ "\nNic nie wskazano. ")
  )
  (princ)
)

Ostatnie zmiany dotyczyły adaptacji makra tak aby wykorzystać funkcje biblioteczne CADPL-Pack-a. Są to funkcje formatujące liczbę rzeczywistą jako łańcuch tekstowy zgodnie z bieżącymi nastawami rysunku (funkcja cd:CON_Real2Str), oraz znaczniki początku i końca dla UNDO (cd:SYS_UndoBegin i cd:SYS_UndoEnd).

( . . . )

AutoLISP, CADPL-Pack

Bloki dynamiczne: wybieranie na podstawie parametru

Lipiec w tym roku to miesiąc urlopowy. W tym czasie odbyły się również TWG, oraz WGP, stąd też nie powinno dziwić nieco zwolnione tempo ukazywania się tutejszych wpisów… Skoro jednak wszystko się kończy, a dziś ostatni dzień miesiąca, trzeba zatem nadrobić zaległości.

Bloki dynamiczne już bardzo mocno zadomowiły się w AutoCAD-zie, ich szerokie możliwości, dają użytkownikom szerokie pole zastosowań. Od tworzenia małych i prostych do całkiem skomplikowanych modeli parametrycznych.  Dla zaawansowanych użytkowników, a zwłaszcza lispowych wyjadaczy, dużym wyzwaniem są próby manipulacji blokami dynamicznymi z poziomu LISP-a. Jednym z nich jest wybieranie wstawień bloków dynamicznych czyli tworzenie zbiorów wskazań. Sam wybór takich bloków jest i tak już stosunkowo skomplikowany, jeśli zaś dodać do tego wybór bloków na podstawie określonych wartości ich parametrów, sprawa wydaje się być jeszcze bardziej trudniejsza.

Na początek zatem o „zwykłym” wyborze wstawień bloków dynamicznych, przy wykorzystaniu standardowej funkcji ssget. Koniecznym jest tutaj zapoznanie się z pojęciem  nazwa efektywna, o czym już pisałem wcześniej TUTAJ.  Wspomniana została tam funkcja zdefiniowana w CADPL-Pack-u, o nazwie cd:BLK_GetBlockNames.  Zwraca ona listę wszystkich nazw wstawień bloków dynamicznych o określonej nazwie. Poniżej wywołanie i wynik działania funkcji, dla  przykładowego bloku o nazwie „Pojazdy – metryczny” (z katalogu Sample AutoCAD-a):

Taka lista pozwala dopiero zbudować filtr zbioru wskazań funkcji ssget, dla wyboru przez nazwę (kod 2 DXF). Najprościej jest połączyć elementy listy w łańcuch tekstowy i przekazać jako wartość kodu 2 filtra ssget.

Pokazany tutaj sposób utworzenia zbioru wskazań określonego przez nazwę bloku, nazwę jego parametru i jego wartość, realizowany jest w dwóch etapach. Po pierwsze utworzony jest zbiór wskazań bloku dynamicznego (oknem) i przez nazwę. Gdy taki zbiór powstanie, ( w drugim etapie) zmieniany jest on na listę obiektów, które kolejno są „przeglądane”, pod kątem zgodności nazwy i wartości parametru bloku. Spełniające warunki są  listy usuwane, a z pozostałych jest tworzony drugi zbiór wskazań. Ich różnica jest wynikiem zwracanym przez funkcję. Zilustrowane jest to poniżej. Wybierane są bloki o nazwie: „Pojazdy – metryczny”, parametrze widoczności: „Typ (widok)”, i jego wartości: „Sedan (przód)”:

Tak samo jak dla parametru widoczności, w wywołaniu funkcji można wykorzystać inny dowolny parametr bloku. Poniżej wybór bloków na podstawie wartości parametru liniowego:

Oczywiście prezentowana funkcja ilustruje mechanizm działania tworzenia zbiorów wskazań bloków dynamicznych na podstawie wartości ich parametru. To najprostszy przykład. Na jego podstawie można zbudować inne bardziej rozbudowane procedury wyboru (np. z zadanego zbioru wskazań, z dowolną nazwą bloku, z dowolnym parametrem, z wieloma parametrami itp.).

Funkcja wygląda tak:


; ------------------------------------------------------------------ ;
; funkcja jk:BLK_DynPropSS tworzy zbior wskazan blokow dynamicznych
; o nazwie [BName] parametrze [PropName] i jego wartosci [PropValue]
; by kojacek 2017
; ------------------------------------------------------------------ ;
(defun jk:BLK_DynPropSS (BName PropName PropValue / s l n r c)
  (if
    (setq s
      (ssget
        (list
         '(0 . "INSERT")
          (cons 2
            (cd:STR_ReParse
              (cd:BLK_GetDynBlockNames BName)
              ",`"
            )
          )
        )
      )
    )
    (progn
      (setq l (cd:SSX_Convert s 0))
      (setq c (getvar "CMDECHO")
            n (ssadd)
      )
      (foreach % l
        (if
          (/= PropValue
            (cdr
              (assoc
                PropName
                (cd:BLK_GetDynamicProps % nil)
              )
            )
          )
          (ssadd % n)
        )
      )
      (if
        (< (sslength n)(sslength s))
        (progn
          (setvar "CMDECHO" 0)
          (setq r (SS_SSsub s n))
          (setvar "CMDECHO" c)
          (sssetfirst nil r)
          r
        )
      )
    )
  )
)

Do prawidłowego działania wymaga wcześniejszego załadowania funkcji bibliotecznych, zdefiniowanych w CADPL-Pack,  (cd:STR_ReParse, cd:BLK_GetDynBlockNames, cd:BLK_GetDynamicProps i cd:SSX_Convert), oraz funkcji SS_SSsub, którą znajdziesz w opisie tworzenia różnicy zbiorów wskazań.

( . . . )

AutoCAD, AutoLISP

Field-y w blokach LISP-em

Tak jak wspomniałem w poprzednim wpisie, ciąg dalszy o polach danych stosowanych w blokach. O ile wcześniej opisany sposób może radować użytkowników AutoCAD-a LT, jednak wytrawni użytkownicy pełnego AutoCAD-a, zwłaszcza zaś wrony LISP-owe, z pewnością mogą odczuwać uzasadniony dyskomfort. Związany oczywiście z nadmierną klikologią, bowiem jak wiadomo, wszystko to co w AutoCAD-zie wymaga 5-10 czynności, LISP-em należy zredukować do 2-4… 😉

Poniżej przedstawiam stosunkowo proste makro lispowe, służące do szybkiego tworzenia skojarzeń atrybutów jednego wybranego bloku.

Program wykorzystuje funkcje zdefiniowane w CADPL-Pack’u, oraz wymaga załadowania biblioteki arx: DOSLib. Potrzebna jest ona do wywołania okna dialogowego wyboru skojarzeń atrybutów.

Zdefiniowane jest polecenie ATAT.  Po jego wywołaniu wymagane jest wskazanie źródłowego atrybutu bloku, wyboru połączeń innych atrybutów dokonuje się w oknie dialogowym. Całe działanie zostało zredukowane do paru kliknięć myszką. Poniżej kod programu:


; =============================================================== ;
; atat.lsp - kojacek 2017
; =============================================================== ;
(defun C:ATAT (/ s a d b l la lb r i u o)
  (defun GetObjectID (obj doc) ;; by Lee Mac
    (if
      (eq "X64" (strcase (getenv "PROCESSOR_ARCHITECTURE")))
      (vlax-invoke-method
        (vla-get-Utility doc) 'GetObjectIdString obj :vlax-false
      )
      (itoa (vla-get-Objectid obj))
    )
  )
  (if
    (not dos_checklist)
    (princ "\nNie załadowano biblioteki DOSLib. ")
    (if
      (setq s (nentselp "\nWybierz atrybut bloku: "))
      (if
        (and 
          (setq d (entget (car s)))
          (= "ATTRIB" (cdr (assoc 0 d)))
        )
        (if
          (zerop (getpropertyvalue (car s) "HasFields"))
          (progn
            (setq b (cdr (assoc 330 d))
                  l (cd:BLK_GetAttEntity b)
            )
            (if
              (= 1 (length l))
              (princ "\nBlok ma tylko jeden atrybut. ")
              (progn
                (setq l (vl-remove (car s) l)
                      la (mapcar
                           '(lambda (% / %1)
                              (setq %1 (entget %))
                              (cons (cdr (assoc 2 %1)) %)
                            ) l
                         )
                      lb (mapcar 'car la)
                      lb (mapcar '(lambda (%)(cons % 0)) lb)
                )
                (if
                  (setq r
                    (dos_checklist
                      (strcat "Skojarz wartość atrybutu "
                        "\"" (cdr (assoc 2 d)) "\"" " jako FILED"
                      )
                      "Wybierz atrybuty:" lb
                    )
                  )
                  (progn
                    (setq i
                      (strcat
                        "%<\\AcObjProp Object(%<\\_ObjId "
                        (GetObjectID
                          (vlax-ename->vla-object (car s))
                          (cd:ACX_ADoc)
                        )
                        ">%).TextString>%"
                      )
                    )
                    (cd:SYS_UndoBegin)
                    (foreach % lb
                      (setq u (car %)
                            o (vlax-ename->vla-object
                                (cdr (assoc u la))
                              )
                      )
                      (if
                        (not (zerop (cdr (assoc u r))))
                        (progn
                          (vla-put-TextString o i)
                          (vla-update o)
                          (vla-regen
                            (cd:ACX_ADoc) acActiveViewport)
                        )
                      )
                    )
                    (cd:SYS_UndoEnd)
                  )
                  (princ "\nNie wybrano skojarzeń atrybutów. ")
                )
              )
            )
          )
          (princ "\nWskazany atrybut jest już field-em. ")
        )
        (princ "\nNie wskazano atrybutu. ")
      )
      (princ "\nNie wskazano obiektu. ")
    )
  )
  (princ)
)
; =============================================================== ;
(princ)

Przedstawione makro będące na poziomie prototypowania, ostatecznie wymagać powinno uzupełnienia o obsługę błędów, tutaj zostało to pominięte. Najważniejsze jest pokazanie mechanizmu działania.

( . . . )

AutoCAD

Field-y w blokach

Tematem tego i kolejnego wpisu będzie opis stosowania dynamicznych pól danych (FIELD) w atrybutach bloków, w szczególnym przypadku skojarzenia dwóch atrybutów tego samego bloku. Wartość jednego, ręcznie wpisanego atrybutu (czerwony tekst) powielona jest jako wartość innego atrybutu (szary podświetlony tekst). Widać to na rysunku poniżej:

Niniejszy opis dotyczy ustawienia połączenia wartości dwóch atrybutów tego samego bloku za pomocą pola danych, stąd może być pomocny dla użytkowników AutoCAD-a LT. Kolejny zaś wpis będzie traktował o zautomatyzowaniu tego procesu za pomocą LISP-a w pełnym AutoCAD-zie.

Dla przykładu potrzebny będzie prosty blok z dwoma lub więcej atrybutami. Poniżej przykładowy blok z czterema atrybutami w edytorze bloku:

Po wstawieniu bloku wystarczy wypełnić dowolną wartością jeden z atrybutów. Tutaj to ciąg znaków AAA, dla atrybutu o tagu NO:

Niechże zadaniem będzie skojarzenie wartości atrybutu NO z atrybutem A. W tym celu, w oknie edytora atrybutów, trzeba wybrać wiersz interesującego nas atrybutu (tutaj A), a następnie polu „Wartość„, z menu prawego klawisza myszy wybrać „Wstaw pole…” :

W nowym oknie należy kolejno wybrać kategorię pola (tutaj Obiekty), następnie z listy nazw pól zaznaczyć Obiekt, i na końcu wybrać przycisk wybierania obiektów:

Okno zostanie chwilowo ukryte aby można było wskazać obiekt. Oczywiście należy wskazać ten sam blok którego edycję atrybutów wcześniej rozpoczęliśmy. Po wyborze okno definiowania pola danych ponownie się pojawia, już z inną zawartością, dotyczącą właśnie odniesienia bloku. Teraz wystarczy wybrać z listy właściwości tag atrybutu źródłowego (tutaj NO), oraz wybrać format (tutaj brak):

Po zaakceptowaniu wartości okna pola danych, powracamy do edytora atrybutów. Jak widać wartość atrybutu NO, została przypisana atrybutowi A. W polu „Wartość” szare zaznaczenie, informuje że danymi jest pole (FIELD):

Taki mechanizm pozwala na wyświetlenie tekstu (wartość atrybutu) dynamicznie powiązanego z i innym atrybutem. Każdorazowa edycja atrybutu „źródłowego” znajdzie odzwierciedlenie w wartości atrybutu „docelowego”. Oczywiście warto pamiętać o zmiennej systemowej FIELDEVAL, a także o poleceniu UAKTUALNIJPOLADANYCH (_UPDATEFIELD).

( . . . )

AutoCAD, AutoLISP, CADPL-Pack

Gdzie jest TRASA?

Dzisiejszy wpis poświęcony jest niedostępnemu już w AutoCAD-zie poleceniu TRASA. Przykładowe proste makra pokazują też, że nie wszystko stracone, i że można nadal tworzyć obiekty TRACE. Proces usunięcia polecenia TRASA (_TRACE) z AutoCAD-a został zapowiedziany już w wersji 2009, a ostateczne pożegnanie nastąpiło w wersji 2012. Za pomocą tego polecenia można było tworzyć wypełnione odcinki o zadanej szerokości (którą określała zmienna systemowa TRACEWID). Trzeba przyznać że było to stosunkowo prymitywne narzędzie. Segmenty skonstruowane były jako wypełnione czworokąty (osobne obiekty typu TRACE). Segmenty nie były ze sobą powiązane, a edycja uchwytami dotyczyła osobno każdego z wierzchołków. Z tych powodów trasy były rzadko używane – dużo wygodniejsze w tworzeniu i edycji są polilinie. Być może wielu użytkowników AutoCAD-a, mogło nie zauważyć nawet faktu usunięcia możliwości rysowania tras.

Oczywiście w celu zachowania zgodności obiekty typu TRACE nadal istnieją w AutoCAD-zie i w specyfikacji DXF, jednak jedynymi możliwościami ich tworzenia, po wycofaniu polecenia TRACE, są mechanizmy API (LISP / VBA / ActiveX / NET).

Ciekawą informacją jest fakt istnienia drugiego obiektu bliźniaczo podobnego do wycofanego TRACE. Tym obiektem jest SOLID. Oba zresztą należą do tej samej  klasy AcDbEntity / AcDbTrace. Poniżej porównanie list DXF tych dwóch obiektów:

Jak widać obiekty nie różnią się niczym oprócz nazwy obiektu (kod DXF = 0), i (co oczywiste uchwytu (handle – kod DXF 5). Na marginesie – polecenie o nazwie _SOLID, w wersji polskiej ustalone jako OBSZAR, zaś sam obiekt przetłumaczony jako BRYŁA. Wydaje się że obie zlokalizowane nazwy nie są w tym przypadku dość szczęśliwe.

Jako przykład ilustrujący tworzenie obiektu TRACE, będącego wypełnionym prostokątem, przedstawiam poniżej dwie funkcje (zdefiniowane polecenia: RETA i RETE). Dodatkowo mają one możliwość rysowania SOLID zamiast TRACE. Polecenie RETA, wykorzystuje mechanizmy ActiveX. Poniżej kod:


; =============================================================== ;
; C:RETA - Tworzy wypełniony prostokat (TRACE / SOLD) metodami    ;
;          ActiveX - kojacek - 2017 -                             ;
; =============================================================== ;
(defun C:RETA (/ p c r o e)
  (if (not *jk-ReTrace*)(setq *jk-ReTrace* "Trasa"))
  (if
    (setq r
      (cd:USR_GetKeyWord
        "\nRysuj wypełniony prostokąt"
        '("Obszar" "Trasa") *jk-ReTrace*
      )
    )
    (if
      (setq p (getpoint "\nOkreśl pierwszy narożnik: "))
      (if
        (setq c
          (cd:USR_GetCorner p "\nOkreśl drugi narożnik: " T)
        )
        (progn
          (setq e (getvar "ELEVATION")
                c (list (car c)(cadddr c)(cadr c)(caddr c))
                c (mapcar '(lambda (%)(append % (list e))) c)
                c
                   (if
                    (= r "Trasa")
                    (apply 'append c)
                    (mapcar 'vlax-3d-point c)
                  )
                *jk-ReTrace* r
          )
          (cd:SYS_UndoBegin)
          (if
            (= *jk-ReTrace* "Trasa")
            (vla-AddTrace (cd:ACX_ASpace)
              (vlax-make-variant
                (vlax-safearray-fill
                  (vlax-make-safearray vlax-vbdouble
                    (cons 0 (1- (length c)))
                  )
                  c
                )
              )
            )
            (vla-AddSolid (cd:ACX_ASpace)
              (car c)(cadr c)(caddr c)(cadddr c)
            )
          )
          (cd:SYS_UndoEnd)
        )
        (princ "\nBłąd. Nie wskazano prawidłowego punktu. ")
      )
      (princ "\nBłąd. Nie wskazano prawidłowego punktu. ")
    )
    (princ "\nBłąd. Nie wybrano opcji. ")
  )
  (princ)
)

Polecenie RETE, którego definicja jest przedstawiona poniżej, funkcjonalnie wykonuje to samo działanie co polecenie RETA, z uwagi jednak na zastosowanie funkcji entmakex, kod makra jest bardziej zoptymalizowany. Lista danych DXF jest w obu przypadkach taka sama, z wyjątkiem danych kodu 0 (TRACE / SOLID). Poniżej kod:


; =============================================================== ;
; C:RETE - Tworzy wypełniony prostokat (TRACE / SOLD) funkcja     ;
;          entmakex - kojacek - 2017 -                            ;
; =============================================================== ;
(defun C:RETE (/ p c r o e)
  (if (not *jk-ReTrace*)(setq *jk-ReTrace* "Trasa"))
  (if
    (setq r
      (cd:USR_GetKeyWord
        "\nRysuj wypełniony prostokąt"
        '("Obszar" "Trasa") *jk-ReTrace*
      )
    )
    (if
      (setq p (getpoint "\nOkreśl pierwszy narożnik: "))
      (if
        (setq c
          (cd:USR_GetCorner p "\nOkreśl drugi narożnik: " T)
        )
        (progn
          (setq e (getvar "ELEVATION")
                c (list (car c)(cadddr c)(cadr c)(caddr c))
                c (mapcar '(lambda (%)(append % (list e))) c)
                c (list
                    (cons 10 (car c))
                    (cons 11 (cadr c))
                    (cons 12 (caddr c))
                    (cons 13 (cadddr c))
                  )
                *jk-ReTrace* r
          )
          (cd:SYS_UndoBegin)
          (entmakex
            (append
              (list
                (cons 0
                  (if (= *jk-ReTrace* "Trasa") "TRACE" "SOLID")
                )
                (cons 100 "AcDbTrace")
              ) c
            )
          )
          (cd:SYS_UndoEnd)
        )
        (princ "\nBłąd. Nie wskazano prawidłowego punktu. ")
      )
      (princ "\nBłąd. Nie wskazano prawidłowego punktu. ")
    )
    (princ "\nBłąd. Nie wybrano opcji. ")
  )
  (princ)
)

Oba polecenia będą działać poprawnie po załadowaniu biblioteki CADPL-Pack, w wersji z aktualizacją kwietniową 2017. Została tam dodana wykorzystywana tutaj funkcja, wskazania punktów o nazwie cd:USR_GetCorner:

( . . . )

AutoLISP

AutoLISP i pliki *.ini

Dzisiaj parę słów o plikach *.ini, i możliwościach manipulowania nimi za pomocą AutoLISP-a. Pliki o rozszerzeniu ini, to zwykłe pliki tekstowe, zawierające dane sformatowane w uporządkowany sposób. Wykorzystywane są jako pliki inicjalizacyjne i konfiguracyjne, w wielu programach systemu Windows. To stosunkowo przestarzały sposób, niemniej nadal szeroko wykorzystywany, ze względu na swoją prostotę, przejrzystość i elastyczność. Możliwość edycji pliku zarówno przez specjalizowany program, jak również przez użytkownika (w zwyczajnym edytorze tekstowym), uważana jest za zaletę. Głównymi wadami przechowywania danych w ten sposób są: ograniczenie typów danych (to tylko plik tekstowy), brak możliwości zagnieżdżenia danych i (co wyżej wymieniłem jako zaletę) – zbyt łatwy dostęp. Struktura pliku ini jest następująca:

  • dane pogrupowane są w unikalne sekcje, których nazwa umieszczona jest pomiędzy kwadratowymi nawiasami.
  • w każdej sekcji w kolejnych liniach dane są sformatowane jako asocjacje rozpoznawane przez klucz (unikalny w sekcji), po którym występuje separator (znak równości), a następnie właściwa wartość.
  • znak średnika na początku linii oznacza komentarz i jego treść jest ignorowana (tak jak w LISP-ie)

Poniżej przykład zawartości pliku has.ini, który z powodzeniem stosuję do zapisywania ustawień programu has.lsp:

Krótki program has.lsp, wykorzystujący ustawienia właśnie w pliku ini,postaram się przedstawić w bliskiej przyszłości, teraz omówię sposoby manipulacji danymi przechowywanymi w plikach *.ini, za pomocą AutoLISP-a, z których on korzysta.

Ani sam AutoLISP, ani VisualLISP nie oferują żadnych bezpośrednich funkcji dostępu do plików typu ini. Oczywiście można napisać własne funkcje tego typu, bazując na mechanizmach zapisu i odczytu plików tekstowych, jakie oferuje LISP. Istnieją jednak jeszcze dwie drogi: wykorzystanie biblioteki acetutil.arx, z pakietu ExpressTools, lub narzędzia firm trzecich – na przykład DOSLib (funkcje dos_getini i dos_getini). W swojej praktyce z reguły korzystam z tej pierwszej możliwości – zawsze bowiem, w każdej wersji AutoCAD-a instaluję ET.

W acetutil.arx zostały zdefiniowane dwie funkcje dostępu do plików *.ini. Są to:

  • acet-ini-get – służąca do odczytu. Jej składnia wygląda tak:
    (acet-ini-get inifile [section [key [default]]])

    gdzie wymaganym argumentem jest inifile – nazwa pliku do odczytu, oraz opcjonalne argumenty section, key i default. Jeśli występuje tylko argument inifile, ta funkcja zwraca listę nazw sekcji lub nil w przypadku gdy nie można otworzyć pliku ini do odczytu. Jeżeli występuje argument z nazwą sekcji (section), zostanie zwrócona lista nazw kluczy w tej sekcji. Jeżeli argumentem jest key, zwracana jest wartość zeń skojarzona. Argument default (jeśli występuje) zwracany jest w przypadku gdy nie można znaleźć wartości podanej w argumencie key. Funkcja zwraca nil, jeśli nie można znaleźć żadnych informacji.

  • acet-ini-set – służąca do zapisu. Jej składnia wygląda tak:
    (acet-ini-set inifile section [key [value]])

    gdzie dwa wymagane argumenty to oczywiście  inifile – nazwa pliku, oraz section. Kolejne (key i value) są argumentami opcjonalnymi. Jeżeli zostanie podany argument section z nieistniejącym wpisem w pliku (i równocześnie podane zostaną argumenty key i value), zostanie on utworzony, wraz z kluczem i przypisaną wartością . Wywołanie funkcji tylko z argumentem section (istniejącym w pliku), zostanie wtedy usunięta cała sekcja (łącznie ze wszystkimi wpisami w sekcji). Argument value zawsze nadpisuje znalezioną w pliku wartość klucza key.

Stosując tylko te dwie funkcje mamy możliwość pełnej kontroli nad ustawieniami zapisywanymi i odczytywanymi w dowolnym pliku ini. Oczywiście pamiętać należy o ograniczeniach, także wcześniej wspomnianych. Ponadto należy zwracać uwagę na używanie w nazwach czy wartościach małych i wielkich liter.

Alternatywnym sposobem zapisywania ustawień programów jest wykorzystywanie dostępu do rejestru Windows. Niemniej jednak stosowanie plików ini, w wielu przypadkach jest ze względu na swoją prostotę i elastyczność rozwiązaniem najbardziej optymalnym.

(  . . . )