Забавная маленькая проблема:)
Если вы посмотрите на диаграмму поближе, последовательность будет четко сформулирована:
Возможно, есть много решений для рисования, может быть, более элегантно, но вот мое:
Вы знаете, что гипотенуза является квадратным корнем из текущего количества сегментов + 1
и противоположная сторона треугольника всегда равна 1.
Также вы знаете, что синус (Math.sin) угла равен противоположной стороне, деленной на гипотенузу.
из старой мнонической SOH (синус, противоположность, гипотенуза), - CAH-TOA.
Math.sin(angle) = opp/hyp
Вы знаете значение синуса для угла, вы знаете две стороны, но вы еще не знаете угол, но вы можете использовать для этого функцию дуги синуса (Math.asin)
angle = Math.asin(opp/hyp)
Теперь вы знаете угол для каждого сегмента и замечаете, что он увеличивается с каждой линией.
Теперь, когда у вас есть угол и радиус (гипотенуза), вы можете использовать для полярная в декартовая формула для преобразования этой пары angle, radius х, у пара.
x = Math.cos(angle) * radius;
y = Math.sin(angle) * radius;
Поскольку вы запросили решение для ActionScript, класс Point уже предоставляет вам эту функцию с помощью метода polar () . Вы передаете ему радиус и угол, и он возвращает ваши x и y в объекте Point.
Вот небольшой фрагмент, который показывает спираль. Вы можете контролировать количество сегментов, перемещая мышь по оси Y.
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;
}
Это могло бы быть написано в меньшем количестве строк, но я хотел разделить это на две функции:
один, который имеет дело только с вычислением чисел, и другой, который имеет дело с рисованием линий.
Вот несколько скриншотов:
Ради интереса я добавил эту версию, используя ProcessingJS здесь .
Работает немного медленно, поэтому я бы порекомендовал Chromium / Chrome для этого.
Теперь вы можете запустить этот код прямо здесь (двигать мышь вверх и вниз):
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>