AutoCAD... AutoLISP... VisualLISP...

  [46] Gdzie jestem? czyli ActiveSpace

index  

  Gdy rysujemy w AutoCAD-zie ręcznie, pytanie właściwie nie istnieje. Rysujemy po prostu w obszarze który jest obecnie aktualnym. Może to być obszar modelu lub papieru, a tutaj możliwości jest więcej. Rysowanie może odbywać się w jednej z wielu zakładek (układów). Mało tego. "Będąc w papierze" możemy mieć aktywną dowolną rzutnię... czyli możemy rysować w modelu...
Z historycznego punktu widzenia, nie zawsze tak było. Na samym początku był tylko obszar modelu. W AutoCAD release 11 (XI 1990) po raz pierwszy wprowadzono obszar papieru (rzutnie jako podział ekranu istniały już w wersji wcześniejszej - Release 10). W R11 pojawiła się zmienna systemowa TILEMODE. Może ona przybierać wartość 0 lub 1. Działa jak przełącznik - wybiera aktualną zakładkę Model lub zakładkę ostatniego arkusza (obecnie) a wtedy tylko jako przełącznik Model/Papier. W AutoCAD Relaease 13 (XI 1994) nastąpiła przebudowa rysunkowej bazy danych AutoCAD-a, czego rezultatem jest (między innymi) wydzielenie (i traktowanie) obszaru modelu i obszaru papieru jako bloki (BLOCK_RECORD w tablicy BLOCK). Innymi słowy: dowolna modyfikacja rysunku (np. w obszarze modelu) jest w rzeczywistości modyfikacją definicji bloku o nazwie *Model_Space. Wykorzystanie tych cech przynosi w AutoCAD 2000 (1999), możliwość tworzenia nieograniczonej ilości zakładek (zwanych Arkuszami albo Układami). Stworzony został nowy obiekt o nazwie Layout. Należy jednak rozróżnić obiekty typu Layout od bloków reprezentujących oddzielne arkusze obszaru papieru. Najogólniej różnica polega na tym że obiekty Layout przechowują informacje dotyczące ustawień plotowania, oraz stylu widoczności układu, blok natomiast przechowuje informacje dotyczące geometrii dla arkusza. Pamiętać należy rówież że jeden obiekt typu Layout przypisany jest tylko jednemu blokowi *Paper_Space. Zależność ta zachodzi również w odwrotnym kierunku.

Ten "przydługi wstęp", mam nadzieję nie przysłonił tytułowego pytania: gdzie jestem? Programujący w AutoLISP, w zależności od swojego zaawansowania, prędzej czy póŸniej staną przed takim pytaniem. Co gorsza - muszą znaleŸć na nie odpowiedŸ. Rozważmy trzy możliwe przypadki:
  1) Aktywna jest zakładka "Model". Zmienna TILEMODE = 1, CTAB = "Model", CVPORT = 2. Jesteśmy w modelu i rysujemy w obszarze modelu.
  2) Aktywna jest zakładka "Layout1". Zmienna TILEMODE = 0, CTAB = "Layout1", CVPORT = 1. Jesteśm w papierze i rysujemy w obszarze papieru.
  3) Aktywna jest zakładka "Layout2". Zmienna TILEMODE = 0, CTAB = "Layout2", CVPORT = 2 (lub dowolna liczba całkowita, ale nie 1) Jesteśmy w obszarze papieru a rysujemy w... modelu.
Programując używając command rysujemy tak samo, jak ma to miejsce w AutoCAD-zie podczas normalnej pracy - obiekty tworzone są w aktualnej przestrzeni.
Wykorzystując entmake do tworzenia obiektów, możemy niezależnie od aktualnej przestrzeni sytuować je w dowolnym miejscu. Wystarczy że do listy entmake dostawimy parę kropkową o kodzie 410 z nazwą obszaru.
Przykładowo: (410 . "Model") albo (410 . "Layout1"). Do sprawdzenia poprawności nazw istniejących w rysunku arkuszy służy funkcja layoutlist.
Najwięcej możliwości oferuje nam jednak funkcjonalność ActiveX. Wykorzystując właściwość że zarówno obszar modelu, obszar papieru, są blokami - uzyskujemy do nich dostęp w taki sam sposób jak do bloków, a co za tym idzie nie ma ograniczeń co do tworzenia obiektów.
Bardzo istotną tutaj jest właśnie kwestia: "gdzie jestem?" Wracając do wyżej przedstawionych przypadków, definiujemy funkcję która zwraca nam obiekt (VLA-OBJECT) aktualnej przestrzeni - czyli modelu, lub odpowiedniej zakładki (bloku *Paper_space a nie obiektu Layout) obszaru papieru. Funkcja wygląda tak:

;; Zwraca obiekt: aktywny obszar                              ;
;;                                                            ;
(defun jk:ACX_GetActiveSpace ()
  (if
    (= (getvar "TILEMODE") 1)
    (jk:ACX_GetModel)
    (if
      (/= (getvar "CVPORT") 1)
      (jk:ACX_GetModel)
      (vla-item (jk:ACX_GetBlocks) "*Paper_Space")
    )
  )
)
Funkcja potrzebuje jeszcze innych wążnych funkcji bibliotecznych, zwracających obiekt typu AcitiveDocument oraz kolekcję Blocks. Funkcje wyglądają tak:

;; Aktywny dokument -> zmienna globalna: *jk-ActDoc           ;
;;                                                            ;
(defun jk:ACX_ActDoc ()
  (if
    (not *jk-ActDoc)
    (setq *jk-ActDoc
      (vla-get-activedocument
        (vlax-get-acad-object)
      )
    )
    *jk-ActDoc
  )
)
;; Obszar modelu -> zmienna globalna: *jk-Model               ;
;;                                                            ;
(defun jk:ACX_GetModel ()
  (if
    (not *jk-Model)
    (setq *jk-Model (vla-get-modelspace (jk:ACX_ActDoc)))
    *jk-Model
  )
)
;; kolekcja bloków -> zmienna globalna: *jk-Blocks            ;
;;                                                            ;
(defun jk:ACX_GetBlocks ()
  (if
    (not *jk-Blocks)
    (setq *jk-Blocks (vla-get-Blocks (jk:ACX_ActDoc)))
    *jk-Blocks
  )
)
Jak widać funkcje te tworzą zmienne globalne: *jk-ActDoc *jk-Model *jk-Blocks, i są tak skonstruowane aby po ich ustaleniu zwracać ich wartość bez konieczności ponownego wyznaczania. Ma to znaczenie w sytuacji intensywnego wykorzystywania programów bazujących na specyfikacji ActiveX AutoCAD-a. Dla zachowania porządku, jasności kodu, oraz spójności - tak jak wszystkie inne moje zmienne globalne mają one prefix: *jk-.
Stosowanie funkcji jk:ACX_GetActiveSpace znacznie ułatwia programowanie, tworzenie obiektów (jeśli nie ma innych wymagań) wykonywane jest "w locie" w aktualnej przestrzeni, bez konieczności każdorazowego ich rozróżniania. Przykładowo:

(vla-AddLine (jk:ACX_GetActiveSpace)(vlax-3d-point '(0 0 0))(vlax-3d-point '(100 200 0)))

utworzy linię, zawsze w aktualnej przestrzeni w momencie wywołania.
  Aktualizacja: 13-11-2009