Форум программистов, компьютерный форум, киберфорум
C# .NET
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.57/7: Рейтинг темы: голосов - 7, средняя оценка - 4.57
6 / 2 / 1
Регистрация: 15.10.2020
Сообщений: 62

Алгоритм сканирующей прямой (Бентли-Оттманна)

10.12.2022, 23:44. Показов 1492. Ответов 10

Студворк — интернет-сервис помощи студентам
Есть некая точка A, вокруг неё располагается n окружностей у каждой из которых известны координаты центра (x,y) и радиус r. Окружности могут пересекаться и быть полностью вложенными в друг друга. Нужно узнать, найдется ли отрезок, выходящий из точки A, такой, что не пересечёт ни одну из n окружностей. На схеме пример. В этом случае отрезка, удовлетворяющего условию не найти.

Как я понял здесь нужно применить алгоритм сканирующей прямой (Бентли-Оттманна), но не понимаю, как именно это сделать.
Если же есть другое решение со схожей эффективностью, тоже будет интересно посмотреть.
Миниатюры
Алгоритм сканирующей прямой (Бентли-Оттманна)  
0
Лучшие ответы (1)
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
10.12.2022, 23:44
Ответы с готовыми решениями:

Используя метод сканирующей прямой определить, сколько треугольников пересекутся, если поедут вправо
Даны координаты точек n треугольников. Используя метод сканирующей прямой нужно определить, сколько треугольников пересекутся, если поедут...

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

Горит проект по СЕО для странички Бентли
Всем привет, Задали проект, где нужно разработать СЕО стратегию с целью увеличения узнаваемости, вовлечения и трафика для вот этой...

10
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16116 / 11237 / 2887
Регистрация: 21.04.2018
Сообщений: 33,038
Записей в блоге: 2
11.12.2022, 10:31
Цитата Сообщение от SlaTH Посмотреть сообщение
Нужно узнать, найдется ли отрезок, выходящий из точки A, такой, что не пересечёт ни одну из n окружностей. На схеме пример. В этом случае отрезка, удовлетворяющего условию не найти.
Отрезок имеет конечную длину.
В данном случае, если взять длину отрезка > 2, то любой такой отрезок будет удовлетворять условию.
Может вам нужно определять для прямой или луча?
0
6 / 2 / 1
Регистрация: 15.10.2020
Сообщений: 62
11.12.2022, 12:33  [ТС]
Да, речь идёт о луче из точки А.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16116 / 11237 / 2887
Регистрация: 21.04.2018
Сообщений: 33,038
Записей в блоге: 2
11.12.2022, 14:01
Лучший ответ Сообщение было отмечено SlaTH как решение

Решение

Цитата Сообщение от SlaTH Посмотреть сообщение
Да, речь идёт о луче из точки А.
Я с графикой не работаю, и не знаю что такое алгоритм сканирующей прямой (Бентли-Оттманна).
Чисто из моих математических представлений, я бы для каждого круга определял перекрываемый им сектор и проверял остался ли не перекрытый сектор.
Определение сектора это не сложно:
1) Луч (вектор) центра сектора по точке и центру окружности;
2) Ширина сектора двойной угол прямоугольного треугольника по гипотенузе (расстояние до центра окружности) и противолежащему катету (радиус окружности).

А вот с накоплением секторов каких-то простых, предусмотренных методов в .Net не знаю.
Придётся реализовывать какой-то кастомный DoubleRange от 0 до +2*пи и какую-то коллекцию вычетов из него.
1
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16116 / 11237 / 2887
Регистрация: 21.04.2018
Сообщений: 33,038
Записей в блоге: 2
11.12.2022, 14:44
SlaTH, для затравки.
Структура точки (из личной библиотеки):
C#
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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
namespace Core2022.Plane
{
    /// <summary>Структура для точки на плоскости.</summary>
    public struct Point2D : IEquatable<Point2D>
    {
        public double X { get; set; }
        public double Y { get; set; }
 
        public Point2D(double x, double y)
        {
            X = x;
            Y = y;
        }
 
        public override bool Equals(object? obj)
        {
            return obj is Point2D d && Equals(d);
        }
 
        public bool Equals(Point2D other)
        {
            return X == other.X &&
                   Y == other.Y;
        }
 
        public override int GetHashCode()
        {
            return HashCode.Combine(X, Y);
        }
 
        public static bool operator ==(Point2D left, Point2D right)
        {
            return left.Equals(right);
        }
 
        public static bool operator !=(Point2D left, Point2D right)
        {
            return !(left == right);
        }
        public override string ToString() => $"({X}; {Y})";
        public static Point2D Parse(string text)
        {
            var split = text.Split("( ;)".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
            if (split.Length != 2)
                throw new ArgumentException("Должно быть два числа разделённых пробелом и/или точкой запятой, скобками.", nameof(text));
            return new Point2D(double.Parse(split[0]), double.Parse(split[1]));
        }
        public static bool TryParse(string text, out Point2D point)
        {
            var split = text.Split(' ', ';', StringSplitOptions.RemoveEmptyEntries);
            if (split.Length != 2 ||
                !double.TryParse(split[0], out double x) ||
                !double.TryParse(split[1], out double y))
            {
                point = new Point2D();
                return false;
            }
            point = new Point2D(x, y);
            return true;
        }
 
        public static double Distance(Point2D a, Point2D b)
        {
            double dx = b.X - a.X;
            double dy = b.Y - a.Y;
            return Math.Sqrt(dx * dx + dy * dy);
        }
        public double Distance(Point2D b)
           => Distance(this, b);
    }
}
Структура сектора (сделал для этой темы - не тестировал!):
C#
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
49
50
51
namespace Core2022.Plane
{
    /// <summary>Структура для сектора обзора.</summary>
    public struct Sector
    {
        /// <summary>Угол средней линии.</summary>
        public double MeanLine { get; set; }
 
        private double _width;
        /// <summary>Ширина угла сектора.</summary>
        public double Width
        {
            get => _width;
            set
            {
                if (value < 0)
                    throw new ArgumentOutOfRangeException(nameof(value), "Отрицательное значение не допускается.");
                _width = value;
            }
        }
 
        /// <summary>Угол начала сектора.</summary>
        public double Begin => MeanLine - 0.5 * Width;
 
        /// <summary>Угол конца сектора.</summary>
        public double End => MeanLine + 0.5 * Width;
 
        /// <summary>Создаёт сектор перекрытия окружности для точки.</summary>
        /// <param name="point">Точка, которой перекрывается сектор.</param>
        /// <param name="center">Центр окружности, перекрывающей сектор.</param>
        /// <param name="radius">Радиус окружности, перекрывающей сектор.</param>
        /// <returns><see cref="Sector"/>.</returns>
        /// <exception cref="ArgumentException">Если <paramref name="radius"/> Не число.</exception>
        /// <exception cref="ArgumentOutOfRangeException">Если <paramref name="radius"/> отрицательное число.</exception>
        public static Sector Create(Point2D point, Point2D center, double radius)
        {
            if (!double.IsNormal(radius))
                throw new ArgumentException("Не число.", nameof(radius));
            if (radius < 0)
                throw new ArgumentOutOfRangeException(nameof(radius), "Отрицательное значение не допускается.");
 
            Sector @new = new()
            {
                MeanLine = Math.Atan2(center.Y - point.Y, center.X - point.X),
                Width = 2 * Math.Asin(radius / point.Distance(center))
            };
 
            return @new;
        }
    }
}
1
6 / 2 / 1
Регистрация: 15.10.2020
Сообщений: 62
11.12.2022, 14:49  [ТС]
А как, теоретически, можно найти не перекрытые сектора? Единственное, что приходит в голову - проброс лучей из точки А по кругу, но это неэффективно.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16116 / 11237 / 2887
Регистрация: 21.04.2018
Сообщений: 33,038
Записей в блоге: 2
11.12.2022, 15:05
Цитата Сообщение от SlaTH Посмотреть сообщение
А как, теоретически, можно найти не перекрытые сектора?
Нужна реализация DoubleRange.
Что-то с таким алгоритмом.
1) Простой диапазон это нижняя и верхняя граница;
2) Фрагментированный диапазон это коллекция простых диапазонов:
3) Коллекция диапазонов упорядочена по нижней границе;
4) При добавлении диапазона в коллекцию, если a ней уже есть диапазоны входящие в его границы, то вместо них всех создаётся один общий диапазон который включает в себя все;
5) При вычитании диапазона, если есть диапазоны полностью входящие в него - они удаляются. Те которые входят частично - заменяются на диапазоны не входящие в вычитаемый.


Потом эта реализация используется так:
1) Создаётся диапазон +-пи;
2) Цикл по всем окружностям;
3) Для каждой окружности создаётся сектор;
4) Сектор вычитается из диапазона. Перед вычетом сектора которые пересекают границы +-пи, надо разбивать на два входящих в границы;
5) После вычитания сектора из диапазона, проверяется есть ли ещё что в его коллекции. Если нет - значит весь диапазон перекрыт;
6) Если диапазон ещё не пуст, значит цикл к следующей окружности.
7) Если цикл закончился нормальным выходом - значит остался какой-то не перекрытый диапазон.
1
6 / 2 / 1
Регистрация: 15.10.2020
Сообщений: 62
11.12.2022, 15:21  [ТС]
Спасибо за интересную идею, попробую реализовать.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16116 / 11237 / 2887
Регистрация: 21.04.2018
Сообщений: 33,038
Записей в блоге: 2
11.12.2022, 17:04
Цитата Сообщение от SlaTH Посмотреть сообщение
попробую реализовать.
Вот для затравки (не тестировал):
C#
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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
namespace Core2022.Data
{
    public readonly struct DoubleRange : IEquatable<DoubleRange>, IComparable<DoubleRange>, IComparable
    {
        public double Start { get; }
 
        public double End { get; }
 
        public DoubleRange(double start, double end)
        {
            if (!double.IsNormal(start))
                throw new ArgumentException("Допускаются только нормальные числа.", nameof(start));
            if (!double.IsNormal(end))
                throw new ArgumentException("Допускаются только нормальные числа.", nameof(end));
            if (start > end)
                throw new ArgumentOutOfRangeException(nameof(start), "Не может превышать end.");
 
            Start = start;
            End = end;
        }
 
        public int CompareTo(DoubleRange other)
        {
            int comp = Start.CompareTo(other.Start);
            if (comp == 0)
            {
                comp = End.CompareTo(other.End);
            }
            return comp;
        }
 
        public int CompareTo(object? obj)
        {
            if (obj is not DoubleRange range)
                throw new NotImplementedException("Сравнение реализовано только для DoubleRange.");
            return CompareTo(range);
        }
 
        public override bool Equals(object? obj)
        {
            return obj is DoubleRange range && Equals(range);
        }
 
        public bool Equals(DoubleRange other)
        {
            return Start == other.Start &&
                   End == other.End;
        }
 
        public override int GetHashCode()
        {
            return HashCode.Combine(Start, End);
        }
 
        public static bool operator ==(DoubleRange left, DoubleRange right)
        {
            return left.Equals(right);
        }
 
        public static bool operator !=(DoubleRange left, DoubleRange right)
        {
            return !(left == right);
        }
 
    }
}
Класс фрагментированного диапазона. Реализовал только добавление диапазонов.
C#
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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
using System.Collections.ObjectModel;
 
namespace Core2022.Data
{
    /// <summary>Класс фрагментированного диапазона.</summary>
    public class FragmentedRange
    {
        /// <summary>Коллекция фрагментов.
        /// Коллекция сортирована по <see cref="DoubleRange.Start"/>.
        /// Все пересекающиеся фрагменты объединены.</summary>
        public ReadOnlyCollection<DoubleRange> Ranges { get; }
 
        // Приватный мутабельный список фрагментов.
        private readonly List<DoubleRange> rangeList = new();
 
        // Конструктор. В нём связываются публичная иммутабельная коллекция и приватный мутабельный список.
        public FragmentedRange() => Ranges = new ReadOnlyCollection<DoubleRange>(rangeList);
 
        /// <summary>Добавление фрагмента.</summary>
        /// <param name="range">Диапазон фрагмента.</param>
        public void Add(DoubleRange range)
        {
            int index = rangeList.BinarySearch(range);
 
            // Если индекс положительный, значит есть диапазон с таким началом.
            if (index >= 0)
            {
                // Если конец диапазона не превышает конец имеющегося, то добавлять ничего не нужно.
                if (range.End <= rangeList[index].End)
                {
                    return;
                }
                // В противном случая - заменяем диапазон.
                rangeList[index] = range;
            }
 
            // Если диапазона с таким же индексом нет, то индекс будет отрицательным.
            // Его значение - инверсия индекса ближайшего большего элемента или Count.
            else
            {
                index = ~index; // Индекс ближайшего большего.
 
                // Проверка пересечения с предыдущим диапазоном
                if (index > 0 && rangeList[index - 1].End >= range.Start)
                {
                    index--;
                    // Если конец диапазона не превышает конец имеющегося, то добавлять ничего не нужно.
                    if (range.End <= rangeList[index].End)
                    {
                        return;
                    }
                    // В противном случая - объединяем диапазоны.
                    range = new DoubleRange(rangeList[index].Start, range.End);
                    rangeList[index] = range;
                }
                // Если не сливается с предыдущим, то вставка перед ближайшим большим.
                else
                {
                    rangeList.Insert(index, range);
                }
            }
 
            // Теперь нужно проверить слияние диапазонов.
 
            for (int i = index + 1; i < rangeList.Count; i++)
            {
                var current = rangeList[i];
                // Проверка условия выхода.
                // Если диапазон заканчивается после конца добавленного,
                // то дальше перебирать нет смысла. 
                if (current.End > range.End)
                {
                    // Проверка условия объединения.
                    // Если диапазон начинается до конца добавленного,
                    // то нужно объединить диапазоны. 
                    if (current.Start <= range.End)
                    {
                        range = new(range.Start, current.End);
                        rangeList[index] = range;
                        rangeList.RemoveAt(i);
                    }
 
                    break;
                }
 
                // Если диапазон заканчивается до конца добавленного,
                // то его нужно удалить.
                rangeList.RemoveAt(i);
                i--;
            }
        }
    }
}
Добавлено через 23 минуты
SlaTH, я код чуть поправил в методе Add нужна была проверка пересечения с предыдущим диапазоном.
Если код копировали, то замените текущей версией.
1
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16116 / 11237 / 2887
Регистрация: 21.04.2018
Сообщений: 33,038
Записей в блоге: 2
12.12.2022, 16:17
SlaTH, вышло реализовать?
0
6 / 2 / 1
Регистрация: 15.10.2020
Сообщений: 62
16.12.2022, 15:35  [ТС]
К сожалению на время пришлось отложить. Форм-мажор с компьютером. Постараюсь сообщить здесь о результате, когда закончу и отметить вас.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
16.12.2022, 15:35
Помогаю со студенческими работами здесь

Заливка сканирующей строкой в Borland C++
Как реализовать метод сканирующей строки с учетом следующих параметров: 1.Соседние точки имеют один цвет, отличающийся от цвета границ. ...

Алгоритм рисования прямой Ву
Всем привет. Есть код // Алгоритм Ву private Color SetColor(float t) { int c =...

Нарисовать фигуру и закрасить ее сканирующей строкой
В компиляторе Borland с++ требуется нарисовать фигуру и закрасить ее сканирующей строкой. Помогите пожалуйста... вот фигура

Заливка фигуры методом сканирующей строки
Смысл задачи, думаю, понятен. Построена фигура (по координатам вершин в массиве). Нужно закрасить, используя сканирующую строку (затравкой...

Алгоритм поиска (прямой метод)
Кто-нибудь может написать реализацию алгоритма поиска Forward Dawg Algorithm??


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
11
Ответ Создать тему
Новые блоги и статьи
PhpStorm 2025.3: WSL Terminal всегда стартует в ~
and_y87 14.12.2025
PhpStorm 2025. 3: WSL Terminal всегда стартует в ~ (home), игнорируя директорию проекта Симптом: После обновления до PhpStorm 2025. 3 встроенный терминал WSL открывается в домашней директории. . .
Как объединить две одинаковые БД Access с разными данными
VikBal 11.12.2025
Помогите пожалуйста !! Как объединить 2 одинаковые БД Access с разными данными.
Новый ноутбук
volvo 07.12.2025
Всем привет. По скидке в "черную пятницу" взял себе новый ноутбук Lenovo ThinkBook 16 G7 на Амазоне: Ryzen 5 7533HS 64 Gb DDR5 1Tb NVMe 16" Full HD Display Win11 Pro
Музыка, написанная Искусственным Интеллектом
volvo 04.12.2025
Всем привет. Некоторое время назад меня заинтересовало, что уже умеет ИИ в плане написания музыки для песен, и, собственно, исполнения этих самых песен. Стихов у нас много, уже вышли 4 книги, еще 3. . .
От async/await к виртуальным потокам в Python
IndentationError 23.11.2025
Армин Ронахер поставил под сомнение async/ await. Создатель Flask заявляет: цветные функции - провал, виртуальные потоки - решение. Не threading-динозавры, а новое поколение лёгких потоков. Откат?. . .
Поиск "дружественных имён" СОМ портов
Argus19 22.11.2025
Поиск "дружественных имён" СОМ портов На странице: https:/ / norseev. ru/ 2018/ 01/ 04/ comportlist_windows/ нашёл схожую тему. Там приведён код на С++, который показывает только имена СОМ портов, типа,. . .
Сколько Государство потратило денег на меня, обеспечивая инсулином.
Programma_Boinc 20.11.2025
Сколько Государство потратило денег на меня, обеспечивая инсулином. Вот решила сделать интересный приблизительный подсчет, сколько государство потратило на меня денег на покупку инсулинов. . . .
Ломающие изменения в C#.NStar Alpha
Etyuhibosecyu 20.11.2025
Уже можно не только тестировать, но и пользоваться C#. NStar - писать оконные приложения, содержащие надписи, кнопки, текстовые поля и даже изображения, например, моя игра "Три в ряд" написана на этом. . .
Мысли в слух
kumehtar 18.11.2025
Кстати, совсем недавно имел разговор на тему медитаций с людьми. И обнаружил, что они вообще не понимают что такое медитация и зачем она нужна. Самые базовые вещи. Для них это - когда просто люди. . .
Создание Single Page Application на фреймах
krapotkin 16.11.2025
Статья исключительно для начинающих. Подходы оригинальностью не блещут. В век Веб все очень привыкли к дизайну Single-Page-Application . Быстренько разберем подход "на фреймах". Мы делаем одну. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru