Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Algorithm to solve the points of a evenly-distributed / even-gaps spiral?

First, just to give a visual idea of what I'm after, here's the closest result (yet not exactly what I'm after) image that I've found:

enter image description here

Here's the entire site-reference: http://www.mathematische-basteleien.de/spiral.htm

BUT, it doesn't exactly solve the problem I'm after. I would like to store an array of points of a very specific spiral algorithm.

  • The points are evenly distributed
  • The 360 degree cycles have an even gap

If I'm not mistaken, the first two points would be:

  • point[ 0 ] = new Point(0,0);
  • point[ 1 ] = new Point(1,0);

But where to go from here?

The only arguments I'd like to provide are:

  • the quantity of points I wish to resolve (length of array).
  • the distance between each points (pixels gap).
  • the distance between cycles.

It almost sounds, to me, that I have to calculate the "spiral-circumference" (if there's such a term) in order to plot the evenly distributed points along the spiral.

Can 2*PI*radius be reliably used for this calculation you think?

If it's been done before, please show some code example!

like image 517
chamberlainpi Avatar asked Dec 03 '10 19:12

chamberlainpi


1 Answers

Fun little problem :)

If you look at the diagram closer, the sequence is clearly stated:

spiral diagram

There are probably many solutions to drawing these, maybe more elegant, but here's mine:

You know the hypotenuse is square root of the current segment count+1 and the opposite side of the triangle is always 1.

Also you know that Sine(Math.sin) of the angle is equal to the opposite side divided by the hypotenuse. from the old mnenonic SOH(Sine,Opposite,Hypotenuse),-CAH-TOA.

Math.sin(angle) = opp/hyp

You know the value of the sine for the angle, you know the two sides, but you don't know the angle yet, but you can use the arc sine function(Math.asin) for that

angle = Math.asin(opp/hyp)

Now you know the angle for each segment, and notice it increments with each line.

Now that you have an angle and a radius(the hypotenuse) you can use for polar to cartesian formula to convert that angle,radius pair to a x,y pair.

x = Math.cos(angle) * radius;
y = Math.sin(angle) * radius;

Since you asked for an actionscript solution, there Point class already provides this function for you through the polar() method. You pass it a radius and angle and it returns your x and y in a Point object.

Here's a little snippet which plots the spiral. You can control the number of segments by moving the mouse on the Y axis.

var sw:Number = stage.stageWidth,sh:Number = stage.stageHeight;
this.addEventListener(Event.ENTER_FRAME,update);
function update(event:Event):void{
    drawTheodorus(144*(mouseY/sh),sw*.5,sh*.5,20);
}
//draw points
function drawTheodorus(segments:int,x:Number,y:Number,scale:Number):void{
    graphics.clear();
    var points:Array = getTheodorus(segments,scale);
    for(var i:int = 0 ; i < segments; i++){
        points[i].offset(x,y);
        graphics.lineStyle(1,0x990000,1.05-(.05+i/segments));
        graphics.moveTo(x,y);//move to centre
        graphics.lineTo(points[i].x,points[i].y);//draw hypotenuse
        graphics.lineStyle(1+(i*(i/segments)*.05),0,(.05+i/segments));
        if(i > 0) graphics.lineTo(points[i-1].x,points[i-1].y);//draw opposite
    }
}
//calculate points
function getTheodorus(segments:int = 1,scale:Number = 10):Array{
    var result = [];
    var radius:Number = 0;
    var angle:Number = 0;
    for(var i:int = 0 ; i < segments ; i++){
        radius = Math.sqrt(i+1);
        angle += Math.asin(1/radius);//sin(angle) = opposite/hypothenuse => used asin to get angle
        result[i] = Point.polar(radius*scale,angle);//same as new Point(Math.cos(angle)*radius.scale,Math.sin(angle)*radius.scale)
    }
    return result;
}

This could've been written in less lines, but I wanted to split this into two functions: one that deals only with computing the numbers, and the other which deals with drawing the lines.

Here are some screenshots:

spiral 1

spiral 2

spiral 3

For fun I added a version of this using ProcessingJS here. Runs a bit slow, so I would recommend Chromium/Chrome for this.

Now you can actually run this code right here (move the mouse up and down):

var totalSegments = 850,hw = 320,hh = 240,segments;
var len = 10;
points = [];
function setup(){
  createCanvas(640,480);
  smooth();
  colorMode(HSB,255,100,100);
  stroke(0);
  noFill();
  //println("move cursor vertically");
}
function draw(){
  background(0);
  translate(hw,hh);
  segments = floor(totalSegments*(mouseY/height));
  points = getTheodorus(segments,len);
  for(var i = 0 ; i < segments ; i++){
    strokeWeight(1);
    stroke(255-((i/segments) * 255),100,100,260-((i/segments) * 255));
    line(0,0,points[i].x,points[i].y);
    // strokeWeight(1+(i*(i/segments)*.01));
    strokeWeight(2);
    stroke(0,0,100,(20+i/segments));
    if(i > 0) line(points[i].x,points[i].y,points[i-1].x,points[i-1].y);
  }
}
function getTheodorus(segments,len){
  var result = [];
  var radius = 0;
  var angle = 0;
  for(var i = 0 ; i < segments ; i++){
    radius = sqrt(i+1);
    angle += asin(1/radius);
    result[i] = new p5.Vector(cos(angle) * radius*len,sin(angle) * radius*len);
  }
  return result;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.4.4/p5.min.js"></script>
like image 63
George Profenza Avatar answered Nov 03 '22 00:11

George Profenza