Особенности vla-функций и их применения, часть 2
В предыдущей части уже были рассмотрены некоторые особенности работы vla-функций применительно к графической составляющей dwg-файла. Но были рассмотрены далеко не все вопросы. В частности, не было освещено “создание примитива через ActiveX без явного указания необязательных настроек”. И никак не был затронут вопрос о модификации созданного примитива (точнее, удобства и очевидности изменения).
Начнем, пожалуй, с “настроек по умолчанию”. Если в DXF Reference напрямую указываются необязательные параметры (то есть принимаемые текущими), то при работе через ActiveX ситуация иная: указываются только необходимые для создания примитива параметры. Все остальное необходимо задавать отдельными вызовами соответствующих функций. К таким параметрам можно отнести, например, цвет примитива (принимается текущий); тип линии примитива; слой примитива и т.п.
Создадим и активируем пользовательскую систему координат, ось Z которой непараллельна мировой и запустим следующие коды:
(10 100. 10. 20.)
(40 . 10.)
)
) ;_ end of entmakex
и
(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-представление примитива:
<Entity name: -401519e8>
Преобразуем его в список:
((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:
(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))
Получилось? Получилось (еще бы!). Теперь обновляем примитив:
(assoc 210 (entget ent))
(entget ent)
) ;_ end of subst
) ;_ end of entmod
; <...>
_$ (entupd ent)
Активируем AutoCAD и наблюдаем за изменениями примитива. Казалось бы, достаточно просто. Но не без некоторых подводных камней.
Например, в примитиве могут повторяться ключи точечных пар (пример можно посмотреть чуть выше – в частности, это группы 100), и использованный assoc вернет “не то”. Можете попробовать, используя представленный механизм, заменить координаты второй (или n-дцатой) вершины обычной полилинии. Кроме того, некоторые точечные пары могут вообще отсутствовать при создании примитива (например, пара 370 – вес линии; или группа 3 в многосточном тексте), и в таком случае приходится ее принудительно добавлять. Внесение же изменений, например, в трехмерную полилинию для неподготовленного человека сродни шаманству.
Теперь возьмемся за изменение vla-представления.
Отменим только что сделанные изменения и вернемся в VLIDE:
#<VLA-OBJECT IAcadCircle 01f43c0c>
Результат немногим более информативен, чем показанный ранее (entget (entlast)). Посмотрим свойства и методы vla-примитива:
; 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 происходит после выхода из функции, в которой выполнялось изменение свойства примитива. То есть если написать код вида:
(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 у пока неизмененного объекта (проверку будем выполнять в сокращенной и в полной форме):
#<variant 8197 ...>
_$ (vlax-get-property ent 'normal)
#<variant 8197 ...>
_$ (vlax-get-property ent "normal")
#<variant 8197 ...>
И списком здесь, казалось бы, и не пахнет.
Ну, на самом деле не совсем так. Точнее, совсем не так. Это просто ActiveX-представление списка (так называемый variant). Его можно преобразовать в обычный список:
(0.0 -1.0 0.0)
Но нам-то надо наоборот: готовый список (точнее, трехмерные координаты точки) преобразовать во что-то “этакое”.
Именно для списков из 3 чисел Autodesk внедрила функцию (vlax-3d-point). Вот ее-то и используем:
nil
И все бы хорошо, да вот незадача – координаты центра окружности в мировой системе координат при изменении через entmod меняются, а при vlax-put-property – нет. И это только потому, что для окружности в ename=представлении в группе 10 хранятся координаты в системе координат объекта (OCS). Мы же не трогали группу 10? Не трогали. Вот и получаем “ускакавший” примитив.
И если бы это был один примитив с подобным поведением! Так ведь нет – для примитива POINT (точка) координаты (кстати, тоже группа 10) хранятся в мировой системе координат; для LINE (отрезки) – тоже мировая; для TEXT (текста) – в OCS и т.д.
Поэтому при работе через ename приходится постоянно отслеживать – какой примитив и как меняется.
Пока это была только графика. Дальше попробую рассмотреть вопросы создания и модификации неграфических данных.
Материалы для проектирования, работы и разработки (и не только в AutoCAD)