В предыдущем уроке мы рассмотрели LISP программу, которая в программе AutoCAD считает сумму длин выбранных отрезков: Сумма длин отрезков.
Один из недостатков этой программы заключается в том, что выбор отрезков производится по одному. А если таких отрезков на чертеже тысячи, то задача их выбора становится не лёгкой.
В этом уроке мы рассмотрим другие возможные варианты этой программы:
- Программу со свободным выбором отрезков.
- Программу суммирования все отрезков, расположенных на чертеже.
- Программу суммирования все отрезков, расположенных на определенном слое.
Для того, чтобы мы могли их тестировать. Давайте вначале, нарисуем в AutoCAD три отрезка на одном слои. Создадим второй слой с другим цветом, и на нем нарисуем четыре отрезка и окружность. См. Рис. 1.
Затем запустим Visual LISP, создадим новый файл и откроем консоль Visual LISP.
LISP программа: Свободный выбор отрезков.
Давайте напишем LISP программу, где выбор отрезков можно делать и по одному и группой при помощи рамки (или при помощи секущей рамки). В общем так, как это делается в AutoCAD.
Для того, чтобы выбирать объекты и создавать из них набор объектов мы будем использовать функцию ssget.
Следующая строка:
(setq set_obj (ssget))
Позволяет нам создать набор объектов и сохранить его в переменной set_obj.
Давайте наберем её. Дважды щелкнем после закрывающейся скобки (функция выделится) и нажмем кнопку «Загрузить выделенный фрагмент». См. Рис. 2.
Активным станет AutoCAD и на запрос в командной строке: «Выберите объекты:», мы может указывать наши объекты по одному, или слева направо при помощи рамки. См. Рис. 3.
или справа налево при помощи секущей рамки. См. Рис. 4.
После того, как вы выберите несколько объектов, они выделяться пунктиром.
В командной строке будет написано количество выбранных объектов.
А ниже запрос, предлагающий продолжить выбор: «Выберите объекты:». См. Рис. 5.
Если вы случайно выделили отрезок (объект), который вы не хотели включать в набор. Его выбор можно отменить. Для этого удерживая клавишу <Shift>, щелкните по нему. Этот отрезок будет исключен из выбора. См. Рис. 6.
После того как вы выбрали все необходимые объекты, нажмите <Enter>, чтобы завершить выбор. Программа вернет нас в Visual LISP.
В окне консоли появится имя набора. См. Рис. 2.
При помощи функции sslength определяем количество элементов в наборе:
(sslength <набор>)
<набор> — имя набора.
Запомним количество элементов в наборе в переменной n.
Добавим следующую строку:
(setq n (sslength set_obj))
Выделим ее и нажмем кнопку «Загрузить выделенный фрагмент». См. Рис. 7.
В консоли Visual LISP появится результат выполнения строки: количество элементов в наборе.
Теперь давайте создадим цикл, в котором поочередно будем выбирать элементы из нашего набора.
В общем виде наш цикл будет выглядеть так:
(while (> n 0)
<выражения выполняемые с отдельным элементом>
(setq n (— n 1))
); end while
На первом шаге n=6, в конце цикла n уменьшиться на 1.
На втором шаге n уже будет равна 5, и в конце снова уменьшиться на 1.
И так далее на седьмом шаге n будет равна 0. И из-за того, что условие (> n 0) станет не верно (ноль не больше нуля), функция while прекратит свою работу.
Теперь давайте рассмотрим выражения, которые мы будем выполнять внутри цикла.
Первое, что нам нужно сделать, это извлечь примитив из набора по его порядковому номеру при помощи функции ssname
(ssname <набор> <номер>)
<набор> — имя набора.
<номер> — номер элемента в наборе.
Следует помнить, что нумерация примитивов в наборе начинается с нуля. Значит, последний примитив в нашем наборе будет иметь номер 5. Именно с него мы и начнем выборку элементов из набора, добавив строку:
(setq name_obj (ssname set_obj (— n 1)))
На первом шаге n=6, функция ssname вернет нам имя примитива с порядковым номером 5.
На первом шаге n=5, функция ssname вернет нам имя примитива с порядковым номером 4.
И так далее …
На шестом шаге n=1, функция ssname вернет нам имя примитива с порядковым номером 0.
То есть, таким образом, мы переберем все примитивы, входящие в набор.
Далее добавляем строки уже известные нам по предыдущей программе:
Читаем список с характеристиками примитива (setq list_obj (entget name_obj))
Извлекаем из списка координаты первой точки (setq p1 (cdr (assoc 10 list_obj)))
Извлекаем из списка координаты второй точки (setq p2 (cdr (assoc 11 list_obj)))
Вычисляем длину отрезка (setq dl_otr (distance p1 p2))
Суммируем длины отрезкой (setq sum_dl (+ sum_dl dl_otr))
, предварительно перед циклом присвоив переменной sum_dl начальное значение 0.0.
(setq sum_dl 0.0)
Для подсчета количество отрезков будем использовать переменную i.
В начале перед циклом присвоим ей значение 0. (setq i 0).
И внутри цикла добавим строку, считающую количество отрезков (setq i (+ i 1))
Для печати результата добавим строку.
(princ (strcat «\nОтрезков — « (itoa i) «\nОбщая длина: — « (rtos sum_dl)))
Добавляем все выше сказанное в программу, и она приобретает следующий вид. См. Рис. 8.
Выделяем весь текст, нажимаем на кнопку «Загрузить выделенный фрагмент».
В Автокаде выбираем все семь отрезков и нажимаем <Enter>.
В консоли Visual LISP (а также в командной строке Автокад) появится результат выполнения программы.
Если при выборе, случайно в наш набор попадет какой-либо примитив, который не является отрезком (например: точка), программа выдаст ошибку.
Чтобы, этого не случилось, как и в предыдущей программе:
Вначале считываем из списка характеристик тип примитива:
(setq tip_obj (cdr (assoc 0 list_obj)))
Затем при помощи функции if ставим условие, что дальнейшие выражения выполнять, только в том случаи, если примитив имеет тип “LINE” (отрезок).
(if (= tip_obj «LINE»)
(progn
<Выражения>
);end progn
); end if
В результате наша программа будет выглядеть так. См. Рис. 9.
Теперь давайте сделаем так, чтобы наши отрезки в конце выполнения программы, были выделены.
Для этого сначала создадим пустой набор и сохраним его в переменной set_line:
(setq set_line (ssadd))
Затем внутри цикла при помощи строки
(sssetfirst nil (ssadd name_obj set_line))
Добавляем наши отрезки в набор set_line, а затем включает ручки и подсвечивает пунктиром.
В заключении преобразуем нашу программу в пользовательскую функцию defun.
Добавим строку, в которой придумаем имя функции и перечислим все временные переменные:
(defun c:SumDls (/ line_set sum_Dl n name_obj list_obj tip_obj p1 p2 dl_otr)
<наша программа>
) ; end_defun
Окончательный вариант программы см. Рис. 10.
Теперь, чтобы запустить программу, используем кнопку «Загрузить активное окно редактора». Переходим в AutoCAD, в командной строке набираем SumDls и нажимаем <Enter>.
Выделяем все отрезки и окружность. Нажимаем <Enter>. См. Рис. 11.
Мы видим, что программа сосчитала и выделила, только отрезки (включение окружности в первоначальный набор set_obj не помещала работе программы).
Также мы видим, что нужный нам ответ выводится дважды. Чтобы этого не происходило нужно добавить строку.
(princ) – которая осуществляет тихий выход и не дает Автокаду показывать значение последней переменной. См. Рис. 12.
Снова запускаем, программу.
Выделяем все отрезки и окружность. Нажимаем <Enter>. См. Рис. 13.
Теперь результат выводится, без ненужных дополнений.
Программный код:
(defun c:SumDls (/ set_obj n sum_dl i set_line name_obj list_obj tip_obj p1 p2 dl_otr) (setq set_obj (ssget)) ; Набор примитивов (setq n (sslength set_obj)) ; Кол-во элементов в наборе (setq sum_dl 0.0) ; Cумма длин отрезков = 0 (setq i 0) ; Счетчик отрезков (setq set_line (ssadd)) ; создаем пустой набор для отрезков (while (> n 0) (setq name_obj (ssname set_obj (- n 1))) (setq list_obj (entget name_obj)) (setq tip_obj (cdr (assoc 0 list_obj))) (if (= tip_obj "LINE") (progn (sssetfirst nil (ssadd name_obj set_line)) (setq p1 (cdr (assoc 10 list_obj))) (setq p2 (cdr (assoc 11 list_obj))) (setq dl_otr (distance p1 p2)) (setq sum_dl (+ sum_dl dl_otr)) (setq i (+ i 1)) ) ;end progn ) ; end if (setq n (- n 1)) ) ; end while (princ (strcat "\nОтрезков - " (itoa i) "\nОбщая длина: - " (rtos sum_dl))) (princ) ) ; end_defun
Скачать программу Sum_dls.lsp (Размер файла: 487 bytes)
LISP программа: Сумма длин отрезков на всем чертеже.
Теперь, давайте немного переделаем программу и сделаем так, чтобы она считала отрезки на всем чертеже.
Для этого мы снова будем использовать функцию ssget, но с дополнительными аргументами. В общем виде она будет, выглядит так:
(ssget [<метод>][<фильтр>])
<метод> — это текстовая строка, которая характеризует метод выбора примитивов.
<фильтр> — это сложный список со структурой, аналогичной структуре списка возвращаемой функцией entget. В этом списке описаны дополнительные признаки примитива (тип, цвет , слой и т. д.). Функция ssget оставит в наборе только те примитивы, которые удовлетворяют этим признакам.
В качестве метода мы будем использовать “_x” – который выбираем всю базу примитивов рисунка, включая объекты расположенные на замороженных слоях и вне видимой части экрана.
В качестве фильтра список (list ‘(0 . «LINE»)), в котором указываем тип примитива: отрезок.
В результате строка:
(ssget «_x» (list ‘(0 . «LINE»)))
Создаст набор из отрезков на всем чертеже. Давайте запомним этот набор в переменной set_line.
(setq set_line (ssget «_x» (list ‘(0 . «LINE»))))
Вставим эту строчку вместо (setq set_obj (ssget)) и внесем дополнительные изменения в программу. См. Рис. 14.
Из списка временных переменных удаляем set_obj
Количество элементов будем определять в наборе set_line.
(setq n (sslength set_line))
Извлекать примитив по его порядковому номеру тоже будем из набора set_line.
(setq name_obj (ssname set_line (- n 1)))
Удалим строчки, которые выделяют выбранные отрезки
(setq set_line (ssadd))
(sssetfirst nil (ssadd name_obj set_line))
В нашем новом наборе будут, только отрезки, поэтому проверку на “тип примитива” делать не нужно. Удаляем строки:
(setq tip_obj (cdr (assoc 0 list_obj)))
(if (= tip_obj «LINE»)
(prong
);end progn
); end if
В конце программы перед строкой печати добавим строку:
(sssetfirst nil set_line)
Которая выделит отрезки входящие в набор set_line.
Переименуйте имя функции defun на SumDlv и сохраните программу под другим именем.
В результате мы получим следующую программу. См. Рис. 15.
Загружаем программу, используем кнопку «Загрузить активное окно редактора». Переходим в AutoCAD, в командной строке набираем SumDlv и нажимаем <Enter>.
На чертеже выделятся все отрезки, в командной строке появится результат суммирования.
См. Рис. 16.
Программный код:
(defun c:SumDlv (/ n sum_dl i set_line name_obj list_obj tip_obj p1 p2 dl_otr)
(setq set_line (ssget "_x" (list '(0 . "LINE")))) ; Набор из линий на чертеже
(setq n (sslength set_line)) ; Кол-во элементов в наборе
(setq sum_dl 0.0) ; Cумма длин отрезков = 0
(setq i 0) ; Счетчик отрезков
(while (> n 0)
(setq name_obj (ssname set_line (- n 1)))
(setq list_obj (entget name_obj))
(setq p1 (cdr (assoc 10 list_obj)))
(setq p2 (cdr (assoc 11 list_obj)))
(setq dl_otr (distance p1 p2))
(setq sum_dl (+ sum_dl dl_otr))
(setq i (+ i 1))
(setq n (- n 1))
) ; end while
(sssetfirst nil set_line)
(princ (strcat "\nОтрезков - " (itoa i) "\nОбщая длина: - " (rtos sum_dl)))
(princ)
) ; end_defun
Скачать программу Sum_dlv.lsp (Размер файла: 440 bytes)
LISP программа: Сумма длин отрезков на выбранном слое.
Есть случаи, когда программу Sum_dlv, считающую сумму длин отрезков на всем чертеже, использовать не удобно. Особенно, если на чертеже есть замороженные слои с отрезками, которые тоже будут сосчитаны.
Поэтому, давайте немного изменим программу и сделаем, так чтобы она считала отрезки на определенном слое.
Слой мы будем выбирать при помощи указания объекта находящегося на этом слое.
При помощи функции entsel выбираем объект.
(entsel «\nВыберите объект для определения слоя:»)
Результат: (<Имя объекта: 7ec9f5f0> (1401.43 1292.2 0.0))
При помощи функции car отбрасываем координаты.
(car (entsel «\nВыберите объект для определения слоя:»)))))
Результат: <Имя объекта: 7ec9f5f0>
При помощи функции entget считываем список с характеристиками примитива.
(entget (car (entsel «\nВыберите объект для определения слоя:»)))))
Результат: ((-1 . <Имя объекта: 7ec9f5f0>) (0 . «LINE») (330 . <Имя объекта: 7ec9dcf8>) (5 . «236») (100 . «AcDbEntity») (67 . 0) (410 . «Model») (8 . «Слой1») (100 . «AcDbLine») (10 1132.85 1117.63 0.0) (11 1591.69 1412.34 0.0) (210 0.0 0.0 1.0))
При помощи функции assoc извлекаем из списка слой, на котором расположен выбранный примитив (элемент с DXF-кодом 8).
(assoc 8 (entget (car (entsel «\nВыберите объект для определения слоя:»)))))
Результат: (8 . «Слой1»)
При помощи функции setq запомним его в переменной layer
(setq layer (assoc 8 (entget (car (entsel «\nВыберите объект для определения слоя:»)))))
Добавляем переменную layer в <фильтр> функции ssget и получает набор отрезков с определенного слоя.
Программа будет выглядеть так. См. Рис. 17.
Переименуйте имя функции defun на SumSl и сохраните программу под другим именем Sum_sl. Добавьте переменную layer в список временных переменных.
Загрузите программу, Перейдите в AutoCAD, в командной строке набираем SumSl и нажимаем <Enter>.
На запрос: “Выберите объект для определения слоя:” укажите белый отрезок.
Отрезки на слое с белым цветом выделятся, в командной строке появится результат. См. Рис. 18.
Если указать объект с другого слоя (не обязательно отрезок), то результат будет другим. См. Рис. 19.
Программный код:
(defun c:SumSl (/ layer n sum_dl i set_line tip_obj p1 p2 dl_otr) (setq layer (assoc 8 (entget (car (entsel "\nВыберите объект для определения слоя:"))))) (setq set_line (ssget "_x" (list '(0 . "LINE") layer))) ; Набор из линий на слое (setq n (sslength set_line)) ; Кол-во элементов в наборе (setq sum_dl 0.0) ; Cумма длин отрезков = 0 (setq i 0) ; Счетчик отрезков (while (> n 0) (setq name_obj (ssname set_line (- n 1))) (setq list_obj (entget name_obj)) (setq p1 (cdr (assoc 10 list_obj))) (setq p2 (cdr (assoc 11 list_obj))) (setq dl_otr (distance p1 p2)) (setq sum_dl (+ sum_dl dl_otr)) (setq i (+ i 1)) (setq n (- n 1)) ); end while (sssetfirst nil set_line) (princ (strcat "\nОтрезков - " (itoa i) "\nОбщая длина: - " (rtos sum_dl))) (princ) ); end_defun
Скачать программу Sum_sl.lsp (Размер файла: 563 bytes)
После того, как программа отлажена нужно:
- Если это ещё не сделано, то добавить все локальные переменные в список временных переменных функции defun.
- Сохранить свою LISP-программу.
- Закрыть редактор Visual LISP и AutoCAD, чтобы очистить все значения переменных.
- Загрузить AutoCAD снова.
- Также можно добавить нашу программу в список автоматической загрузки и создать для нее кнопку запуска. Как это сделать рассмотрено в уроке: Простой запуск LISP программ.
И так, мы рассмотрели несколько вариантов LISP программ, которые считают количество и суммируют длины отрезков.
Пишите в комментариях:
Трудно ли было выполнить этот урок?
Где у вас возникли трудности?
Была ли для Вас полезной информация, данная в этом уроке?
На какие вопросы программирования, Вы хотели бы, увидит ответы в следующих уроках?
Я с удовольствием отвечу на ваши комментарии.
Если вы хотите получать новости с моего сайта. Оформляйте подписку.
До новых встреч.
«Автор: Михаил Орлов»
Очень любопытный топик
Вот это реал…уважуха…Респект!
Спасибо за объяснение. Все гениальное просто.
Интересно здесь у вас!
Спасибо)
После выбора объекта для определения слоя в командой строке появляется следующее сообщение: «Настройка переменной AutoCAD отвергнута: «CMDECHO» nil».
Не могу понять почему такое происходит. Программа вообще не трогает переменную «CMDECHO». Вы программу сами набирали или скачали с сайта?
Пришлите мне на почту acadprog@gmail.com файл, в котором Вы запускали программу (может дело в нем) и саму программу.
Здравствуйте!
Бывает такое, что вроде пишу внимательно все. А при работе программы все отрезки равны 0,000. А скопировав вашу программу — все нормально. При сравнении кода — все идентично.
Autocad 2015 (64)
Пришлите вашу программу на почту: acadprog@gmail.com Я найду ошибку.
Почитайте урок по отладке программ: http://acad-prog.ru/otladka-programmy/
михаил здравствуйте.
Очень понравилось медот выбора примитивов помощью SSGET и дальнейший расчет.
Есть что либо похожее для расчета площадей замкнутых полилиний.
Нашел несколько программ в сети достаточно интересных но без всяческих обьяснений. и без этих важных добавок как выбор по фильтру и выделение обьектов.
Нет. Но я думаю, такую программу не трудно написать.
Михаил, огромное спасибо и низкий Вам поклон, информация очень полезная!
Ваши примеры предусматривают выбор примитивов во время выполнения программы, а можно ли получить список уже выбранных до запуска программы примитивов? например я выбираю несколько примитивов, запускаю программу и она обрабатывает эти примитивы
Программа со свободным выбором отрезков должна работать и по Вами предложенному способу. Выбирайте отрезки, затем запускайте команду
SumDls
Программа сосчитает сумму длин предварительно выбранных отрезков.