2010-12-03 11 views
7

En primer lugar, sólo para dar una idea visual de lo que busco, aquí está el resultado más cercano (todavía no es exactamente lo que busco) imagen que he encontrado:¿Algoritmo para resolver los puntos de una espiral distribuida equitativamente?

enter image description here

Aquí está la totalidad sitio-referencia: http://www.mathematische-basteleien.de/spiral.htm

PERO, no resuelve exactamente el problema que estoy buscando. Me gustaría almacenar una matriz de puntos de un algoritmo en espiral muy específico.

  • Los puntos se uniformemente distribuida
  • Los ciclos de 360 ​​grados tienen una brecha aún

Si no estoy equivocado, los dos primeros puntos serían:

  • punto [ 0] = nuevo punto (0,0);
  • punto [1] = nuevo Punto (1,0);

¿Pero dónde ir desde aquí?

Los únicos argumentos que me gustaría ofrecer son:

  • la cantidad de puntos que deseo resolver (longitud de la matriz).
  • la distancia entre cada punto (espacio de píxeles).
  • la distancia entre ciclos.

Casi suena, a mí, que tengo que calcular el "espiral circunferencia" (si hay tal término) con el fin de representar los puntos uniformemente distribuidos a lo largo de la espiral.

Can 2 * PI * radius se utilizan de forma fiable para este cálculo, ¿cree?

Si se ha hecho antes, ¡muestre algún ejemplo de código!

+0

Por "Cada punto se distribuye uniformemente", ¿quiere decir "El ángulo entre puntos consecutivos es una constante", o algo más? – mbeckish

+0

Esta pregunta es probablemente una mejor coincidencia para math.stackexchange.com. –

+0

Por "Cada ciclo de 360 ​​grados tiene un espacio par", ¿quiere decir "La diferencia entre el radio en el ángulo xy el radio en el ángulo x + 2 * Pi es una constante", o algo más? – mbeckish

Respuesta

18

pequeño problema diversión :)

Si nos fijamos en el diagrama más cerca, la secuencia se afirma claramente:

spiral diagram

Probablemente hay muchas soluciones a dibujar estos, tal vez más elegante, pero aquí está el mío:

Usted sabe que la hipotenusa es la raíz cuadrada del conteo del segmento actual + 1 y el lado opuesto del triángulo es siempre 1.

También sabes que Sine (Math.sin) del ángulo es igual al lado opuesto dividido por la hipotenusa. del antiguo SOHN sennen (Sine, Opuesto, Hipotenusa), - CAH-TOA.

Math.sin(angle) = opp/hyp 

conoce el valor del seno del ángulo, se conocen los dos lados, pero no saben el ángulo todavía, pero se puede utilizar el arco función seno (Math.asin) para que

angle = Math.asin(opp/hyp) 

Ahora conoce el ángulo de cada segmento y observe que se incrementa con cada línea.

Ahora que tiene un ángulo y un radio (la hipotenusa) se puede utilizar para polar a cartesiano fórmula para convertir que ángulo, radio par a una x, y par.

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

Has solicitado que una solución ActionScript, hay clase Point ya proporciona esta función para usted a través del método polar(). Le pasa un radio y un ángulo y devuelve su xey en un objeto Point.

Aquí hay un pequeño fragmento que traza la espiral. Puede controlar la cantidad de segmentos moviendo el mouse en el eje 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; 
} 

esto podría haber sido escrito en menos líneas, pero quería dividirla en dos funciones: uno que sólo se ocupa de calcular los números, y el otro que se ocupa de la elaboración de las líneas.

Estas son algunas capturas de pantalla:

spiral 1

spiral 2

spiral 3

Para la diversión que añade una versión de esto utilizando ProcessingJS here. Se ejecuta un poco lento, por lo que recomendaría Chromium/Chrome para esto.

ahora hasta puede ejecutar este código aquí (mover el ratón hacia arriba y hacia abajo): Respuesta

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>

+0

Amigo ... Todavía estoy viendo esto y bastante impresionado, podría resolverse con esas pocas líneas de código (al menos para mí no es mucho). Así es exactamente como separaría el código (dibujar/generar puntos). ¡Esto es fabuloso! Ojalá pudiera votar un poco más sobre tu respuesta: D – bigp

+0

También ... gracias por mostrar un uso muy práctico de Point.polar - Nunca me di cuenta de que podría resolver una expresión tan larga. Entonces, básicamente, en este caso, ¿se usa para "ajustar" el punto a lo largo del camino de "Archimedean"? – bigp

+0

@bigp Me alegro de poder ayudar. En cuanto a Point.polar, "snap" podría ser una forma de verlo. Prefiero imaginarme moviéndome en círculos, no en cuadrículas. Digamos que usted es el manejo de un reloj y desea moverse a las 3 en punto o a las 12 en punto ... ese sería su ángulo y qué tan lejos del centro deseaba llegar a su radio. Dibuja fácilmente círculos, pero si aumenta el radio, los círculos dejan de superponerse y se convierten en una espiral. Imagine dibujar círculos con una brújula/divisor. Ahora imagina intentar dibujar un círculo, pero de vez en cuando aumentas la distancia entre los mangos. –

3

de George era excelente! Estaba buscando la solución por bastante tiempo.

Aquí está el mismo código ajustado para PHP, en caso de que ayude a alguien. Uso el guión para dibujar puntos (= ciudades) para un mapa con coordenadas X, Y. X comienza desde la izquierda, Y comienza desde abajo a la izquierda.

<? 
/** 
* Initialize variables 
**/ 

// MAXIMUM width & height of canvas (X: 0->400, Y: 0->400) 
$width = 400; 

// For loop iteration amount, adjust this manually 
$segments = 10000; 

// Scale for radius 
$radiusScale = 2; 

// Draw dot (e.g. a city in a game) for every N'th drawn point 
$cityForEveryNthDot = 14; 

/** 
* Private variables 
**/ 
$radius = 0; 
$angle = 0; 
$centerPoint = $width/2; 

/** 
* Container print 
**/ 
print("<div style=\"width: ${width}px; height: ${width}px; background: #cdcdcd; z-index: 1; position: absolute; left: 0; top: 0;\"></div>"); 

/** 
* Looper 
**/ 
for($i=0;$i<$segments;$i++) { 
    // calculate radius and angle 
    $radius = sqrt($i+1) * $radiusScale; 
    $angle += asin(1/$radius); 

    // skip this point, if city won't be created here 
    if($i % $cityForEveryNthDot != 0) { 
     continue; 
    } 

    // calculate X & Y (from top left) for this point 
    $x = cos($angle) * $radius; 
    $y = sin($angle) * $radius; 

    // print dot 
    print("<div style=\"width: 1px; height: 1px; background: black; position: absolute; z-index: 2; left: " . round($x+$centerPoint) . "; top: " . round($y+$centerPoint) . ";\"></div>"); 

    // calculate rounded X & Y (from bottom left) 
    $xNew = round($x+$centerPoint); 
    $yNew = round($width - ($y+$centerPoint)); 

    // just some internal checks 
    if($xNew > 1 && $yNew > 1 && $xNew < $width && $yNew < $width) { 
     /** 
     * do something (e.g. store to database). Use xNew and yNew 
     **/ 
    } 
} 
+0

¡Gracias por compartir eso! Está bien para ver cómo se puede reproducir en otros idiomas y en diferentes contextos :) – bigp

Cuestiones relacionadas