Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
342 views
in Technique[技术] by (71.8m points)

actionscript 3 - 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!

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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>

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...