Рассчитать случайные точки (пиксель) внутри круга (изображение)


19

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

private Point CalculatePoint()
{
    var angle = _random.NextDouble() * ( Math.PI * 2 );
    var x = _originX + ( _radius * Math.Cos( angle ) );
    var y = _originY + ( _radius * Math.Sin( angle ) );
    return new Point( ( int )x, ( int )y );
}

И это прекрасно работает для нахождения всех точек по окружности круга, но мне нужны все точки из любой точки круга. Если это не имеет смысла, дайте мне знать, и я сделаю все возможное, чтобы уточнить.


Проверьте обновление.
Дэвид Гувейя

3
Хороший вопрос в том смысле, что непреднамеренное создание взвешенного распределения является распространенной ошибкой.
Тим Холт

Ответы:


35

Если вы хотите простое решение, просто рандомизируйте радиус:

private Point CalculatePoint()
{
    var angle = _random.NextDouble() * Math.PI * 2;
    var radius = _random.NextDouble() * _radius;
    var x = _originX + radius * Math.Cos(angle);
    var y = _originY + radius * Math.Sin(angle);
    return new Point((int)x,(int)y);
}

Это, однако, приводит к тому, что ваши точки становятся более сосредоточенными к центру круга:

введите описание изображения здесь

Чтобы получить равномерное распределение, внесите следующие изменения в алгоритм:

var radius = Math.Sqrt(_random.NextDouble()) * _radius;

Который даст следующий результат:

введите описание изображения здесь

Для получения дополнительной информации проверьте следующую ссылку: MathWorld - Disk Point Picking .

И, наконец, простая демонстрация JsFiddle, сравнивающая обе версии алгоритма.


1
Отличный ответ. Еще одна вещь, которую нужно добавить: не забудьте
запустить

Ой, ты победил, встретил это - не видел этот пост, когда я отправил мой. Сайт wolfram является отличным ресурсом для такого рода вещей.
Тим Холт

1
@TimHolt случается все время :)
Дэвид Гувея

Это при условии, что центр круга находится в 0,0?
13:06

@PsychoDad Центр круга будет (_originX, _originY)
Дэвид Гувея

5

НЕ просто используйте случайные r и theta! Это создает взвешенное распределение с большим количеством точек в центре. Эта страница хорошо иллюстрирует это ...

http://mathworld.wolfram.com/DiskPointPicking.html

Вот метод, который создает невзвешенное распределение ...

var r = rand(0,1)
var theta = rand(0,360)

var x = sqrt(r) * cos(theta)
var y = sqrt(r) * sin(theta)

К сожалению, дубликат выбранного ответа: P
Тим Холт

Я сбит с толку, потому что вы говорите, что не следует использовать случайные r и theta, поскольку это создает взвешенное распределение, тогда код, который, как вы заявляете, создает невзвешенное распределение, генерирует r в пределах диапазона [0,1]. Вы намеревались получить квадратный корень случайного числа?
PeteUK

Да, выполнение квадратного корня радиуса (до 0-1) уменьшает неожиданную концентрацию точек в середине. Посмотрите размещенную мной ссылку на Вольфрама, которая иллюстрирует это и объясняет это математикой лучше, чем я.
Тим Холт

Виноват. Я вижу, вы делаете sqrt (r) при расчете х и у.
PeteUK

4

Ты на полпути. В дополнение к генерации случайного угла, просто сгенерируйте случайное расстояние, меньшее или равное радиусу, взвешенное так, чтобы вы получили равномерное распределение:

private Point CalculatePoint()
{
    var angle = _random.NextDouble() * Math.PI * 2;
    var distance = Math.Sqrt(_random.NextDouble()) * _radius;
    var x = _originX + (distance * Math.Cos(angle));
    var y = _originY + (distance * Math.Sin(angle));
    return new Point((int)x, (int)y);
}

Теперь ты думаешь с полярностью .

Вы также можете взвесить расстояние, чтобы избежать квадратного корня:

var distance = _random.NextDouble() + _random.NextDouble();
distance = (distance <= 1 ? distance : 2 - distance) * _radius;

Итак, мы дали точно такой же ответ в течение нескольких секунд разницы. Что теперь? :)
Дэвид Гувея

@DavidGouveia Мы оба получили положительные отзывы за то, что оба правы. Все побеждают! : D
Джон Перди

Оба ответа очень ценятся (и ссылка тоже!). Чувак, я идиот за то, что сам этого не видел, -1 мне :(
Еще

Это создаст случайные точки, но они не будут равномерно распределены по диску, верно? Скорее они будут взвешены к центру. Просто проверяя, я что-то не пропустил
PeteUK

1
@PeteUK: Вы правы, расстояние должно быть взвешенным. Позвольте мне обновить.
Джон Перди

3

Если производительность является проблемой, то одним из альтернативных решений является создание случайного положения в поле с шириной / высотой вашего круга, а затем отбрасывание любых точек, которые не находятся в области круга.

Преимущество этого метода заключается в том, что вы не выполняете функции cos / sin / sqrt, что в зависимости от вашей платформы может значительно сэкономить на скорости.

var x = _random.NextDouble();
var y = _random.NextDouble();
if (x*x + y*y < 1.0f)
{
    // we have a usable point inside a circle
    x = x * diameter - _radius + _OriginX;
    y = y * diameter - _radius + _OriginY;
    // use the point(x,y)
}

Я собираюсь попробовать это и посмотреть, ускоряет ли это вещи. Я не уверен, что у меня проблемы с производительностью, но я все равно попробую, спасибо!
DMills

0

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

            private Vector2 CalculatePosition()
            {
                double angle = _rnd.NextDouble() * Math.PI * 2;
                double radius = InnerCircleRadius + (Math.Sqrt(_rnd.NextDouble()) * (OuterCircleRadius - InnerCircleRadius));
                double x = (_context.ScreenLayout.Width * 0.5f) + (radius * Math.Cos(angle));
                double y = (_context.ScreenLayout.Height * 0.5f) + (radius * Math.Sin(angle));
                return new Vector2((int)x, (int)y);
            }

Это похожий подход, как уже упоминалось ранее, но он дал другие результаты. Внутренняя часть круга останется пустой без точек.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.