在螺旋线上画出等距离的点

28

我需要一个算法来计算螺旋路径上点的分布。

该算法的输入参数应为:

  • 环宽(从最内圈到环边之间的距离)
  • 点之间的固定距离
  • 所需绘制的点数

要绘制的螺旋线是一种阿基米德螺旋线,并且得到的点必须等距分布。

该算法应打印出单个点的笛卡尔坐标序列,例如:

Point 1: (0.0) Point 2: (..., ...) ........ Point 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);
    }
}

但是点的分布不正确,这些点之间不是等距的。

非等距螺旋分布

正确的分布示例是左图所示:

螺旋分布


1
当您说等距时,是指在一点到另一点的直线路径上遵循的恒定距离,还是指沿着螺旋路径的距离?(我猜您可能想要后者,但当前的措辞更接近前者)。 - Jerry Coffin
1
嗨,杰瑞。提前感谢你。我的意思是沿着螺旋路径的恒定距离。我认为两个距离都相似,但沿曲线的距离更精确。(也许!) - Giulio Pierucci
2
Wolfram 给出了螺旋线上一段长度的方程。至少乍一看,对于给定距离而言,重新排列得到对应角度似乎是相当简单的代数运算(尽管我想可能有我没注意到的东西使它比看起来更难)。 - Jerry Coffin
谢谢Jerry。你知道我怎么把Wolfram概念加入我的代码吗?我更新了问题 :) - Giulio Pierucci
@JerryCoffin 那个网页(实际上我读的是这个页面:Wolfram)提供了s(theta)的解析表示,但是解析地反演它以获得theta(s)非常困难。我认为可以使用根查找算法或某些近似插值函数来实现这个目的。如果我有时间,我会在一个单独的答案中详细说明。 - Alan Wang
4个回答

24

初步估计 - 对于在足够接近的区域绘制方块来说可能已经足够准确了 - 螺旋线是一个圆,并且通过比率 弦长/半径 增加角度。

// 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圈螺旋第二次近似

上述第二个版本使用了一个基于求解平均半径的角度theta和theta+delta的增量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;

对于松散螺旋,为了得到更好的结果,请使用数值迭代解法来找到计算距离在适当容差范围内的 delta 值。


非常感谢,大家。这个算法非常适合我的需求! - Giulio Pierucci
为了好玩(并不完全理解),我正在尝试绘制您的第一张图片,哪些值可以类似地表示? - Terence
1
@Terence 不确定具体是什么,但它有十个线圈,你可以在http://jsfiddle.net/gjowrfcd/1/上玩。 - Pete Kirkham
1
你应该限制角度变化量最多为1.0左右,以获得更好的螺旋末端结果。 - Will Ness

8

贡献一个Python生成器(OP没有要求任何特定的语言)。 它使用与Pete Kirkham的答案类似的圆逼近。

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

5

根据liborm的答案,在Swift中,按照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
}

3

我觉得这篇文章非常有用,所以我补充了一个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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接