Skip to content

Policircumference

Animación

animation

Exploración Visual

Policircumference es una de las primeras animaciones que desarrollé con la biblioteca p5.js. En este experimento visual, exploro la transición de polígonos regulares hacia la apariencia de una circunferencia a través del aumento progresivo del número de lados.

El concepto se basa en una idea geométrica simple: a medida que el número de lados de un polígono aumenta, este se asemeja cada vez más a un círculo. La animación busca resaltar esta transición de manera fluida y dinámica.

Dinámica de la Animación

La animación sigue un ciclo en el que:

  • Se inicia con un polígono regular de pocos lados.
  • Gradualmente, el número de lados aumenta, acercándose al efecto visual de una circunferencia.
  • Posteriormente, el proceso se invierte de forma suavizada, reduciendo los lados progresivamente hasta volver a la forma inicial.
  • Este proceso se convierte en un bucle continuo, generando un flujo visual hipnótico.

Implementación Técnica

La animación se desarrolla en p5.js y sigue una estructura basada en la actualización de parámetros en cada fotograma:

  • Número de lados: Se incrementa y decrementa dentro de un rango predefinido para generar la transición.
  • Radio del polígono: Se mantiene constante para preservar la percepción de transformación sin variaciones en escala.
  • Interpolación: Se aplican funciones de suavizado para evitar transiciones abruptas y dotar de fluidez al efecto.

Interactividad y Posibilidades

Dado que el código está construido en p5.js, se pueden agregar controles interactivos para modificar:

  • La velocidad de la transición.
  • El rango de lados mínimo y máximo.
  • Efectos visuales como colores y transparencias.

El resultado es una animación en bucle que visualiza de manera elegante la relación entre los polígonos y las circunferencias, resaltando su conexión geométrica fundamental.

Code (p5.js)

Text Only
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
var gshooter;
var nelementos=40;
var radioOriginal=200;


function setup() {
// put setup code here
createCanvas(1200, 800);
gshooter=new Shooter(width/2, height/2, 1,nelementos,radioOriginal, 0.09);
}

function draw() {

    // put drawing code here
     background(51);

     gshooter.update();
     gshooter.display();
}

    //Parameters
    //maximo: max number of elements
    //minimo: min number of elements
    //id: identification of the Shooter
    //numShooters: number of Shooters generated
    //radio of the Shooter (distance to the centerz<)
    //speed of animation
    function Shooter(xpos, ypos, minimo, maximo, radio, speed){

    //variables
    this.elementos=1;
    this.r=radio;
    this.radio_min=8;
    this.maximo=maximo;
    this.minimo=minimo;
    this.paso=speed;
    //translation of the animation to witsh/part, height/part
    this.xoff=0;

    this.xpos=xpos;
    this.ypos=ypos;
    this.parte=1;

    //variables para segunda animación
    this.pos=createVector(0, 0);
    this.place=createVector(xpos, ypos+this.r);
    this.m=0;
    this.paso2=0.007;
    this.nradios=maximo;

    this.renew=function(){
        gshooter=new Shooter(width/2, height/2, this.minimo,this.maximo,radioOriginal, this.paso);
    }

    //update function
    this.update=function(){

if(this.parte==1){
  if(this.elementos<this.maximo){
    this.elementos+=this.paso;
  }else{
      this.elementos=this.maximo;
      this.parte=2;
  }
}
if(this.parte==2){
  if(this.m<1){
    this.m=this.m+this.paso2;
    this.r=radioOriginal*(1-this.m);
  }
  else{
    this.m=1;
    this.r=0;
    this.renew();
  }

  //coordinates for point B
  this.x_1=this.r*Math.sin(TWO_PI*(1-this.m))/(1-this.m);
  this.x_2=this.r*(1-Math.cos(TWO_PI*(1-this.m)))/(1-this.m);
  //angle
  this.angle=TWO_PI*(1-this.m);

    }   
}
this.display=function(){
fill(255);

if(this.parte==1){
  //la variable giro controla el enlace del vértice más cercano al origen
  //que no tiene exactamente un sector de diferencia con él
  var giro=1;

  var entero=Math.floor(this.elementos);
  //var varRad=10*noise(this.xoff-this.id/10);
  //this.xoff+=0.01*this.id;
  for(var i=0; i<=this.elementos;i++){
    push();
    //noFill();
    fill(255);
    stroke(255);
    strokeWeight(2);
    translate(this.xpos, this.ypos);
    //cuando el elemento corresponde a la parte decimal mantenemos el valor de giro
    if(i==entero) giro=Math.abs(this.elementos-Math.floor(this.elementos));

    rotate(i*TWO_PI/this.elementos+PI/2);

    ellipse(this.r, 0, this.radio_min, this.radio_min);
    line(0, 0, this.r, 0);
    line(this.r, 0, this.r*Math.cos(giro*TWO_PI/this.elementos),   this.r*Math.sin(giro*TWO_PI/this.elementos));
    //var y_point=this.radio*Math.tan(TWO_PI/this.elementos);
    //line(0, this.radio, this.radio, y_point);
    pop();
  }
}

if(this.parte==2){
  fill(255);
  push();
  translate(this.place.x, this.place.y);
  rotate(PI);

  fill(255);
  stroke(255);
  strokeWeight(2);

  //ellipse(this.pos.x, this.r/(1-this.m), 5, 5);



  if(this.m<1){
    //point A
    noFill();
    arc(0, this.r/(1-this.m), 2*this.r/(1-this.m), 2*this.r/(1-this.m),  HALF_PI+PI, HALF_PI+PI+this.angle);

    for(var t=0; t<=this.nradios; t++)
    {


      this.x_1=this.r*Math.sin(t*(TWO_PI/this.nradios)*(1-this.m))/(1-this.m);
      this.x_2=this.r*(1-Math.cos(t*(TWO_PI/this.nradios)*(1-this.m)))/(1-this.m);

      //point B
      ellipse(this.x_1, this.x_2, 5, 5);
      //radio
      line(this.pos.x, this.r/(1-this.m), this.x_1, this.x_2);
      //arco
    }

    }else{
      line(0, 0, TWO_PI*this.r, 0);

    }
  pop();

 }

 }
}