Особенности vla-функций и их применения, часть 2
В предыдущей части уже были рассмотрены некоторые особенности работы vla-функций применительно к графической составляющей dwg-файла. Но были рассмотрены далеко не все вопросы. В частности, не было освещено "создание примитива через ActiveX без явного указания необязательных настроек". И никак не был затронут вопрос о модификации созданного примитива (точнее, удобства и очевидности изменения).
Начнем, пожалуй, с "настроек по умолчанию". Если в DXF Reference напрямую указываются необязательные параметры (то есть принимаемые текущими), то при работе через ActiveX ситуация иная: указываются только необходимые для создания примитива параметры. Все остальное необходимо задавать отдельными вызовами соответствующих функций. К таким параметрам можно отнести, например, цвет примитива (принимается текущий); тип линии примитива; слой примитива и т.п.
Создадим и активируем пользовательскую систему координат, ось Z которой непараллельна мировой и запустим следующие коды:
1 2 3 4 5 | (entmakex '((0 . "CIRCLE") (10 100. 10. 20.) (40 . 10.) ) ) ;_ end of entmakex |
и
1 2 3 4 5 6 7 | (vla-addcircle (vla-get-modelspace (vla-get-activedocument (vlax-get-acad-object)) ) ;_ end of vla-get-modelspace (vlax-3d-point '(100. 10. 20.)) 10. ) ;_ end of vla-addcircle |
Обратите внимание, что в первом случае окружность создается в мировой системе координат, а вторая - в текущей. При этом координаты центра у них не совпадают. Поигравшись с trans, можно добиться полного соответствия результатов vla- и ent-функций. Но пока разговор немного не об этом.
Разберемся с модификацией примитивов.
Как правило, текущие настройки мало кого устраивают, и создаваемые примитивы (это касается не только графических элементов) приходится менять. Иногда при создании, иногда - уже готовые, но пришедшие из других файлов.
Сначала разберем вариант внесения изменений в ename-представление примитива:
1 2 | _$ (setq ent (entlast)) <Entity name: -401519e8> |
Преобразуем его в список:
1 2 | _$ (entget ent) ((0 . "CIRCLE")(100 . "AcDbEntity") (8 . "0") (100 . "AcDbCircle") (10 100.0 20.0 -10.0) (40 . 10.0) (210 0.0 -1.0 0.0)) |
Ненужные и некритичные элементы списка удалены.
Последовательность изменения такова: сначала надо изменить список; потом - модифицировать примитив; потом - обновить его графическое представление. Для этого обычно используются соответственно subst, entmod, entupd. Допустим, мы хотим изменить значение 210 группы на '(-1.0 0.0 0.0). Сначала проверим работу subst:
1 2 3 4 5 | _$ (subst '(210 -1.0 0.0 0.0) (assoc 210 (entget ent)) (entget ent) ) ;_ end of subst ((0 . "CIRCLE") (100 . "AcDbEntity") (8 . "0") (100 . "AcDbCircle") (10 100.0 20.0 -10.0) (40 . 10.0) (210 -1.0 0.0 0.0)) |
Получилось? Получилось (еще бы!). Теперь обновляем примитив:
1 2 3 4 5 6 7 | _$ (entmod (subst '(210 -1. 0. 0.) (assoc 210 (entget ent)) (entget ent) ) ;_ end of subst ) ;_ end of entmod ; <...> _$ (entupd ent) |
Активируем AutoCAD и наблюдаем за изменениями примитива. Казалось бы, достаточно просто. Но не без некоторых подводных камней.
Например, в примитиве могут повторяться ключи точечных пар (пример можно посмотреть чуть выше - в частности, это группы 100), и использованный assoc вернет "не то". Можете попробовать, используя представленный механизм, заменить координаты второй (или n-дцатой) вершины обычной полилинии. Кроме того, некоторые точечные пары могут вообще отсутствовать при создании примитива (например, пара 370 - вес линии; или группа 3 в многосточном тексте), и в таком случае приходится ее принудительно добавлять. Внесение же изменений, например, в трехмерную полилинию для неподготовленного человека сродни шаманству.
Теперь возьмемся за изменение vla-представления.
Отменим только что сделанные изменения и вернемся в VLIDE:
1 2 | _$ (setq ent (vlax-ename->vla-object (entlast))) #<VLA-OBJECT IAcadCircle 01f43c0c> |
Результат немногим более информативен, чем показанный ранее (entget (entlast)). Посмотрим свойства и методы vla-примитива:
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 | _$ (vlax-dump-object ent t) ; IAcadCircle: AutoCAD Circle Interface ; Property values: ; Application (RO) = #<VLA-OBJECT IAcadApplication 00d74d3c> ; Area = 314.159 ; Center = (100.0 10.0 20.0) ; Circumference = 62.8319 ; Diameter = 20.0 ; Document (RO) = #<VLA-OBJECT IAcadDocument 01e0e958> ; Handle (RO) = "1FB" ; HasExtensionDictionary (RO) = 0 ; Hyperlinks (RO) = #<VLA-OBJECT IAcadHyperlinks 0b803294> ; Layer = "0" ; Linetype = "ByLayer" ; LinetypeScale = 1.0 ; Lineweight = -1 ; Material = "ByLayer" ; Normal = (0.0 -1.0 0.0) ; ObjectID (RO) = -1075124712 ; ObjectName (RO) = "AcDbCircle" ; OwnerID (RO) = -1075131144 ; PlotStyleName = "ByLayer" ; Radius = 10.0 ; Thickness = 0.0 ; TrueColor = #<VLA-OBJECT IAcadAcCmColor 073b04e8> ; Visible = -1 ; Methods supported: ; ArrayPolar (3) ; ArrayRectangular (6) ; Copy () ; Delete () ; GetBoundingBox (2) ; GetExtensionDictionary () ; GetXData (3) ; Highlight (1) ; IntersectWith (2) ; Mirror (2) ; Mirror3D (3) ; Move (2) ; Offset (1) ; Rotate (2) ; Rotate3D (3) ; ScaleEntity (2) ; SetXData (2) ; TransformBy (1) ; Update () T _$ |
Здесь сначала показываются доступные свойства примитива; следом идут его методы. Мы пока будем работать именно со свойствами, а методы оставим "на потом, если руки дойдут. И если кому-то понадобится".
Внесение изменений в vla-представления отличается от изменений ename-представлений. И отличия тут достаточно серьезные.
Прежде чем мы продолжим, следует напомнить, что при работе через ActiveX есть непреложное правило: первым аргументом функции обязательно идет указатель на объект, с которым выполняется работа. Без этого дальнейшее движение невозможно. Дополнительные данные по работе с ActiveX внутри VisualLISP можно посмотреть здесь.
Итак, отличия.
Во-первых, существует два варианта задания / чтения свойств: сокращенная и полная. У каждой есть свой плюс и свой минус. Сокращенная короче; полная - устойчивее.
Во-вторых, обновление большинства примитивов (спасибо Евгению Елпанову) при работе через ActiveX происходит после выхода из функции, в которой выполнялось изменение свойства примитива. То есть если написать код вида:
1 2 3 4 5 6 7 8 9 10 11 12 13 | (defun test (/ ent) (setq ent (vlax-ename->vla-object (car (entsel)))) (test1) (test2) ) ;_ end of defun (defun test1 () (vla-put-color ent 1) ) ;_ end of defun (defun test2 () (vla-put-lineweight ent aclnwt211) ) ;_ end of defun |
то обновление примитива будет выполнено 3 раза: первый раз при выходе из test1, второй - при выходе из test2, третий - при выходе из test. При работе, например, с большими и насыщенными блоками, бездумное использование подобного подхода породит приличные проблемы с быстродействием готового кода. Особняком здесь стоят таблицы AutoCAD, но разговор о них впереди.
При "разборе" ename-представлений мы рассматривали изменений нормали объекта (группа 210). В vla-представлении это Normal. Проверим свойство Normal у пока неизмененного объекта (проверку будем выполнять в сокращенной и в полной форме):
1 2 3 4 5 6 | _$ (vla-get-normal ent) #<variant 8197 ...> _$ (vlax-get-property ent 'normal) #<variant 8197 ...> _$ (vlax-get-property ent "normal") #<variant 8197 ...> |
И списком здесь, казалось бы, и не пахнет.
Ну, на самом деле не совсем так. Точнее, совсем не так. Это просто ActiveX-представление списка (так называемый variant). Его можно преобразовать в обычный список:
1 2 | _$ (vlax-safearray->list (vlax-variant-value (vlax-get-property ent 'normal))) (0.0 -1.0 0.0) |
Но нам-то надо наоборот: готовый список (точнее, трехмерные координаты точки) преобразовать во что-то "этакое".
Именно для списков из 3 чисел Autodesk внедрила функцию (vlax-3d-point). Вот ее-то и используем:
1 2 | _$ (vlax-put-property ent 'normal (vlax-3d-point '(-1. 0. 0.))) nil |
И все бы хорошо, да вот незадача - координаты центра окружности в мировой системе координат при изменении через entmod меняются, а при vlax-put-property - нет. И это только потому, что для окружности в ename=представлении в группе 10 хранятся координаты в системе координат объекта (OCS). Мы же не трогали группу 10? Не трогали. Вот и получаем "ускакавший" примитив.
И если бы это был один примитив с подобным поведением! Так ведь нет - для примитива POINT (точка) координаты (кстати, тоже группа 10) хранятся в мировой системе координат; для LINE (отрезки) - тоже мировая; для TEXT (текста) - в OCS и т.д.
Поэтому при работе через ename приходится постоянно отслеживать - какой примитив и как меняется.
Пока это была только графика. Дальше попробую рассмотреть вопросы создания и модификации неграфических данных.