Тема: Внутренний и внешний контуры ...

Кто подскажет код для определения: находится ли точка или контур внутри другого замкнутого контура или снаружи ? Возможный алгаритм примерно ясен: луч , проведенный из точки внутри контура, пересекает контур не четное число раз. Но выразить  ето в виде функции что-то но получается ...

Re: Внутренний и внешний контуры ...

Может надо воспользоваться не лучом, а двумя прямыми - вертекалью и горизонталью, проведенными через эту точку. Если точек пересечения не 4, то точка не контуре.
А вот с контуром сложнее - есть еще случай при котором они будут пересекаться, будут параллельны или перпендикулярны друг другу - эти варианты тоже нельзя списывать, если действительно надо писать "чистую" программу.

Re: Внутренний и внешний контуры ...

Все что только было сказано относится к простым областям. А вот если ваша внешняя область будет иметь сложный профиль, пример - почвенный участок, то эти алгоритмы работать не будут, так-как будут варианты когда точка лежит за контуром, а точки пересечения будут четными.

Re: Внутренний и внешний контуры ...

Прошу извинение. Сначало ответил а затем внимательно прочитал вопрос. Мой ответ неправильный(не понял тему)

Re: Внутренний и внешний контуры ...

Алгоритм, рассказанный Valeri, будет работать абсолютно для любого контура любой сложности. Т.е. из любой точки проводим бесконечный луч, если он имеет четное количество персечений с нужным контуром, то данная точка - вне контура, если не четное - внутри. С одной ремаркой: касание считать или за 0, или за 2 пересечения.
А вот насчет функции... Зависит от того, что представляет собой контур: полилиния, регион, просто набор кривых или что-то еще. Поделится готовой функцией, наверно, сможет только тот, кто уже когда то делал это для себя, и если он не жадный. Попробуй зайти на http://book.by.ru/cgi-bin/book.cgi?book … 1026399239 (там эта тема поднималась) и спроси именно у тех людей, может помогут.

Re: Внутренний и внешний контуры ...

О, пока искал ссылку, Сергей уж сам исправился.

Re: Внутренний и внешний контуры ...

Есть еще возможное решение для точки. Через ssget создать набор, затем, выролнить команду (command "_-boundary" "_A" "_I" "_N" "" "" tk "") и повторить набор. Если число примитивов в  наборе увеличился - значит точка внутри другого замкнутого контура.
Но как быть с контуром, каторый создан отрезками, дугами или
полилинией?

Re: Внутренний и внешний контуры ...

(defun ic_BoundBox (lst /)
   ;; возвращает список из координат
   ;; углов прямоугольника, внутри которого
   ;; полностью помещаются точки из списка LST
   ;; (правый_верхний_угол левый_нижний_угол)
   (list
     (list
       (apply 'max (mapcar 'car lst))
       (apply 'max (mapcar 'cadr lst))
                    ; (apply 'max (mapcar 'caddr
                    ; lst))
     ) ;_ end of list
     (list
       (apply 'min (mapcar 'car lst))
       (apply 'min (mapcar 'cadr lst))
                    ; (apply 'min (mapcar 'caddr
                    ; lst))
     ) ;_ end of list
   ) ;_ end of list
) ;_ end of defun

(defun ic_chetn    (x)
   ;;T, если X четное
   (/= (rem x 2) 0)
) ;_ end of defun



(defun ic_poly_inters (p1 p2 lst / len x a b n)
   ;; Параметры - две точки p1 и p2 и список точек lst.
   ;; возвращает  T
   ;; списка или NIL


   ;;первая вершина
   (setq n 0)
   (setq x 0)
   (setq len (length lst))
   (repeat len
     (setq a (nth n lst))
     (setq n (1+ n))
     (if    (< n len)
       (setq b (nth n lst))
       (setq b (nth 0 lst))
     ) ;_ end of if
     ;; следующая вершина
     ;;    (grdraw p1 p2 1)
     ;;   (grdraw a b 3)
     (if    (inters p1 p2 a b)
       ;; если нашли пересечение
       (setq x (1+ x))
     ) ;_ end of if
     ;; первая точка перемещается на вторую

   ) ;_ end of repeat
   (ic_chetn x)
) ;_ end of defun

(defun ic_ptInKontur (pt lst / p2 p1)
   ;;возвращает признак нахождения точки PT
   ;; внутри контура, образованного списком точек lst
   ;; если точка внутри или на границе - возвращает T
   ;; иначе NIL
   (setq p1 (car (ic_BoundBox lst)))
   ;; правая верхняя точка габарита
   (setq p2 (list (+ 10.0 (car p1)) (+ 10.0 (cadr p1))))
   (ic_poly_inters pt p2 lst)
) ;_ end of defu

Re: Внутренний и внешний контуры ...

У ShaggyDoc типичная ошибка:
(setq p2 (list (+ 10.0 (car p1)) (+ 10.0 (cadr p1))))
Любая точка pt, расположенная таким образом, что отрезок pt-p2 проходит через любую из вершин контура, выдаст nil. Например, для квадрата - это любая точка на диагонали из левого нижнего к правому верхнему углу + все вершины. Для ACAD - это примерно то, что я говорил о касании. Но это можно вылечить дополнительными функциями. А вот то, что это только для контура, состоящего из прямых сложнее.

Re: Внутренний и внешний контуры ...

> leha
(12.11.2002 в 15:31:56)
Согласен. Это НЕ универсальный пример, взят из конкретной задачи, где он работает с требуемой "правдивостью". Было быстро надо, и "шибко быстро делали".

Что касается проверки нахождения точки в произвольном контуре, это алгоритмически сложная задача. Сложная для написания на конкретном языке с его методами. Математики легко оперируют понятиями типа "если точка принадлежит к множеству точек, находящихся в контуре, то и она находится в контуре". А вопрос "как это определить", считают недостойным своей науки. Ремесло, мол.

Так что я только даю заготовку для совершенствования. Дерзайте!

Re: Внутренний и внешний контуры ...

Дополнение к варианту ShaggyDoc:

;   Функция, определяющая принадлежит ли точка pt отрезку (pt1;pt2).             
;   Определение происходит с заданной точностью tolerance.                       
;   Если принадлежит, возвращает t, если нет - nil.                               
;   Если совпадает с точкой pt1, возвращает 1, если с pt2 - 2,                   
;     если pt, pt1 и pt2 - одна и таже точка - возвращает 3                       
(defun pnt_on_segment (pt pt1 pt2 tolerance / xp yp x1 y1 x2 y2 a1 a2 )
   (cond
     ((equal pt1 pt2)
      (if (<= (distance pt pt1) tolerance)
        3
        nil
      );if
     )
     ((<= (distance pt pt1) tolerance) 1)
     ((<= (distance pt pt2) tolerance) 2)
     (progn
       (setq xp (car pt))
       (setq yp (cadr pt))
       (setq x1 (car pt1))
       (setq y1 (cadr pt1))
       (setq x2 (car pt2))
       (setq y2 (cadr pt2))
       (cond
    ((equal x1 x2 tolerance)  ; отрезок параллелен оси OY
       (if (and (<= (abs (- xp x1)) tolerance)
            (equal (abs (- y2 y1)) (+ (abs (- y2 yp)) (abs (- yp y1))) tolerance)
           );and
         t
         nil
       );if
    )
    ((equal y1 y2 tolerance)  ; отрезок параллелен оси OX
       (if (and (<= (abs (- yp y1)) tolerance)
            (equal (abs (- x2 x1)) (+ (abs (- x2 xp)) (abs (- xp x1))) tolerance)
           );and
         t
         nil
       );if
    )
    (progn
           (setq a1 (/ (- x1 xp) (- xp x2)))
           (setq a2 (/ (- y1 yp) (- yp y2)))
           (if (and (<= (abs (- a1 a2)) tolerance) (> a1 0))
        t
        nil
           );if
         );progn
       );cond
     );progn
   );cond
);defun

(defun ic_poly_inters (p1 p2 lst / len x a b n erronline ckl)
   ;; Параметры - две точки p1 и p2 и список точек lst.
   ;; возвращает T
   ;; списка или NIL или 1
   ;;первая вершина
   (setq n 0)
   (setq x 0)
   (setq len (length lst))
   ; Проверка, чтоб точка не лежала на контуре
   (setq ckl t erronline nil)
   (while ckl
     (setq a (nth n lst))
     (setq n (1+ n))
     (if (< n len)
       (setq b (nth n lst))
       (setq b (nth 0 lst) ckl nil)
     ) ;_ end of if
     (if (pnt_on_segment p1 a b 0.001)
       (setq ckl nil erronline t)
     );if
   );while
   (if erronline
     1
     (progn
       (setq n 0)
       (repeat len
         (setq a (nth n lst))
         (setq n (1+ n))
         (if (< n len)
           (setq b (nth n lst))
           (setq b (nth 0 lst))
         ) ;_ end of if
         ;; следующая вершина
         ;; (grdraw p1 p2 1)
         ;; (grdraw a b 3)
         (if (and (inters p1 p2 a b)
             (not (pnt_on_segment a p1 p2 0.001))
        );and
           ;; если нашли пересечение и не проходит через вершину
           (setq x (1+ x))
         ) ;_ end of if
       ;; первая точка перемещается на вторую

       ) ;_ end of repeat
       (ic_chetn x)
     );progn
   );if
) ;_ end of defun


(defun ic_ptInKontur (pt lst / p2 p1)
   ;;возвращает признак нахождения точки PT
   ;; внутри контура, образованного списком точек lst
   ;; если точка внутри - возвращает T
   ;; если точка на границе - возвращает 1
   ;; иначе NIL
   (setq p1 (car (ic_BoundBox lst)))
   ;; правая верхняя точка габарита
   (setq p2 (list (+ 10.0 (car p1)) (+ 10.0 (cadr p1))))
   (ic_poly_inters pt p2 lst)
) ;_ end of defu

Re: Внутренний и внешний контуры ...

Вариант, основанный на идее Valeri. Чисто AutoCAD-овское решение, далекое от математики. Есть некоторые ограничения:
1. Набор объектов-границ должен быть таким, чтобы убирание любого из граничных объектов приводило бы к разрыву контура.
2. В набор могут входить любые объекты, кроме точек и блоков (можно добавить и исключение линий нулевой длины).
3. Если есть уверенность, что точка не лежит на одной из границ, то границы могут и пересекать друг друга и выходить за пределы контура, лишь бы выполнялся пункт 1.
4. "Блымание" экрана, и если это нужно выполнять в цикле, то не очень приятно
5. Вариант универсальный для любых объектов, но если контур состоит из прямых, предложение ShaggyDoc лучше и быстрее.

;*********************************************************************************
;   Функция возвращает список ((список_имен) (список_уровней)) в данном чертеже. 
;   Если переданный параметр t - имена в нижнем регистре, если nil - в верхнем.   
;*********************************************************************************
(defun get_all_layer (param / lst_lr lst_lrnm lrobj)
   (setq lst_lr '() lst_lrnm '())
   (setq lrobj (tblnext "layer" t))
   (while lrobj
     (setq lst_lrnm (cons (strcase (cdr (assoc 2 lrobj)) param) lst_lrnm))
     (setq lst_lr (cons lrobj lst_lr))
     (setq lrobj (tblnext "layer"))
   );while
   (list lst_lrnm lst_lr)
);defun

;*********************************************************************************
;   Функция возвращает имя уровня, которого нет в чертеже.                       
;   Передается исходное имя и список всех имен.                                   
;*********************************************************************************
(defun get_free_layer_name (name lst_lrnm / nmb new_name)
   (setq nmb 1 new_name name)
   (while (member (strcase new_name t) lst_lrnm)
     (setq new_name (strcat name (itoa nmb)))
     (setq nmb (1+ nmb))
   );while
   new_name
);defun

;*********************************************************************************
;   Функция гасит все уровни из списка, кроме одного.                             
;   Передается имя уровня и список уровней.                                       
;   Возвращает список уровней, которые были погашены.                             
;*********************************************************************************
(defun off_layers (name lst_lr / lr_off current_lr status lstobj lrnm)
   (command "_.point" '(0 0))
   (setq current_lr (cdr (assoc 8 (entget (entlast)))))
   (command "_erase" (entlast) "")
   (setq lr_off '())
   (while lst_lr
     (setq lstobj (car lst_lr))
     (setq status (cdr (assoc 62 lstobj)))
     (setq lrnm (cdr (assoc 2 lstobj)))
     (if (and (>= status 0) (/= (strcase name t) (strcase lrnm t)))
       (progn
    (setq lr_off (cons lrnm lr_off))
    (if (= (strcase lrnm t) (strcase current_lr t))
      (command "_.layer" "_off" lrnm "_yes" "")
      (command "_.layer" "_off" lrnm "")
    );if
       );progn
     );if
     (setq lst_lr (cdr lst_lr))
   );while
   (setq lst_lr lst_lr)
   lr_off
);defun

;*********************************************************************************
;   Функция отображает все уровни из списка.                                     
;*********************************************************************************
(defun on_layers (lr_off / )
   (while lr_off
     (command "_.layer" "_on" (car lr_off) "")
     (setq lr_off (cdr lr_off))
   );while
);defun

;*********************************************************************************
;   Функция проверяет, станет ли контур разомкнутым, если убрать любую из границ.
;   Возвращает t - если станет, иначе nil                                         
;*********************************************************************************
(defun test_bound_objects (lst_copy pt / answer ckl obj obj_last obj2 lstmus)
   (setq answer t)
   (setq ckl t)
   (while ckl
     (setq obj (car lst_copy))
     (entdel obj)
     (setq obj_last (entlast))
     (command "_.boundary" "_a" "_o" "_r" "" pt "")
     (setq obj2 (entlast))
     (setq lstmus '())
     (while (not (equal obj2 obj_last))
       (setq lstmus (cons obj2 lstmus))
       (entdel obj2)
       (setq obj2 (entlast))
     );while
     (if lstmus
       (progn
    (command "_.erase" lstmus "")
         (setq answer nil ckl nil)
       );progn
       (progn
         (setq lst_copy (cdr lst_copy))
         (if (not lst_copy)
           (setq ckl nil)
      (entdel obj)
         );if
       );progn
     );if
   );while
   answer
);defun

;*********************************************************************************
;   Функция возвращает t, если точка pt - внутри контура,                         
;   ограниченного объектами ss_obj, 1 - если точка на границе, nil - если снаружи
;*********************************************************************************
(defun inside_point (ss_obj pt / lst_objects nmb obj lstmus lst_lrnm lst_lr lrnm lr_off old_osmode
                         lstobj lst_copy obj_last obj2 answer ss_del ss_pnt old_pickbox)
   (setq nmb 0 lst_objects '())
   (repeat (sslength ss_obj)
     (setq obj (ssname ss_obj nmb))
     (setq lstobj (entget obj))
     (if (and (/= (cdr (assoc 0 lstobj)) "INSERT")
         (/= (cdr (assoc 0 lstobj)) "POINT")
    );and
       (setq lst_objects (cons obj lst_objects))
     );if
     (setq nmb (1+ nmb))
   );repeat
   (if lst_objects
     (progn
       (setq lstmus (get_all_layer t))
       (setq lst_lrnm (car lstmus))
       (setq lst_lr (cadr lstmus))
       (setq lrnm (get_free_layer_name "test" lst_lrnm))
       (command "_.layer" "n" lrnm "")
       (setq old_osmode (getvar "OSMODE"))
       (setvar "OSMODE" 0)
       (setq lst_copy '())
       (repeat (length lst_objects)
         (command "_.copy" (car lst_objects) "" '(0 0) '(0 0))
    (setq obj (entlast))
    (setq lstobj (entget obj))
    (setq lstobj (subst (cons 8 lrnm) (assoc 8 lstobj) lstobj))
    (entmod lstobj)
    (setq lst_copy (cons obj lst_copy))
    (setq lst_objects (cdr lst_objects))
       );repeat
       (setq lr_off (off_layers lrnm lst_lr))
       (command "_.zoom" "_e")
       (setq old_pickbox (getvar "PICKBOX"))
       (setvar "PICKBOX" 0)
       (setq ss_pnt (ssget pt))
       (if ss_pnt
    (setq answer 1)
    (progn
           (setq obj_last (entlast))
           (command "_.boundary" "_a" "_o" "_r" "" pt "")
           (setq obj2 (entlast))
           (setq lstmus '())
           (while (not (equal obj2 obj_last))
        (setq lstmus (cons obj2 lstmus))
        (entdel obj2)
        (setq obj2 (entlast))
           );while
           (if (/= (length lstmus) 1)
        (setq answer nil)
        (progn
          (command "_.erase" (car lstmus) "")
          (setq answer (test_bound_objects lst_copy pt))
        );progn
           );if
         );progn
       );if
       (command "_.zoom" "_p")
       (setvar "PICKBOX" old_pickbox)
       (setvar "OSMODE" old_osmode)
       (setq ss_del (ssget "_X" (list (cons 8 lrnm))))
       (if ss_del
    (command "_.erase" ss_del "")
       );if
       (on_layers lr_off)
       (command "_.purge" "_la" lrnm "_n")
       answer
     );progn
     nil
   );if
);defun

(defun c:test ( / ss_obj pt answer)
   (prompt "\nВыберите объекты, ограничивающие контур:")
   (setq ss_obj (ssget))
   (if ss_obj
     (progn
       (setq pt (getpoint "\nУкажите точку:"))
       (if pt
    (progn
      (setq answer (inside_point ss_obj pt))
      (cond
        ((= answer t)
         (alert "Точка внутри!")
        )
        ((= answer 1)
         (alert "Точка на границе!")
        )
        ((= answer nil)
         (alert "Точка снаружи или выбраны некорректные границы!")
        )
      );cond
    );progn
       );if
     );progn
   );if
   (princ)
);defun

Re: Внутренний и внешний контуры ...

Точка +контур.
1. Для начала уберем весь "мусор" и оставим только контур.
2. Если при каком-то положении точки можно образовать регион (hpbound=0), значит точка внутри контура:
(command "_boundary" inter_point "")             
  Контур + контур.
1. Убираем "мусор" и оставляем два контура.
2. Представляем исследуемый контур как множество точек
3. Повторяем исследование п.2 точка-контур.
4. Если каждый раз удалось образовать новый регион, значит исследуемый контур - внутри, если нет, то исследуемый контур или вне другого контура или имеет с ним пересечения.

Re: Внутренний и внешний контуры ...

> shura-alex
- (Контур + контур)
А если исследуемый контур - например, полилиния, вершины которой представляют прямоугольник, но одна из сторон - полукруг. И этот контур, расположен так, что все вершины внутри, а часть дуги вылазит.

Re: Внутренний и внешний контуры ...

> shura-alex
- (Контур + контур)
Лучше, наверно, сначала проверить пересечение двух контуров, и если пересечения нет, то достаточно проверить всего одну точку.

Re: Внутренний и внешний контуры ...

Если из примитивов, (из которых состоит исследуемый контур), можно построить boundary (не регион, а область), проблем нет. Создавайте область и исследуйте, хотя бы по схеме:(vlax-curve-getpointatdist vla-obj dist).
Сложнее с эллипсами и сплайнами. По-видимому, их все-таки надо заменять на доступные для области полилинии.

Re: Внутренний и внешний контуры ...

На Ваше предыдущее.
Как вам будет удобнее. Проще всего определить пересекаются контуры или нет можно сравнением получаемых площадей.
Определите площадь исследуемого контура.
Возьмите любую точку внутри этого контура и создайте регион.
Если площадь созданного региона отличается от площади контура, то было пересечение, если нет - то не было.

Re: Внутренний и внешний контуры ...

Да, но я имел ввиду несколько другое, должно работать чуть быстрее:
1. Еще до убирания "мусора" создать два региона с помощью (command "_.region"). Не требует внутренней точки, но только потом надо будет восстановить объекты, по которым регион был создан.
2. Определить пересечение или с помощью того, что Вы сами предлагали, или с помощью функции (Intersect): https://www.caduser.ru/forum/topic3692.html
3. И уж если пересечения нет, тогда убираем "мусор" и проверяем всего одну точку.

И можно будет разделить случаи: внутри, снаружи, пересекает. Это все-таки разные ситуации. И даже четвертый случай - касание: (Intersect) выдаст одну точку. А площади двух исходных контуров можно сравнить, чтобы вдруг исследуемый контур не оказался таким, что внутри него полностью умещается другой.

Re: Внутренний и внешний контуры ...

Значится так.
1. Убирание мусора можно делать сразу копированием исследуемых объектов на подопытный слой и отключением других.
Восстанавливать объекты при этом нет необходимости.
2. Нет необходимости использовать также функцию интерсект, если сравнить площади.
3. На подопытном слое из исследуемого и "окружающего его" контура делаем регионы (только набором и, ест-но каждый отдельно). Определяем площади полученных регионов.
4. Делаем им intersect.
5. Если площадь оставщегося региона равна разнице - значит было пересечение. Если площадь оставшегося региона равна исследуемому - внутри. Если ничего не осталось - снаружи.
6. Убираем оставшиеся объекты с подопытного слоя и сам слой.
А причем здесь касание? Даже если оно имеется, как это влияет при ответе на вопрос контур внутри или снаружи?

Re: Внутренний и внешний контуры ...

Под "восстановлением объектов" я и имел ввиду работать с их копиями (см. здесть же (13.11.2002 в 10:43:07) ). И мусор убирать точно также. Только так, как Вы предложили в последний раз, отключать слои вообще не надо. А мне именно это больше всего не нравилось. Касание действительно не причем, просто раз уж само по себе получается его определить (без дополнительных усилий), то вдруг когда-нибудь в будущем и пригодится.

Re: Внутренний и внешний контуры ...

Переносить контуры я хотел, чтобы не восстанавливать рисунок, хотя это не проблема. А в результате получается простейший алгоритм: делаем два набора, затем из них два региона, применяем intersect, далее анализ и восстановление.

Re: Внутренний и внешний контуры ...

Полностью согласен, и, кстати, насчет (Точка +контур). Если вокруг точки создать окружность малого радиуса, то эта задача сводится к (Контур + контур). А в случае пересечения, можно считать, что точка находится на границе с точностью, равной радиусу окружности.

Re: Внутренний и внешний контуры ...

Два небольших замечания.
1. Площади сплайнов (и их частей) при преобразовании в регионы будут немного отличаться от оригиналов - на доли процентов. Как это может повлиять на результаты - надо проверять.
2. Если "внутренний-исследуемый" контур оказался снаружи "внешнего" (такой случай тоже может иметь место),то после команды intersect останется только "внутренний-исследуемый". Поэтому во избежание непредвиденной ошибки надо это предусмотреть. То есть, если площадь оставшегося региона равна внешнему - тогда внешний находится внутри исследуемого.
Примерно и очень грубо это может быть записано так:
(setq nab1 (ssget Фильтр)); внешний
(setq nab2(ssget Фильтр)); внутренний
  (command "region"  nab1  "");
(setq reg1 (vlax-ename->vla-object (setq r1(entlast))) s1 (vlax-get-property reg1 'Area))
(command "region"  nab2  "");
(setq reg2 (vlax-ename->vla-object (setq r2(entlast))) s2 (vlax-get-property reg2 'Area))
(command "_intersect" r1 r2 "")
  (if(or(not(eq r1 (entlast))) (not(eq r1 (entlast))))
     (princ "\nКонтур снаружи")
     (progn
(if (eq(vlax-get-property(vlax-ename->vla-object (entlast))'Area)s2)
   (princ "\nКонтур внутри")
   (progn
   (if(eq(vlax-get-property(vlax-ename->vla-object (entlast))'Area)s1)
    (princ "\nВнутренний контур оказался снаружи")
    (princ "\nКонтуры пересекаются")
   )))))