Тема: LISP. LIB. Получение габаритов для списка объектов.

;|
;**************** lst-getboundingbox.lsp *************
;   Библиотечная функция
;   определения габаритного контейнера
;   для списка VLA объектов
;   Автор  Евгений Елпанов.
;*****************************************************
;   Аргумент lst - список VLA объектов
;   пример получения списка с использованием (ssget) :
(if (setq sset (ssget))
 (setq lst
       (mapcar
        (function vlax-ename->vla-object)
        (vl-remove-if
         (function listp)
         (mapcar (function cadr) (ssnamex sset))
        ) ;_ vl-remove-if
       ) ;_  mapcar
 ) ;_  setq
)
;   Пример вызова:
 (lst-getboundingbox lst)
;   Возвращает список из двух 3d точек
;   '((левая нижняя) (правая верхняя))
|;
(defun lst-getboundingbox (lst / maxp minp)
 (vl-load-com)
 (if (and lst (listp lst))
  (apply
   (function
    (lambda (a1 a2 a3 a4 a5 a6)
     (list
      (list
       (apply (function min) a1)
       (apply (function min) a2)
       (apply (function min) a3)
      ) ;_  list
      (list
       (apply (function max) a4)
       (apply (function max) a5)
       (apply (function max) a6)
      ) ;_  list
     ) ;_  list
    ) ;_  lambda
   ) ;_  function
   (apply
    (function mapcar)
    (cons
     'list
     (mapcar
      (function
       (lambda (x)
        (vla-getboundingbox x 'minp 'maxp)
        (append
         (vlax-safearray->list minp)
         (vlax-safearray->list maxp)
        ) ;_  list
       ) ;_  lambda
      ) ;_  function
      lst
     ) ;_  mapcar
    ) ;_  cons
   ) ;_  apply
  ) ;_  apply
 ) ;_  if
) ;_  defun

Re: LISP. LIB. Получение габаритов для списка объектов.

Кстати, если загруженны экспрессы, можно короче...

(defun lst-getboundingbox (lst / maxp minp)
 (vl-load-com)
 (if (and lst (listp lst))
  (maxminpnt
   (apply
    (function append)
    (apply
     (function mapcar)
     (cons
      'list
      (mapcar
       (function
        (lambda (x)
         (vla-getboundingbox x 'minp 'maxp)
         (list
          (vlax-safearray->list minp)
          (vlax-safearray->list maxp)
         ) ;_  list
        ) ;_  lambda
       ) ;_  function
       lst
      ) ;_  mapcar
     ) ;_  cons
    ) ;_  apply
   ) ;_  apply
  ) ;_  maxminpnt
 ) ;_  if
) ;_  defun

Re: LISP. LIB. Получение габаритов для списка объектов.

Все прекрасно, но если в набор попадают объекты "XLINE" или "RAY" функция "слетает".

Re: LISP. LIB. Получение габаритов для списка объектов.

> Alexsandr13
Спасибо за тестирование!
Выкладываю новую версию - исправил ошибку и слегка переделал...

(defun lst-getboundingbox (lst)
  (vl-load-com)
  (if (and lst (listp lst))
    ((lambda (x)
       (list
         (apply
           (function mapcar)
           (cons (function min) (mapcar (function car) x))
         ) ;_  apply
         (apply
           (function mapcar)
           (cons (function max) (mapcar (function cadr) x))
         ) ;_  apply
       ) ;_  list
     ) ;_  lambda
      (mapcar
        (function
          (lambda (x / minp maxp)
            (vla-getboundingbox x 'minp 'maxp)
            (list
              (vlax-safearray->list minp)
              (vlax-safearray->list maxp)
            ) ;_  list
          ) ;_  lambda
        ) ;_  function
        (vl-remove-if
          (function
            (lambda (x)
              (or
                (not (eq (type x) 'VLA-OBJECT))
                (eq (vla-get-ObjectName x) "AcDbRay")
                (eq (vla-get-ObjectName x) "AcDbXline")
              ) ;_  or
            ) ;_  lambda
          ) ;_  function
          lst
        ) ;_  vl-remove-if
      ) ;_  mapcar
    )
  ) ;_  if
)

Re: LISP. LIB. Получение габаритов для списка объектов.

У меня есть аналогичная функция
Нет смысла анализировать типы примитивов для которых boundingbox не вычисляется. Мало ли что пападет в набор.
Просто поставьте vl-catch-all-apply и проверяйте на ошибку.

Re: LISP. LIB. Получение габаритов для списка объектов.

> BOZ
По скорости будет одинаково, если не ошибаюсь...

Re: LISP. LIB. Получение габаритов для списка объектов.

Думаю, что и по скорости будет быстрее. Все таки, убирается целый цикл, начиная с (vl-remove-if (function ... .
Но главное не в скорости. Скрость такое дело, которое можно совершенствовать долго и нудно, но при этом получать только моральное удовлетвоние.
Просто существуют объекты, о которых вы не предполагаете, для которых getboundingbox возвращает ошибку. Это могут быть и прокси. Могу ошибаться, но насколько я помню при работе с визуализацией Автокад создает на слое ASHADE блоки для которых AVE_GLOBAL и другие AV_*, RM_SDB для которых get_boundingbox вылетает.
Так что проверка на ошибку с помощью vl-catch-all-apply, мне кажется, лишней не окажется. Тем более и программа упростится.

Re: LISP. LIB. Получение габаритов для списка объектов.

> BOZ
Убедили!

(defun lst-getboundingbox (lst)
  (vl-load-com)
  (if (listp lst)
    ((lambda (x)
       (list
         (apply
           (function mapcar)
           (cons (function min) (mapcar (function car) x))
         ) ;_  apply
         (apply
           (function mapcar)
           (cons (function max) (mapcar (function cadr) x))
         ) ;_  apply
       ) ;_  list
     ) ;_  lambda
      (vl-remove-if (function null)
        (mapcar
          (function
            (lambda (x / minp maxp)
              (if (not (vl-catch-all-error-p
                         (vl-catch-all-apply
                           (function vla-getboundingbox)
                           (list x 'minp 'maxp)
                         ) ;_  vl-catch-all-apply
                       ) ;_  vl-catch-all-error-p
                  ) ;_  not
                (list (vlax-safearray->list minp)
                      (vlax-safearray->list maxp)
                ) ;_  list
              ) ;_  if
            ) ;_  lambda
          ) ;_  function
          lst
        ) ;_  mapcar
      ) ;_  vl-remove-if
    )
  ) ;_  if
)

Re: LISP. LIB. Получение габаритов для списка объектов.

> Евгений Елпанов
Все лучшее враг хорошего! (listp nil) - вернет Т и соответственно ... . А вообще-то тему "Где производить проверку аргументов библиотечных функций (в них самих или при вызове) и результатов работы функции? Стоит ли предусматривать вывод сообщений в случае некорректного результата обработки?" неплохо было бы обсудить на форуме.
   Интересно выслушать мнение профессианалов...

Re: LISP. LIB. Получение габаритов для списка объектов.

> aleksandr13
Я это знаю и предусмотрел!

(mapcar
   (function
...........................
   ) ;_  function
  NIL
 ) ;_  mapcar

Выдаст то, что надо...

Re: LISP. LIB. Получение габаритов для списка объектов.

> Евгений Елпанов
Ничего не понимаю! У меня идет ошибка!
(mapcar
   (function
...........................
   ) ;_  function
  NIL
) ;_  mapcar
вернет NIL.
(vl-remove-if (function null)
............................
);vl-remove-if
вернет NIL.
Ошибка идет в
(apply
  (function mapcar)
(cons (function min) (mapcar (function car) x)) - ; возвращает (MIN)
) ;_  apply
- несоответствие аргументов.
Грешным делом подумал на низкую версию AutoСАDа, но 2006 выдает тоже самое!
Извини, если надоел, но хочется разобраться.

Re: LISP. LIB. Получение габаритов для списка объектов.

> aleksandr13
Верно, есть ошибка, при пустом списке...
Исправляюсь:

(defun lst-getboundingbox (lst)
  (vl-load-com)
  (if (and lst (listp lst))
    ((lambda (x)
       (list
         (apply
           (function mapcar)
           (cons (function min) (mapcar (function car) x))
         ) ;_  apply
         (apply
           (function mapcar)
           (cons (function max) (mapcar (function cadr) x))
         ) ;_  apply
       ) ;_  list
     ) ;_  lambda
      (vl-remove-if (function null)
        (mapcar
          (function
            (lambda (x / minp maxp)
              (if (not (vl-catch-all-error-p
                         (vl-catch-all-apply
                           (function vla-getboundingbox)
                           (list x 'minp 'maxp)
                         ) ;_  vl-catch-all-apply
                       ) ;_  vl-catch-all-error-p
                  ) ;_  not
                (list (vlax-safearray->list minp)
                      (vlax-safearray->list maxp)
                ) ;_  list
              ) ;_  if
            ) ;_  lambda
          ) ;_  function
          lst
        ) ;_  mapcar
      ) ;_  vl-remove-if
    )
  ) ;_  if
)

Re: LISP. LIB. Получение габаритов для списка объектов.

> Евгений Елпанов
А что будет, если функция
(vl-remove-if (function null)
............
);vl-remove-if
вернет пустой список?!

Re: LISP. LIB. Получение габаритов для списка объектов.

> aleksandr13
Конечно ошибка smile
На самом деле это именно библиотечная функция и используется для измерения габаритного контейнера всех примитивов из специально подготовленного списка!
Я изначально был не согласен, что нужны дополнительные проверки - они должны быть в формировании списка, а эта функция используется как обычный калькулятор!
Ведь ни у кого не возникает вопросов, что при суммировании двух слов возникает ошибка о несоответствии типов...
Короче говоря - данный калькулятор может выдать габариты всех объектов заданных списком, но в списке нужны указатели на объекты, у которых  есть эти габариты и автокад умеет их измерять! Я не собирался делать программу, которая будет обрабатывать вообще любые списки...
PS. Если у вас есть необходимость в программе, которая будет на входе брать один аргумент, и если в этом аргументе окажется список с объектами, для которых возможно определить габаритный контейнер, будет его определять, в остальных случаях выдавать различные предупреждения или еще какие либо действия - опишите задачу - если задача покажется мне интересной - я ее реализую...

Re: LISP. LIB. Получение габаритов для списка объектов.

> Евгений Елпанов
У нас с Вами разный взгляд на библиотечные функции...
  Во первых библиотечные функции должны быть очень надежны. И я думаю, в этом со мной согласятся многие. Но для того чтобы они не давали сбой необходимо проверка аргументов, условий и т.д. В этом случае встает вопрос о котором я писал выше. Для себя я решил однозначно - проверка в самой функции + конвертирование однотипных аргументов к необходимому виду, для её универсальности и разумеется вывод сообщений если результат несоответствует ожиданиям. Практика показала, что даже "нормально" работающие программы начинают "ругаться" сообщениями в этом случае. А при необходимости сообщения можно и выключить, как это делается описано в общеизвестной книге.
  Библиотечная функция может вызываться много раз в некоторых случаях даже сотни раз, естественно нерационально проверять условия или конвертировать аргументы в определенный тип каждый раз. Можно правда поступить иначе написать еще одну библиотечную функцию которая будет конвертировать и проверять. Но в этом случае можно утонуть в этой лавине функций их и так набегает до сотни и этому нет конца.
    За предложение спасибо. У меня была функция выполняющая аналогичную операцию, написанная на AUTOLispe. Большая и громоздкая, но необходимая мне. К сожалению я не знаком с функциями с префиксом VBA-. Литературы (нормальной, имеется в виду учебников) на эту тему я не встречал, а с английским напряг...
  В общем я взял за ядро вышеописанную функцию и оформил то, что мне нужно.
  PS. Спасибо, если хватило терпения дочитать до конца. А несоответствие надо исправить. Один оператор IF не испортит красоту (действительно красивой) функции. Калькулятор не должен ломаться из-за того, что пользователь нажал не на ту кнопку!

Re: LISP. LIB. Получение габаритов для списка объектов.

> aleksandr13
Терпения дочитать до конца у меня хватило, скажу больше, спасибо за ответ!
Действительно, у нас совершенно различные взгляды на библиотечные функции...
Я пишу очень ресурсоемкие программы - такие передо мной стоят задачи, поэтому я стараюсь делать необходимые проверки один раз, а лучше изначально не допускать внутрь программ неправильные данные - иначе весь проект будет состоять из одних проверок.

Re: LISP. LIB. Получение габаритов для списка объектов.

> Евгений Елпанов
... стараюсь делать необходимые проверки один раз...
Но ведь проверять все равно приходится! Соответственно лучше это делать где-то за основным текстом и не особенно заботясь, что что-то упустил. К тому же мне кажется, что выполнение функций проверок не занимает много времени... . По моему личному опыту знаю, что огромное количество времени съедает создание (редактирование) примитивов (объектов) - любая вуализация. Надо будет проверить, тем более есть примеры... .
  К тому же по своему горькому опыту знаю, что лучше подождать 1...2 сек., чем перезапускать весь AutoCad, когда он наглухо виснет.

Re: LISP. LIB. Получение габаритов для списка объектов.

> aleksandr13
Вы правы, но только частично. Для библиотечной функции есть понятие "контракт" - т.е. что она получает на вход, что возвращает и (!) условия ее использовании (например, значения системых переменных AutoCAD, типы обрабатываемых примитивов, и т.д.). В данном случае Евгений не полностью описал этот "контракт".
Что касается лучше подождать 1...2 сек., то это не в случае Евгения. Он оперирует сотнями тысяч, если не миллионами полилилиний и счет идет не на дополнительные секунды, а на десятки минут и часы. Поэтому, дополнительные (ненужные) проверки, которые выполняются сотни тысяч раз очень значительно снижают производительность всей системы в целом.
Так что это всё очень индивидуально. В его случае лучше один раз "отсеять" недопустимые элементы, чем каждый раз их проверять.

Re: LISP. LIB. Получение габаритов для списка объектов.

> Александр Ривилис
Спасибо, теперь понял в чем суть дела. При таком раскладе трудно не согласиться с Евгением. Если не секрет, что за отрасль?
   Проверил время выполнения функции IF (компилированный код) на своем стареньком "железе":
(if (> x 0)
..........
);if
получил 1.2 е-6, действительно много! На скидывание значения в переменную на порядок меньше!

Re: LISP. LIB. Получение габаритов для списка объектов.

> aleksandr13
Мои задачи крутятся вокруг автоматизированного раскроя и иже с ним...

Re: LISP. LIB. Получение габаритов для списка объектов.

> Евгений Елпанов
OK. Удачи!

Re: LISP. LIB. Получение габаритов для списка объектов.

> aleksandr13
А чем занимаетесь вы? Мне тоже интересно :)

Re: LISP. LIB. Получение габаритов для списка объектов.

> Евгений Елпанов
Отрасль машиностроение. Но лично мои задачи мало связаны с проектированием. Программирование на Avtolisp - просто хобби, хотя часто это помогает в решении задач связанных с большим объемом расчетов (имеется ввиду ручной расчет) и вуализацией полученного решения. Например расчет и построение шестерни.

Re: LISP. LIB. Получение габаритов для списка объектов.

> Евгений Елпанов
Евгений стал пользоваться программой в ADT. Список для (ssget) беру '((0 . "AecWall"))
Всё определяет нормально и всё работает, но когда закрываешь файл выдаётся сообщение
INTERNAL ERROR: Attempt to access AecSheduleDataServices after shutdown
Без использования этой функции сообщение не выдаётся. В чём может быть проблема?

Re: LISP. LIB. Получение габаритов для списка объектов.

> PahRam
Попробуй после обработки vla списка объектов их релизить...
PS. Вообще то у меня нет adt и я не могу проверить...