Tipus | joguina |
---|---|
Data de creació | 1965 |
L'espirògraf és una joguina de dibuix geomètric que permet traçar corbes matemàtiques estil ruleta, tècnicament conegudes com a hipotrocoide i epitrocoide. Va ser desenvolupat per l'enginyer britànic Denys Fisher i venut per primera vegada el 1965.
En computació, la idea es pot ampliar de forma recursiva per generar un espirògraf fractal, on el cercle generatiu actua a la vegada de cercle directiu d'un de més petit, i així successivament generant cada vegada més detall en el traç final d'una manera similar a les sèries de Fourier complexes. Dit d'una altra manera, un espirògraf bàsic de dos cercles (l'immòbil, anomenat directiu, i el que fa la rotació, anomenat generatiu) es pot considerar el cas més simple d'una família de corbes trocoides.
Es necessiten definir varis de paràmetres per construir un espirògraf; el nombre total de cercles directius (n), la ratio entre els radis dels diferents cercles (m), la ratio entre la velocitat de gir dels diferents cercles (k), i la distància de cada cercle (d) respecte el centre de l'anterior.
El radi del cercle actual es calcula utilitzant la ratio descrita, .
La distància del cercle actual respecte el centre es calcula tenint en compte el radi del cercle anterior i el del cercle actual, , aplicant la suma en el cas de l'epitrocoide i la resta en el de l'hipotrocoide.
La velocitat de gir es calcula partint de la velocitat de gir del cercle anterior i aplicant una ratio constant. Aquesta ratio és gairebé sempre negativa, és a dir, la direcció de rotació va alternant-se a cada cercle.
La fórmula més bàsica seria . Aquesta velocitat és la que s'utilitza per actualitzar l'angle .
Obtenim la posició de cada cercle:
Aquesta posició de l'últim cercle serà la utilitzada per aplicar el traçat final de l'espirògraf.
Exemple en pseudocodi:
// Definir els paràmetres necessaris n = 11 r_ratio = 3 d_prev = 0.5 d_current = 0.5 k = -4 // Crear el cercle inicial ary orbits = [] circle = Circle.new(size) // Mida del cercle immòbil // Emmagatzemar tota la informació necessària del cercle orbit = Orbit.new(circle, d=0, angle=0, speed=1) orbits.push(o) // Crear cadascun dels cercles partint de l'anterior for i in 1...n prev = orbits[i - 1] size = previous.circle.size / r_ratio circle = Circle.new(size) circle.x = prev.circle.x circle.y = prev.circle.y d = previous.ciecle.size * d_prev + circle.size * d_current angle = previous.angle speed = previous.speed * k orbit = Orbit.new(circle, d, angle, speed) orbits.push(orbit) end // Actualitzar el dibuix canviant l'angle constantment loop do // Actualitzar la posició de cada cercle Graphics.update for i in 1...orbits.size prev = orbits[i - 1] orbit = orbits[i] orbit.angle += orbit.speed d = orbit.distance dx = prev.circle.x + d * Math.sin(angle) dy = prev.circle.y + d * Math.cos(angle) end // Traçar el dibuix last = orbits[-1].circle if (last_drawn != NULL) sx = last_drawn.x sy = last_drawn.y draw_line(sx, sy, last.x, last.y) else set_pixel(last.x, last.y) end last_drawn = Point.new(last.x, last.y) end