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...

mc-setAnnoScale

Komentarze

Popularne posty z tego bloga

Nowe wydanie u-View 1.0 - dawny ViewPortMaster

Jak zmienić centrum widoku rzutni w obszarze papieru