Jak ustawić skalę opisową rzutni poprzez VisualLisp
Z poziomu Visual Lisp można ustawić wiele własności obiektów typu viewport
ale nie ma funkcji, dzięki której można bezpośrednio ustawić skalę opisową dla danej rzutni. Trudno znaleźć jakiekolwiek opisy jak to zrobić. Udało mi się znaleźć using lisp to set annotation scale for a viewport sposób wykorzystujący dodatkową funkcję napisaną w ObjectARX w celu ominięcia ograniczeń Visual Lisp ale z powodu słabej znajomości C++ i braku chęci jego nauki, znaleziony sposób mnie nie satysfakcjonował.
Drążyłem temat głębiej i udało mi się w odkryć, że jeżeli rzutnia ma ustawioną skalę opisową, to dodatkowe informacje zapisane są w xRecord o nazwie ASDK_XREC_ANNOTATION_SCALE_INFO
w słowniku załączonym do obiektu rzutni. Dzięki poniższej funkcji odczytamy dane zawarte w xRecord obiektu:
(setq vpDict (vla-GetExtensionDictionary vpObj) ;get extension dictionary of viewport object
vpXrec (vla-item vpDict "ASDK_XREC_ANNOTATION_SCALE_INFO") ;get xRecord "ASDK_XREC_ANNOTATION_SCALE_INFO"
);setq
(vla-GetXrecordData vpXrec 'dxfCodes 'dxfData) ;get the xRecord data
Należy pamiętać, że xRecord ASDK_XREC_ANNOTATION_SCALE_INFO
jest obecny tylko w rzutni z ustawioną skalą opisową, w przeciwnym wypadku funkcja vla-item
wywoła błąd.
Po wyodrębnieniu surowych wartości typu variant
otrzymujemy:
nth | dxfCodes | dxfData |
---|---|---|
0 | 90 | 1 |
1 | 340 | (-9415280 -69366291) |
Nie wiem co kryje się pod kodem numer: 90
, ale wiem, że Autocad bardzo nie lubi kiedy się go zmienia... ;).
To co nas interesuje znajduje się pod numerem 340
. Jest tam zapisany hardpoint link do obiektu skali opisowej znajdującego się słowniku ACAD_SCALELIST
z głównego słownika rysunku. Tylko jak zdekodować te liczby? Spróbujmy odczytać te same dane z poziomu AutoLISP:
(setq vpEnt (handent "22E") ;viewport object vpDxf (entget vpEnt) ;group codes of the object vpDictDxf (entget(cdr(assoc 360 vpDxf))) ;group codes of extension dictionary of the object vpXrecDxf (entget(cdr(assoc 360 vpDictDxf))) ;group codes of the xRecord );setq
Zawartość odczytanej zmiennej vpXrecDxf:
(-1 . <Entity name: 7ffff705b80>)
(0 . XRECORD)
(5 . 230)
(102 . {ACAD_REACTORS)
(330 . <Entity name: 7ffff705b70>)
(102 . })
(330 . <Entity name: 7ffff705b70>)
(100 . AcDbXrecord)
(280 . 1)
(90 . 1)
(340 . <Entity name: 7ffff705590>)
Tak jak widać, pod numerem 340
znajduje się Entity Name
. Niestety Autocad (GstarCAD przeciwnie) nie pozwala na modyfikację zawartości zmiennej vpXrecDxf
funkcjami subst entmod
. Trzeba się wrócić do VisualLisp i zbadać dokładniej tę listę liczb (-9415280 -69366291)
. Znalazłem post, według którego ta lista to objectID reprezentowany poprzez dwie liczby typu long32. Utknąłem w tym miejscu, ponieważ nie znalazłem sposobu konwersji obiektu eName na objectID w tak dziwnym zapisie.
Nie mając lepszych pomysłów postanowiłem zapisać rysunek w formacie dxf i spróbować przeanalizować co jest zapisane przy numerze 340 w czystym tekście. Okazało się, że znajduje się tam handle przypisanego obiektu. Zatem, może zamiast walczyć z objectID spróbujmy zapisać nazwę handle w danych xRecord? Bingo! Tak jak pisałem wcześniej, nie może być to zrobione funkcjami AutoLISP, ale metody Visual Lisp już na to pozwalają 🙂
Uwaga dotycząca GstarCAD - ponieważ GstarCAD pewne rzeczy robi po swojemu, więc i tym nie było inaczej 😝. Dla odmiany, nie można zmienić odniesienia do obiektu skali poprzez Visual Lisp, ale można poprzez AutoLISP. Różnice w zapisie zmian zawarte w poniższym kodzie.
Poniższe funkcje pokazują krok po kroku jak to zrobić:
;===================================================================================
; mc-setAnnoScale - routine sets the annotation scale of viewport object
;
; _obj <ENAME/VLA-OBJECT> - viewport object to set the scale
; _sc <STR/REAL> - scale description or factor
;
; EXAMPLE OF USE: (mc-setAnnoScale (car(entsel)) "1:50")
;===================================================================================
(defun mc-setAnnoScale ( _obj _sc / vpDict vpXrec dxfCodes dxfData oDxf dDxf xDxf scH)
;---------find handle of the scale record with given name----------------
(if (setq scH (mc-findScale _sc))
(progn
(setq _obj (if (= 'ENAME (type _obj)) ;if eName then, convert to VLA-Object
(vlax-ename->vla-object _obj)
_obj
);if
vpDict (vla-GetExtensionDictionary _obj) ;get extension dictionary of viewport object
vpXrec (vl-catch-all-apply 'vla-item(list vpDict "ASDK_XREC_ANNOTATION_SCALE_INFO")) ;get xRecord "ASDK_XREC_ANNOTATION_SCALE_INFO"
);setq
(if (= (getvar "PRODUCT") "GstarCAD")
;-------------------------------------------GSTARCAD-------------------------------------------------------------
(progn
(if (vl-catch-all-error-p vpXrec)
;-------------if there is no xRecord "ASDK_XREC_ANNOTATION_SCALE_INFO" then add it and fill it with desired values-------------
(setq vpXrec (vla-AddXrecord vpDict "ASDK_XREC_ANNOTATION_SCALE_INFO")) ;add xRecord to dictionary
);if
(setq oDxf (entget(vlax-vla-object->ename _obj)) ;group codes of viewports object
dDxf (entget(cdr(assoc 360 oDxf))) ;group codes of extension dictionary
xDxf (entget(cdr(assoc 360 dDxf))) ;group codes of xRecord
);setq
(if (not(assoc 90 xDxf))
(setq xDxf (append xDxf (list (cons 90 1) (cons 340 (handent scH)))))
(setq xDxf (subst (cons 340 (handent scH)) (assoc 340 xDxf) xDxf))
);if
(entmod xDxf) ;save new values
);progn
;-------------------------------------------AUTOCAD/ZWCAD/BRICSCAD-------------------------------------------------------------
(progn
(if (vl-catch-all-error-p vpXrec)
;-------------if there is no xRecord "ASDK_XREC_ANNOTATION_SCALE_INFO" then add it and fill it with desired values-------------
(progn
(setq vpXrec (vla-AddXrecord vpDict "ASDK_XREC_ANNOTATION_SCALE_INFO") ;add xRecord to dictionary
dxfCodes (vlax-make-variant ;make and fill safearray with dxf codes of xRecord
(vlax-safearray-fill
(vlax-make-safearray vlax-vbInteger '(0 . 1))
'(90 340)
);vlax-safearray-fill
);vlax-make-variant
dxfData (vlax-make-variant ;make and fill safearray with dxf data of xRecord
(vlax-safearray-fill
(vlax-make-safearray vlax-vbVariant '(0 . 1))
(list 1 scH)
);vlax-safearray-fill
);vlax-make-variant
);setq
(vla-SetXrecordData vpXrec dxfCodes dxfData) ;set the xRecord data
);progn
;-------------if there is the xRecord "ASDK_XREC_ANNOTATION_SCALE_INFO" then change the value of scale object reference-------------
(progn
(vla-GetXrecordData vpXrec 'dxfCodes 'dxfData) ;get the xRecord data
(vlax-safearray-put-element dxfData 1 scH) ;put the scale object handle in the right place
(vla-SetXrecordData vpXrec dxfCodes dxfData) ;and save the new values in the xRecord
(setq vpDict (vla-GetExtensionDictionary _obj))
(vla-GetXrecordData vpXrec 'dxfCodes 'dxfData) ;get it again to make shire they are display properly
);progn
);if
);progn
);if
);progn
);if
);defun
;===================================================================================
; mc-findScale - check if provided scale is present in the drawing
;
; anSc <STR/REAL> - scale description or factor
; RetVal <STR/nil> - if found then return handle of the object otherwise nil
;
; EXAMPLE OF USE: (mc-findScale "1:50") or (mc-findScale 0.02)
;===================================================================================
(defun mc-findScale ( anSc / scDict i doCont scHand)
(setq scDict (dictsearch (namedobjdict) "ACAD_SCALELIST") ;dictionary with scales
i 0 ;loop counter
doCont T ;loop execution marker
);setq
(while (and
doCont
(< i (length scDict))
);and
(if (= 3 (car(nth i scDict)))
;-------------if it's name of scale object-------------
(progn
(setq scEnt (entget(cdr(nth (1+ i) scDict)))) ;get the dxf codes of the entity
(cond
(
;-------------if anSc is a name of scale (string) and the names are equal-------------
(and
(= (type anSc) 'STR)
(= anSc (cdr(assoc 300 scEnt)))
);and
(setq scHand (cdr(assoc 5 scEnt)) ;return the handle of scale object
doCont nil ;exit the lopp
);setq
);#string
(
;-------------if anSc is a scale factor (number) and the factors are equal-------------
(and
(numberp anSc)
(= anSc (/ (cdr(assoc 140 scEnt)) (cdr(assoc 141 scEnt))))
(not(wcmatch (cdr (assoc 300 scEnt)) "*XREF"))
);and
(setq scHand (cdr(assoc 5 scEnt))
doCont nil
);setq
);#number
(T
(setq i (+ i 2)) ;increment counter by 2 to get to the next scale entity
)
);cond
);progn
(setq i (1+ i)) ;increment counter by 1
);if
);while
;-------------return scale object handle or nil if not found-------------
scHand
);defun
Okazało się, że ustawienie skali opisowej rzutni, nie jest aż tak trudne jak na początku się wydawało...
Komentarze
Prześlij komentarz