algorithm - Dibujar puntos equidistantes en una espiral.
draw algebra (4)
Necesito un algoritmo para calcular la distribución de puntos en una trayectoria en espiral.
Los parámetros de entrada de este algoritmo deben ser:
- Ancho del bucle (distancia desde el bucle más interno)
- Distancia fija entre los puntos.
- El número de puntos a dibujar.
La espiral para dibujar es una espiral arquimediana y los puntos obtenidos deben ser equidistantes entre sí.
El algoritmo debe imprimir la secuencia de las coordenadas cartesianas de puntos individuales, por ejemplo:
Punto 1: (0.0) Punto 2: (..., ...) ........ Punto N (..., ...)
¡El lenguaje de programación no es importante y toda la ayuda es muy apreciada!
EDITAR:
Ya tengo y modifico este ejemplo de este sitio:
//
//
// 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);
}
}
Pero la disposición del punto es incorrecta, los puntos no son equidistantes entre sí.
El ejemplo de distribución correcta es la imagen de la izquierda:
Contribuyendo con un generador de Python (OP no solicitó ningún idioma específico). Utiliza una aproximación circular similar a la de Pete Kirkham.
arc
es la distancia de puntos requerida a lo largo del camino, la separation
es la separación requerida de los brazos espirales.
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
En Swift (basado en la respuesta de liborm), tomando las tres entradas como OP solicitó:
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
}
Encontré esta publicación útil, así que estoy agregando una versión de Matlab del código anterior.
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
Para una primera aproximación, que probablemente sea lo suficientemente buena para trazar bloques lo suficientemente cerca, la espiral es un círculo e incrementa el ángulo en función de la 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;
}
Sin embargo, para una espiral más suelta, tendrá que resolver la distancia del camino con mayor precisión como espacios demasiado amplios donde la diferencia entre la away
para los puntos sucesivos es significativa en comparación con el chord
:
La segunda versión anterior utiliza un paso basado en la resolución de delta basado en el uso del radio promedio para theta y theta + delta:
// 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;
Para obtener resultados aún mejores en una espiral suelta, use una solución iterativa numérica para encontrar el valor de delta donde la distancia calculada está dentro de una tolerancia adecuada.