Особенности 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 приходится постоянно отслеживать - какой примитив и как меняется.

Пока это была только графика. Дальше попробую рассмотреть вопросы создания и модификации неграфических данных.

Размещено в Код LISP, Новости · Метки: ,



Поделитесь своим мнением


Я не робот.