Нарисуйте эквидистантные точки на спирали
Мне нужен алгоритм для вычисления распределения точек по спиральному пути.
Входные параметры этого алгоритма должны быть:
- Ширина петли (расстояние от самой внутренней петли)
- Фиксированное расстояние между точками
- Число точек для рисования
Спираль для рисования - это архимедованная спираль, и полученные точки должны быть равноудаленными друг от друга.
Алгоритм должен распечатать последовательность декартовых координат отдельных точек, например:
Точка 1: (0.0)
Пункт 2: (...,...)
........
Точка N (...,...)
Язык программирования не важен, и вся помощь очень ценится!
EDIT:
Я уже получаю и изменяю этот пример с этого сайта:
//
//
// 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 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);
}
}
Но расположение точки неверно, точки не равноудалены друг от друга.
![Spiral with non equidistant distribution]()
Правильный пример распределения - это изображение слева:
![Sirals]()
Ответы
Ответ 1
В первом приближении, что, вероятно, достаточно хорошо для построения блоков достаточно близко - спираль представляет собой круг и увеличивает угол на коэффициент 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 coil spiral]()
Однако для более свободной спирали вам нужно будет более точно решить расстояние пути, так как пространства слишком широкие, где разница между away
для последовательных точек значительна по сравнению с chord
:
![1 coil spiral 1st approximation]()
![1 coil spiral 2nd approximation]()
В приведенной выше второй версии используется шаг, основанный на решении для дельта, основанный на использовании среднего радиуса для тета и тета + дельта:
// 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;
Для получения еще лучших результатов на свободной спирали используйте числовое итеративное решение, чтобы найти значение дельта, где расчетное расстояние находится в пределах допустимого отклонения.
Ответ 2
В Swift (на основе ответа liborm), беря три входа в качестве запрошенных OP:
func drawSpiral(arc: Double, separation: Double, var 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 * M_PI)
var phi = r / b
while numPoints > 0 {
result.append(p2c(r, phi: phi))
phi += arc / r
r = b * phi
numPoints -= 1
}
return result
}
Ответ 3
Вклад генератора 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
Ответ 4
Я нашел этот пост полезным, поэтому добавляю версию 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