Прозрачность в AutoCAD
Понадобилось мне тут поковыряться с прозрачностью примитивов и слоев в "чистом" AutoCAD. Обнаружились достаточно интересные вещи, доложу я вам!
Прежде чем двигаться дальше, понадобится вспомнить, что само понятие прозрачности появилось в AutoCAD 2011. В AutoCAD 2009 (других версий просто нет) было понятие "растр либо прозрачен, либо нет", но установить прозрачность равной, например, 15%, было невозможно.
Итак, попробуем поиграться с установлением и изменением прозрачности:
- обычных примитивов
- растров
- слоев
Используем AutoCAD 2015 Eng / Rus, 64-разрядный, со всеми установленными обновлениями.
Конечно, идеальным был бы вариант установки прозрачности через ActiveX - тогда можно будет безболезненно менять прозрачность даже в неактивном документе. Но, к сожалению, тут не все просто.
Во-первых, возникает путаница с самим понятие "прозрачности" для растров и масок: этим примитивам можно установить свойство Transparency в :vlax-true, повторив ситуацию с более старыми версиями AutoCAD. А для "процентной" прозрачности используется EntityTransparency. То есть, казалось бы, можно нарисовать код установки прозрачности примитиву:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | (defun set-ent-trans-vla (ent tr / err res) ;| * Установка прозрачности для примитива * Параметры вызова: ent - vla-указатель на графический примитив. Не контролируется tr - устанавливаемое значение прозрачности. Строка или целое число |; (cond ((not (vlax-property-available-p ent 'entitytransparency t)) (princ "\nEntityTRansparency not available") ) ((vl-catch-all-error-p (setq err (vl-catch-all-apply (function (lambda () (vla-put-entitytransparency ent tr) ) ;_ end of lambda ) ;_ end of function ) ;_ end of vl-catch-all-apply ) ;_ end of setq ) ;_ end of vl-catch-all-error-p (princ (strcat "\nError set EntityTransparency property : " (vl-catch-all-error-message err))) ) (t (vla-get-entitytransparency ent) ) ) ;_ end of cond ) ;_ end of defun |
Но! Попробуем задать прозрачность "ByLayer" ("ПоСлою") или "ByBlock" ("ПоБлоку")! Что проще, казалось бы! Задаем в качестве tr прямо строку - "ByLayer", и все!
И все? Нет, не получится. Стоит код запустить в русской версии AutoCAD, и вместо "bylayer" придется передавать "послою". Сильно подозреваю, что при работе в других локализациях ситуация будет аналогичной. Конечно, от безысходности можно и такое использовать, но попробуем все же работу через ename-представления. Понятно, что это гарантированно отключает работу в неактивном документе и осложняет обработку блоков, но что поделать...
Внимательно посмотрев DXF Reference, мы можем увидеть новую необязательную группу: 440. Именно ее значение и регулирует значение прозрачности. Кажется, вот оно решение! Но несколько экспериментов по установке прозрачности "ПоСлою" у меня сначала потерпели полное фиаско. Спасибо Александру Ривилису, он подсказал решение. Всего-то надо установить в 440 группу значение 0, и она исчезает. То есть можно нарисовать код:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | (defun _kpblc-ent-modify-autoregen (ent bit value ext_regen / ent_list old_dxf new_dxf layer_dxf70) ;| * Функция модификации указанного бита примитива * Параметры вызова: * entity - примитив, полученный через (entsel), (entlast) etc * bit - dxf-код, значение которого надо установить * value - новое значение * regen - выполнять или нет регенерацию примитива сразу. t/ nil * Примеры вызова: (_kpblc-ent-modify-autoregen (entlast) 8 "0" t) ; перенести последний примитив на слой 0 (_kpblc-ent-modify-autoregen (entsel) 62 10 nil) ; установить выбранному примитиву цвет 10 * Возвращаемое значение: * примитив с модифицированным dxf-списком. Примитив перерисовывается в * зависимости от значения ключа ext_regen |; (setq ent (_kpblc-conv-ent-to-ename ent)) (if (not (and (or (= (strcase (cdr (assoc 0 (entget ent))) nil) "STYLE") (= (strcase (cdr (assoc 0 (entget ent))) nil) "DIMSTYLE") (= (strcase (cdr (assoc 0 (entget ent))) nil) "LAYER") ) ;_ end of or (= bit 100) ) ;_ end of and ) ;_ end of not (progn (setq ent_list (entget ent) new_dxf (cons bit (if (and (= bit 62) (= (type value) 'str)) (if (= (strcase value) "BYLAYER") 256 0 ) ;_ end of if value ) ;_ end of if ) ;_ end of cons ) ;_ end of setq (if (not (equal new_dxf (setq old_dxf (assoc bit ent_list)))) (progn (entmod (if old_dxf (subst new_dxf old_dxf ent_list) (append ent_list (list new_dxf)) ) ;_ end of if ) ;_ end of entmod (if ent_regen (entupd ent) (redraw ent) ) ;_ end of if ) ;_ end of progn ) ;_ end of if ) ;_ end of progn ) ;_ end of if ent ) ;_ end of defun |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | (defun set-ent-trans-ename (ent tr / lst) ;| * Установка прозрачности для примитива * Параметры вызова: ent - ename-указатель на графический примитив. Не контролируется tr - устанавливаемое значение прозрачности. Строка или целое число (для нецелых дробная часть отсекается) |; (setq lst '(("bylayer" . 0) ("byblock" . 16777216) ("0" . 33554687) ("1" . 33554684) ("2" . 33554681) ("3" . 33554679) ("4" . 33554676) ("5" . 33554674) ("6" . 33554671) ("7" . 33554669) ("8" . 33554666) ("9" . 33554664) ("10" . 33554661) ("11" . 33554658) ("12" . 33554656) ("13" . 33554653) ("14" . 33554651) ("15" . 33554648) ("16" . 33554646) ("17" . 33554643) ("18" . 33554641) ("19" . 33554638) ("20" . 33554636) ("21" . 33554633) ("22" . 33554630) ("23" . 33554628) ("24" . 33554625) ("25" . 33554623) ("26" . 33554620) ("27" . 33554618) ("28" . 33554615) ("29" . 33554613) ("30" . 33554610) ("31" . 33554607) ("32" . 33554605) ("33" . 33554602) ("34" . 33554600) ("35" . 33554597) ("36" . 33554595) ("37" . 33554592) ("38" . 33554590) ("39" . 33554587) ("40" . 33554585) ("41" . 33554582) ("42" . 33554579) ("43" . 33554577) ("44" . 33554574) ("45" . 33554572) ("46" . 33554569) ("47" . 33554567) ("48" . 33554564) ("49" . 33554562) ("50" . 33554559) ("51" . 33554556) ("52" . 33554554) ("53" . 33554551) ("54" . 33554549) ("55" . 33554546) ("56" . 33554544) ("57" . 33554541) ("58" . 33554539) ("59" . 33554536) ("60" . 33554534) ("61" . 33554531) ("62" . 33554528) ("63" . 33554526) ("64" . 33554523) ("65" . 33554521) ("66" . 33554518) ("67" . 33554516) ("68" . 33554513) ("69" . 33554511) ("70" . 33554508) ("71" . 33554505) ("72" . 33554503) ("73" . 33554500) ("74" . 33554498) ("75" . 33554495) ("76" . 33554493) ("77" . 33554490) ("78" . 33554488) ("79" . 33554485) ("80" . 33554483) ("81" . 33554480) ("82" . 33554477) ("83" . 33554475) ("84" . 33554472) ("85" . 33554470) ("86" . 33554467) ("87" . 33554465) ("88" . 33554462) ("89" . 33554460) ("90" . 33554457) ) ) ;_ end of setq (_kpblc-ent-modify-autoregen ent 440 (cdr (assoc (cond ((= (type tr) 'int) (itoa tr) ) ((= (type tr) 'real) (itoa (fix tr)) ) (t tr ) ) ;_ end of cond lst ) ;_ end of assoc ) ;_ end of cdr t ) ;_ end of _kpblc-ent-modify-autoregen ) ;_ end of defun |
Теперь можно передавать и строку "bylayer", не заморачиваясь с локализацией.
Ффух, теперь можно попытаться поиграться и с прозрачностью слоев.
А вот тут все совсем грустно. Дамп vla-представления слоя вообще не показывает свойств с Transparency:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | _$ (vlax-dump-Object (vla-item (vla-get-layers (vla-get-ActiveDocument (vlax-get-acad-object))) "0") t) ; IAcadLayer: A logical grouping of data, similar to transparent acetate overlays on a drawing ; Property values: ; Application (RO) = #<VLA-OBJECT IAcadApplication 000000014029a130> ; Description = "" ; Document (RO) = #<VLA-OBJECT IAcadDocument 000000000966a318> ; Freeze = 0 ; Handle (RO) = "10" ; HasExtensionDictionary (RO) = -1 ; LayerOn = -1 ; Linetype = "Continuous" ; Lineweight = -3 ; Lock = 0 ; Material = "Global" ; Name = "0" ; ObjectID (RO) = 45 ; ObjectName (RO) = "AcDbLayerTableRecord" ; OwnerID (RO) = 46 ; PlotStyleName = "Color_7" ; Plottable = -1 ; TrueColor = #<VLA-OBJECT IAcadAcCmColor 0000000031969dc0> ; Used (RO) = -1 ; ViewportDefault = 0 ; Methods supported: ; Delete () ; GetExtensionDictionary () ; GetXData (3) ; SetXData (2) |
Поэтому придется опять же работать с ename-представлением.
Сначала просто выполним entget соответствующей записи:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | _$ (entget (tblobjname "layer" "0") '("*")) '((-1 . <entity name: 7ffffb08900>) (0 . "LAYER") (5 . "10") (102 . "{ACAD_XDICTIONARY") (360 . <entity name: 7ffffb0a240>) (102 . "}") (330 . <entity name: 7ffffb08820>) (100 . "AcDbSymbolTableRecord") (100 . "AcDbLayerTableRecord") (2 . "0") (70 . 0) (62 . 7) (6 . "Continuous") (290 . 1) (370 . -3) (390 . <entity name: 7ffffb088f0>) (347 . <entity name: 7ffffb08ee0>) (348 . <entity name: 0>) ) |
А теперь вручную поменяем прозрачность слоя, например, на 15%:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | _$ (entget (tblobjname "layer" "0") '("*")) '((-1 . <entity name: 7ffffb08900>) (0 . "LAYER") (5 . "10") (102 . "{ACAD_XDICTIONARY") (360 . <entity name: 7ffffb0a240>) (102 . "}") (330 . <entity name: 7ffffb08820>) (100 . "AcDbSymbolTableRecord") (100 . "AcDbLayerTableRecord") (2 . "0") (70 . 0) (62 . 7) (6 . "Continuous") (290 . 1) (370 . -3) (390 . <entity name: 7ffffb088f0>) (347 . <entity name: 7ffffb08ee0>) (348 . <entity name: 0>) (-3 ("AcCmTransparency" (1071 . 33554648))) ) |
Становится интересно: появились расширенные данные для приложения "AcCmTransparency". Казалось бы, решение найдено: достаточно добавлять или изменять эти данные (благо список соответствия "процент" - "значение" уже есть), и прозрачность слоя будет меняться автоматом. К сожалению, номер не срабатывает: AutoCAD 2014 и 2015 просто игнорируют эти попытки, оставляя для слоя ту прозрачность, которая для него была установлена. Остается единственный способ установки прозрачности для слоя: командный
1 | (vl-cmdf "_.-layer" "_tr" Значение ИмяСлоя "" |
Я пробовал и через ActiveX, и через vla, и даже дошел до того, чтобы дать команду _.-layer _tr и тут же ее прервать - не сработало
Теперь, когда мы более-менее смогли установить прозрачность примитива / слоя, осталось только лишь его получить. Причем получить в нормальном и внятном виде - процент, послою или поблоку. Конечно, можно перенести список из функции set-ent-trans-ename в глобальные переменные и обращаться уже к нему, но есть способ попроще (подсмотрен здесь.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | (defun get-ent-trans-ename (ent / res) ;| * Получение прозрачности слоя / примитива * Параметры вызова: ent - ename-указатель на примитив |; (cond ((and (setq res (cdr (assoc 440 (entget ent)))) (= res 16777216) ) ;_ end of and "byblock" ) ((or (setq res (cdr (assoc 440 (entget ent)))) (setq res (cdr (assoc 1071 (cdar (cdr (assoc -3 (entget ent '("AcCmTransparency")) ) ;_ end of assoc ) ;_ end of cdr ) ;_ end of cdar ) ;_ end of assoc ) ;_ end of cdr ) ;_ end of setq ) ;_ end of or (fix (- 100 (/ (logand res -33554433) 2.55))) ) ) ;_ end of cond ) ;_ end of defun |
Для примитивов с прозрачностью "ПоСлою" вернет nil, для прозрачности "ПоБлоку" - "byblock", для всех остальных вариантов - установленные проценты прозрачности. Для слоя возвращается число от 0 до 90.
---
Использованные коды:
set-ent-trans-vla
set-ent-trans-ename
_kpblc-ent-modify-autoregen
_kpblc-conv-ent-to-ename
set-ent-trans-ename
Привет, Алексей!
Понравилась статья, провел свои исследования на тему прозрачности.
В твоем коде большой список значений, которые задает сам акад.
Но на самом деле, акад обрабатывает прозрачность в битном формате 0-255, а перевод в проценты вызывает такую интересную нелинейность.
В моем примере, программа устанавливает прозрачность в битах, либо в процентах, вычисляя необходимое значение. Не всегда оно будет совпадать с вычислениями акада, но отображаемое в процентах для пользователя значение верное.
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
;;by ElpanovEvgeniy
;; по мотивам статьи http://autolisp.ru/2015/01/12/acad_transparency/
;; установка прозрачности для примитива
;;
;; E - Примитив (car(entsel))
;; I - Прозрачность в процентах (1 - 99), битах (0 - 255), либо ПоСлою/ПоБлоку
;; F - Флаг, если задан, то I задается в битах, иначе в процентах.
;; при задании прозрачности ПоСлою/ПоБлоку флаг игнорируется.
;;
;; пример вызова:
;; (EEA-SET-TRANSPARENCY (car(entsel)) 20 nil) ;установка прозрачности 20%
;; (EEA-SET-TRANSPARENCY (car(entsel)) 240 t) ;установка прозрачности 20%
;; (EEA-SET-TRANSPARENCY (car(entsel)) acbylayer t) ;установка прозрачности по слою
;; (EEA-SET-TRANSPARENCY (car(entsel)) acbyblock nil) ;установка прозрачности по блоку
;; Возвращаемое значение - измененный DXF список примитива.
;|
(defun C:T1 ()
;; тестирование установки прозрачности с заданием процентов.
(EEA-SET-TRANSPARENCY-%
(car (entsel "\n Выберите примитив для смены прозрачности"))
(getint "\nВведите процент прозрачности")
NIL
)
(princ)
)
|;
(cond ((= I acbylayer) (entmod (vl-remove (assoc 440 (entget E)) (entget E))))
((= I acbyblock) (entmod (append (entget E) '((440 . 16777216)))))
((entmod (append (entget E) (list (cons 440 (+ 33554431 (if F (- 256 I) (fix (* (- 100 I) 2.56)))))))))))
Действительно, проверил код в 2014 акаде, удаление 440 кода не работает...
Новая версия с исправлениями:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
;;by ElpanovEvgeniy
;; по мотивам статьи http://autolisp.ru/2015/01/12/acad_transparency/
;; установка прозрачности для примитива
;;
;; E - Примитив (car(entsel))
;; I - Прозрачность в процентах (1 - 99), битах (0 - 255), либо ПоСлою/ПоБлоку
;; F - Флаг, если задан, то I задается в битах, иначе в процентах.
;; при задании прозрачности ПоСлою/ПоБлоку флаг игнорируется.
;;
;; пример вызова:
;; (EEA-SET-TRANSPARENCY (car(entsel)) 20 nil) ;установка прозрачности 20%
;; (EEA-SET-TRANSPARENCY (car(entsel)) 240 t) ;установка прозрачности 20%
;; (EEA-SET-TRANSPARENCY (car(entsel)) acbylayer t) ;установка прозрачности по слою
;; (EEA-SET-TRANSPARENCY (car(entsel)) acbyblock nil) ;установка прозрачности по блоку
;; Возвращаемое значение - измененный DXF список примитива.
;|
(defun C:T1 ()
;; тестирование установки прозрачности с заданием процентов.
(EEA-SET-TRANSPARENCY (car (entsel "\n Выберите примитив для смены прозрачности"))
(getint "\nВведите процент прозрачности")
NIL
)
(princ)
)
|;
(cond ((= I acbyblock) (entmod (append (entget E) '((440 . 16777216)))))
((entmod (append (entget E)
(list (cons 440
(if (= I acbylayer)
256
(+ 33554431
(if F
(- 256 I)
(fix (* (- 100 I) 2.56))
)
)
)
)
)
)
)
)
)
)
Для продолжения экспериментов нарисовал пару кодов (Евгений, я использовал твои подходы, каюсь ;)):
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
;; name : имя слоя
;; tr : прозрачность
(setq adoc (vla-get-activedocument (vlax-get-acad-object)))
(if (vl-catch-all-error-p
(setq err (vl-catch-all-apply
(function
(lambda ()
(setq ent (vla-item (vla-get-layers (vla-get-activedocument (vlax-get-acad-object))) name))
(vla-getxdata ent "AcCmTransparency" 'xt 'xd)
(setq xt (if xt
(vlax-safearray->list xt)
(list 1001 1071)
) ;_ end of if
xd (if xd
(mapcar (function vlax-variant-value) (vlax-safearray->list xd))
(list "AcCmTransparency" 0)
) ;_ end of if
xd (mapcar (function cons) xt xd)
xd (subst (cons 1071
(cond
((< tr 1.) (+ 33554431 (fix (* (- 1. tr) 256))))
(t (+ 33554431 (- 256 tr)))
) ;_ end of cond
) ;_ end of cons
(assoc 1071 xd)
xd
) ;_ end of subst
) ;_ end of setq
(vla-setxdata
ent
(vlax-safearray-fill
(vlax-make-safearray
vlax-vbinteger
(cons 0 (1- (length xd)))
) ;_ end of vlax-make-safearray
(mapcar (function car) xd)
) ;_ end of vlax-safearray-fill
(vlax-safearray-fill
(vlax-make-safearray
vlax-vbvariant
(cons 0 (1- (length xd)))
) ;_ end of vlax-make-safearray
(mapcar
(function
(lambda (x)
(vlax-make-variant (cdr x))
) ;_ end of LAMBDA
) ;_ end of function
xd
) ;_ end of mapcar
) ;_ end of vlax-safearray-fill
) ;_ end of vla-setxdata
) ;_ end of lambda
) ;_ end of function
) ;_ end of vl-catch-all-apply
) ;_ end of setq
) ;_ end of vl-catch-all-error-p
(princ (strcat "\nError : " (vl-catch-all-error-message err)))
) ;_ end of if
(command "_.regenall")
) ;_ end of defun
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
;; name : имя слоя
;; tr : прозрачность
(setq ent (tblobjname "layer" name)
ent (entget ent '("*"))
xd (cdr (assoc -3 ent))
value (cond
((< tr 1.) (+ 33554431 (fix (* (- 1. tr) 256))))
(t (+ 33554431 (- 256 tr)))
) ;_ end of cond
) ;_ end of setq
(setq xd (if xd
(subst (cons "AcCmTransparency"
(subst (cons 1071 value)
(assoc 1071 (cdr (assoc "AcCmTransparency" xd)))
(cdr (assoc "AcCmTransparency" xd))
) ;_ end of subst
) ;_ end of cons
(assoc "AcCmTransparency" xd)
xd
) ;_ end of subst
(cons "AcCmTransparency" (list (cons 1071 value)))
) ;_ end of if
) ;_ end of setq
(entmod (if (assoc -3 ent)
(subst (cons -3 xd) (assoc -3 ent) ent)
(append ent (list (list -3 xd)))
) ;_ end of if
) ;_ end of entmod
(entupd (cdr (assoc -1 ent)))
(command "_.regenall")
) ;_ end of defun
Попробовал я этими функциями поменять прозрачность слоя "0". Получились забавнейшие вещи
1. ActiveX-подход не срабатывает. От слова совсем. Ни при каких обстоятельствах. Даже если у слоя уже есть какое-то значение прозрачности - оно не меняется.
2. ename срабатывает только если уже была попытка задать прозрачность слою руками или командой _.-layer _tr (причем необязательно обрабатываемому).
3. Достаточно в ename-обработку добавить строку вида
как функция начинает корректно работать. Явно в файл dwg загружаются какие-то дополнительные приложения (этот вопрос я пока еще не исследовал).
Добавлю: прозрачность до 1 расценивается как %, больше - как биты (по аналогии с решением Евгения).
Алексей, инициализацию прозрачности для задания прозрачности нулевому слою, можно сделать и без обращений к командной строке:
Спасибо, добавил: в ename
в vla
И все равно ActiveX не работает. Блин, что ж я там не так делаю-то, интересно?
P.S. Еще немного, и опять на форуме задам вопрос ))))
P.P.S. Ссылки на соответствующие коды:
layer-trans-ename
layer-trans-vla
>> И все равно ActiveX не работает. Блин, что ж я там не так делаю-то, интересно?
Вообще то, все работает и если чертеж сохранить и заново открыть, все отображается верно!
Проблема в том, что у слоя нет метода для обновления видимости прозрачности. Все подразумевается автоматом. Вот и выходит, что через activex можно изменить прозрачность, но мы это не увидим...