Нарисуйте равноудаленные точки на спирали

Мне нужен алгоритм для расчета распределения точек на спиральном пути.

Входными параметрами этого алгоритма должны быть:

  • Ширина петли (расстояние от самой внутренней петли)
  • Фиксированное расстояние между точками
  • Количество очков для розыгрыша

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

Алгоритм должен распечатать последовательность декартовых координат отдельных точек, например:

Точка 1: (0,0) Точка 2: (..., ...) ........ Точка N (..., ...)

Язык программирования не важен, и вся помощь приветствуется!

РЕДАКТИРОВАТЬ:

Я уже получаю и изменяю этот пример с этого сайта:

    //
//
// centerX-- X origin of the spiral.
// centerY-- Y origin of the spiral.
// radius--- Distance from origin to outer arm.
// sides---- Number of points or sides along the spiral's arm.
// coils---- Number of coils or full rotations. (Positive numbers spin clockwise, negative numbers spin counter-clockwise)
// rotation- Overall rotation of the spiral. ('0'=no rotation, '1'=360 degrees, '180/360'=180 degrees)
//
void SetBlockDisposition(float centerX, float centerY, float radius, float sides, float coils, float rotation)
{
    //
    // How far to step away from center for each side.
    var awayStep = radius/sides;
    //
    // How far to rotate around center for each side.
    var aroundStep = coils/sides;// 0 to 1 based.
    //
    // Convert aroundStep to radians.
    var aroundRadians = aroundStep * 2 * Mathf.PI;
    //
    // Convert rotation to radians.
    rotation *= 2 * Mathf.PI;
    //
    // For every side, step around and away from center.
    for(var i=1; i<=sides; i++){

        //
        // How far away from center
        var away = i * awayStep;
        //
        // How far around the center.
        var around = i * aroundRadians + rotation;
        //
        // Convert 'around' and 'away' to X and Y.
        var x = centerX + Mathf.Cos(around) * away;
        var y = centerY + Mathf.Sin(around) * away;
        //
        // Now that you know it, do it.

        DoSome(x,y);
    }
}

Но расположение точки неправильное, точки не равноудалены друг от друга.

Спираль с неравномерным распределением

Правильный пример распределения — это изображение слева:

Сиралы


person Giulio Pierucci    schedule 15.12.2012    source источник
comment
Когда вы говорите «равноудаленно», вы имеете в виду постоянное расстояние по прямому (прямому) пути от одной точки к другой, или вы имеете в виду расстояние по пути спирали? (Я предполагаю, что вы, вероятно, хотите последнего, но текущая формулировка звучит ближе к первому).   -  person Jerry Coffin    schedule 15.12.2012
comment
Привет, Джерри. Заранее спасибо. Я имею в виду постоянное расстояние по пути спирали. Я думаю, что оба расстояния похожи, но расстояние по кривой точнее. (МОЖЕТ БЫТЬ!)   -  person Giulio Pierucci    schedule 15.12.2012
comment
Вольфрам дает уравнение для длины спирали. По крайней мере, на первый взгляд перестановка этого для получения угла для заданного расстояния выглядит как довольно простая алгебраическая манипуляция (хотя я полагаю, что мог что-то упустить, так что это сложнее, чем кажется).   -  person Jerry Coffin    schedule 15.12.2012
comment
Спасибо, Джерри. Знаете ли вы, как я могу добавить концепции Wolfram в свой код? Я обновил вопрос :)   -  person Giulio Pierucci    schedule 16.12.2012
comment
@JerryCoffin Эта веб-страница (на самом деле я читал эту страницу: Wolfram) дает s(theta) аналитически, но инвертировать его (чтобы получить тета (ы)) аналитически довольно сложно. Я думаю, что для этой цели можно использовать либо алгоритм поиска корня, либо некоторую приблизительную функцию интерполяции. Если у меня будет время, я уточню это в отдельном ответе.   -  person Alan Wang    schedule 09.11.2015


Ответы (4)


В первом приближении, которое, вероятно, достаточно хорошо для построения блоков достаточно близко, спираль представляет собой круг, а угол увеличивается на коэффициент chord / radius.

// value of theta corresponding to end of last coil
final double thetaMax = coils * 2 * Math.PI;

// How far to step away from center for each side.
final double awayStep = radius / thetaMax;

// distance between points to plot
final double chord = 10;

DoSome ( centerX, centerY );

// For every side, step around and away from center.
// start at the angle corresponding to a distance of chord
// away from centre.
for ( double theta = chord / awayStep; theta <= thetaMax; ) {
    //
    // How far away from center
    double away = awayStep * theta;
    //
    // How far around the center.
    double around = theta + rotation;
    //
    // Convert 'around' and 'away' to X and Y.
    double x = centerX + Math.cos ( around ) * away;
    double y = centerY + Math.sin ( around ) * away;
    //
    // Now that you know it, do it.
    DoSome ( x, y );

    // to a first approximation, the points are on a circle
    // so the angle between them is chord/radius
    theta += chord / away;
}

Спираль из 10 витков

Тем не менее, для более свободной спирали вам придется вычислять расстояние пути более точно, так как промежутки слишком широки, где разница между away для последовательных точек значительна по сравнению с chord: 1 спиральная спираль, 1-е приближение 1 спиральная спираль, 2-е приближение

Во второй версии выше используется шаг, основанный на решении для дельты на основе использования среднего радиуса для тета и тета + дельта:

// take theta2 = theta + delta and use average value of away
// away2 = away + awayStep * delta 
// delta = 2 * chord / ( away + away2 )
// delta = 2 * chord / ( 2*away + awayStep * delta )
// ( 2*away + awayStep * delta ) * delta = 2 * chord 
// awayStep * delta ** 2 + 2*away * delta - 2 * chord = 0
// plug into quadratic formula
// a= awayStep; b = 2*away; c = -2*chord

double delta = ( -2 * away + Math.sqrt ( 4 * away * away + 8 * awayStep * chord ) ) / ( 2 * awayStep );

theta += delta;

Чтобы получить еще лучшие результаты для свободной спирали, используйте численное итеративное решение, чтобы найти значение дельты, при котором расчетное расстояние находится в допустимых пределах.

person Pete Kirkham    schedule 16.12.2012
comment
Большое спасибо, ребята. Этот алгоритм идеально подходит для моих нужд! - person Giulio Pierucci; 17.12.2012
comment
Для забавы (не полностью понимая это) я пытаюсь построить ваше первое изображение, какие значения будут представляться похожими? - person Terence; 16.09.2018
comment
@Terence точно не уверен, но это десять катушек, и вы можете играть на jsfiddle.net/gjowrfcd/1 - person Pete Kirkham; 26.09.2018
comment
вам, вероятно, также следует ограничить угол дельты максимум до 1,0 или около того, чтобы получить лучшие результаты в конце спирали. - person Will Ness; 03.07.2021

Предоставление генератора Python (OP не запрашивал какой-либо конкретный язык). Он использует такое же приближение круга, как и ответ Пита Киркхема.

arc — требуемое расстояние между точками на пути, separation — требуемое расстояние между спиральными рукавами.

def spiral_points(arc=1, separation=1):
    """generate points on an Archimedes' spiral
    with `arc` giving the length of arc between two points
    and `separation` giving the distance between consecutive 
    turnings
    - approximate arc length with circle arc at given distance
    - use a spiral equation r = b * phi
    """
    def p2c(r, phi):
        """polar to cartesian
        """
        return (r * math.cos(phi), r * math.sin(phi))

    # yield a point at origin
    yield (0, 0)

    # initialize the next point in the required distance
    r = arc
    b = separation / (2 * math.pi)
    # find the first phi to satisfy distance of `arc` to the second point
    phi = float(r) / b
    while True:
        yield p2c(r, phi)
        # advance the variables
        # calculate phi that will give desired arc length at current radius
        # (approximating with circle)
        phi += float(arc) / r
        r = b * phi
person liborm    schedule 17.12.2014

В Swift (на основе ответа liborm), используя три входа в соответствии с запросом OP:

func drawSpiral(arc: Double, separation: Double, numPoints: Int) -> [(Double,Double)] {

  func p2c(r:Double, phi: Double) -> (Double,Double) {
      return (r * cos(phi), r * sin(phi))
  }

  var result = [(Double(0), Double(0))]

  var r = arc
  let b = separation / (2 * Double.pi)
  var phi = r / b

  var remaining = numPoints
  while remaining > 0 {
      result.append(p2c(r: r, phi: phi))
      phi += arc / r
      r = b * phi
      remaining -= 1
  }
  return result
}
person stoffen    schedule 17.02.2016

Я нашел этот пост полезным, поэтому я добавляю версию кода выше для Matlab.

function [sx, sy] = spiralpoints(arc, separation, numpoints)

    %polar to cartesian
    function [ rx,ry ] = p2c(rr, phi)
        rx = rr * cos(phi);
        ry = rr * sin(phi);
    end

    sx = zeros(numpoints);
    sy = zeros(numpoints);

    r = arc;
    b = separation / (2 * pi());
    phi = r / b;

    while numpoints > 0
        [ sx(numpoints), sy(numpoints) ] = p2c(r, phi);
        phi = phi + (arc / r);
        r = b * phi;
        numpoints = numpoints - 1;
    end

end
person Hans Omenaas    schedule 25.10.2017