How to show a flag for 16 milliseconds

edited March 2018 in Questions about Code

Hello.

I'm trying to make an experiment where I need to show a national flag for only 16 or 18 milliseconds, inmmediately followed by a 300 milliseconds mask. I thought that millis() was a good solution. But it seems to me that sometimes the flag is not showed. I don't know why. Maybe I have to change the frame rate, or millis() is not reliable. Dou you have any idea about why that happens? Thank you very much.

This is the code:

Flag Spain;
Mask mask;

PImage img;

float scale;

void setup() {
  fullScreen();
  noStroke();
  scale = displayHeight/10;
  Spain = new Flag();
  mask = new Mask();

  img = loadImage ("pic4.png");

  frameRate(64);
}

void draw() {
  background (255, 234, 150);

  Spain.showFlag();
  mask.showMask();

}

void keyPressed() {
   if (key == 'e' || key == 'E') {
    exit();
  }
}

class Flag {

  void showFlag() {
    if(millis() > 2000 && millis() <=2100) {
      rectMode(CENTER);
    colorMode(RGB, 255, 255, 255);
    fill(255, 0, 17);
    rect(displayWidth/2, (displayHeight/2)-33, 134, 22);
    rect(displayWidth/2, (displayHeight/2)+33, 134, 22);
    fill(255, 234, 0);
    rect(displayWidth/2, (displayHeight/2), 134, 44);
    }
  }
}

class Mask {
 void showMask() {
   if (millis() > 2016 && millis() < 2316) {
   imageMode(CENTER);
   image(img,displayWidth/2, displayHeight/2, 134, 88);
  } 
 }
}
Tagged:

Answers

  • 16 milliseconds is just a single frame. Depending on the exact timing of a frame, 16 milliseconds might elapse between two frames.

    If you really need that exact timing, use the frameCount variable instead. But note that a single frame is going to be pretty hard to see.

  • Thanks KevinWorkman.frameCountworks very well. In fact, it would be perfect if almost nobody can see the frame.

  • edited March 2018

    Finally I could not use frameCount. I needed to create a experiment where people has to answer questions after subliminal exposure to some simbols (a national flag, for example). The subliminal image is masked by another one. In order to assure that people is looking at the screen, they have to anticipate where the next question will appear and press up or down keys according to it. After that, they have to answer the question. It was very difficult to combine all that things. I had to use a trick to show the flag only for 16 milliseconds: I used millis() and pictures to make the animation. Maybe you can find a more nice and elegant solution. My code:

    subliminalFlag.pde

    /**
     *Roberto Losada Maestre
     *subliminal Flag (v1.02)
     *25/03/2018
     */
    
    //=================bienvenida.txt================
    /*
    Bienvenido
    Por favor, antes de comenzar la prueba asegúrese de que ha entendido bien las instrucciones.
    Recuerde: antes de que aparezca la pregunta se le ofrecerá, durante un breve instante, una pista sobre el lugar de la pantalla en que la se mostrará (arriba o abajo).
    Una vez que haya visto la pista, indique con las flechas lo más rápido que pueda, donde cree que aparecerá la pregunta para poder verla y responder a la misma.
    Responda con sinceridad a las preguntas que se le plantean.
    Cuando acabe el test se le indicará.
    Todos los datos son confidenciales y se recopilan sólo con el objeto de realizar la investigación.
    Gracias por participar.
    */
    //================cuestionario.txt================
    /*
    España debería negociar la independencia de Cataluña con la Generalitat
    Apoyaría el veto de España a la entrada en la Unión Europea de un hipotético Estado catalán
    Apoyaría la construcción de un Estado catalán independiente
    Está de acuerdo con las organizaciones que están promoviendo un boicot a productos de origen catalán
    El Gobierno central debería dejar de aportar una fuente de financiación extra a Cataluña a través del Fondo de Liquidez Autonómica
    Las federaciones internacionales deportivas no deberían permitir la inscripción de selecciones regionales o autonómicas como la catalana
    Los jugadores de clubes de fútbol catalanes no deberían jugar en la Selección Española de Fútbol
    Son adecuadas las acciones judiciales emprendidas por el Tribunal Supremo contra los promotores y organizadores del referéndum del 1-O
    Los responsables del referéndum del 1-O deberían ser condenados a penas de prisión
    Los independentistas catalanes una amenaza para la democracia española
    */
    //==========================================
    
    import java.util.Calendar;
    
    Estimulos primes;
    
    Table tabla;
    
    int FPS = 60;
    int LAPSO = 0;
    int timer = 2000;
    int modo = 0;
    int contador = 0;
    int azaroso;
    int posPreg;
    int posPrime;
    int inicio = 0;
    
    boolean a = true;
    boolean controlTecla = false;
    boolean controlFlecha = false;
    boolean controlInicio = false;
    boolean controlFin = false;
    
    PFont fuenteB; //Negrita
    PFont fuenteR; //Regular
    
    String[] bienvenida1;
    String bienvenida2 = "";
    String texto;
    String[] preguntas;
    StringList cuestiones;
    StringList respuestas;
    String[] ArchivoPreguntas;
    
    int numeroFrames = 27; //número de imágenes de la animación
    PImage[] estimulos = new PImage[numeroFrames];
    
    void setup() {
    
      fullScreen();
      //size (600, 500);
      noStroke();
      rectMode(CENTER);
      imageMode(CENTER);
    
    //===============INITIALIZES VARIABLES==================
    
    
      for (int i = 0; i<estimulos.length; i ++) {
       String nombreMascara = "mascara" + nf(i, 5) + ".png";
       estimulos[i] = loadImage(nombreMascara);
      } 
    
      tabla = new Table();
      tabla.addColumn("Pregunta", Table.STRING);
      tabla.addColumn("Respuesta", Table.STRING);
    
      fuenteB = createFont("Montserrat-Bold.ttf", 30);
      fuenteR = createFont("Montserrat-Regular.ttf", 20);
    
    
      respuestas = new StringList();
    
      primes = new Estimulos();
    
      bienvenida1 = loadStrings("bienvenida.txt");
    
      preguntas = loadStrings("cuestionario.txt");
      cuestiones = new StringList();
    
    //=========SHUFFLES THE QUESTIONS OF THE TEST========
    
      for (int i = 0; i < preguntas.length; i ++) {
       cuestiones.append(preguntas[i]);
       cuestiones.shuffle(this);
      }
      ArchivoPreguntas = cuestiones.array();
    
    //===================CONTROLS SPEED=============
      frameRate(FPS);
    }
    
    //===========================================
    
    void draw() {
      background (#5085b6);
      textFont(fuenteB);
      textSize(22);
      fill(#fff4de);
      textAlign(CENTER);
    
      //int randomTime = int(random(1, 2))*1000;// Por si se quiere que el tiempo varíe entre preguntas
    
      posPrime = displayHeight/2;
    
      posPreg = azaroso;
      if(posPreg != 0) {
          posPreg = 200;
        }else{
          posPreg = height - 100;
        }
    
    //========SHOWS ALL THE STUFF IN THE SCREEN==========
    
    if (modo == 0 && a == true) {
      timer = 2000;
      if (contador == 0) mostrarBienvenida();
      controlInicio = true;
      noLoop();
    }else if (modo == 1) {
      controlInicio = false;
      timer = 440; //tiempo necesario para mostrar 16 milisegundos la bandera
    }else if (modo == 2 && contador <= 10) {
      timer = 0;
      primes.mostrarTodo();
      controlFlecha = true;
      controlTecla = false;
    }else if (modo == 3) {
      timer = 0;
      noLoop();
    }else if (modo == 4) {
      mostrarPregunta();
      controlFlecha = false;
      if (contador < 10) controlTecla = true;
      contador ++;
      noLoop();
      timer = 1000; //Aquí puede usarse randomTime
    }
    
    //=========CONTROLS SKETCH EVOLUTION==============
    
     if(millis() > LAPSO) {
       modo ++;
     if (modo > 4) {
       modo = 0;
     }
     LAPSO = millis()+ timer;
      }
    }
    
    //================SHOWS QUESTIONS===============
    
    void mostrarPregunta() {
      int i = 0;
      if (i < cuestiones.size()) {
      text(cuestiones.get(i), width/2, posPreg, 800, 200);
      textFont(fuenteR);
      text("Por favor, indique de 1 a 9 su grado de acuerdo con esta afirmación."
            + "\n" + "Recuerde que 1 indica un total desacuerdo y 9 un total acuerdo.",
            width/2, height/2, 800, 200);
      cuestiones.remove(i);
      i ++;
      }
      else{
        controlFin = true;
        controlTecla = false;
        text("Ha finalizado la prubea" + "\n" + "Gracias por su colaboración" + "\n" +
        "Pulse F para finalizar", width/2, height/2);
      }
    }
    
    //=============SHOWS WELCOME MESSAGE=========
    
    void mostrarBienvenida() {
          for (int i = 0; i < bienvenida1.length; i ++) {
          bienvenida2 += bienvenida1[i]+"\n";
          }
          textSize(18);
          textLeading(25);
          text(bienvenida2, width/2, 400, 900, 600);
          textFont(fuenteR);
          text("Por favor, pulse C para comenzar", width/2, height-100);
          } 
    
    //===========SAVE ANSWERS IN .CSV FILE============
    
    void guardarDatos() {
      String[] ArchivoRespuestas = respuestas.array();
      for (int i = 0; i < ArchivoRespuestas.length; i ++) {
      TableRow fila = tabla.addRow();
      fila.setString("Pregunta", ArchivoPreguntas[i]);
      fila.setString("Respuesta", ArchivoRespuestas[i]);
      }
      saveTable(tabla, "data/" + estampaTiempo() + ".csv");
    }
    
    //==================KEYS===================
    
    void keyPressed() {
    
      if ((key == 'c' || key == 'C') && controlInicio == true) {
         if(looping) noLoop();
         else loop();
         a = false;
         inicio = 0;
         controlInicio = false;
      }
    
      if (key == '1' && controlTecla == true){
        respuestas.append("1");
        inicio = 0;
        loop();
      }
      if (key == '2' && controlTecla == true) {
        respuestas.append("2");
        inicio = 0;
        loop();
      }
      if (key == '3' && controlTecla == true) {
        respuestas.append("3");
        inicio = 0;
        loop();
      }
      if (key == '4' && controlTecla == true) {
        respuestas.append("4");
        inicio = 0;
        loop();
      }
      if (key == '5' && controlTecla == true) {
        respuestas.append("5");
        inicio = 0;
        loop();
      }
      if (key == '6' && controlTecla == true) {
        respuestas.append("6");
        inicio = 0;
        loop();
      }
      if (key == '7' && controlTecla == true) {
        respuestas.append("7");
        inicio = 0;
        loop();
      }
      if (key == '8' && controlTecla == true) {
        respuestas.append("8");
        inicio = 0;
        loop();
      }
      if (key == '9' && controlTecla == true) {
        respuestas.append("9");
        inicio = 0;
        loop();
      }   
    
      if (key == CODED) {
        if (keyCode == UP || keyCode == DOWN) { 
        if (controlFlecha == true) {
        if(looping) noLoop();
         else loop();
         }
        }
      }
    
       if (key == 'e' || key == 'E') {
          exit();
      }
    }
    
    //==RANDOMIZES WHERE THE QUESTION APPEARS (UP OR DOWN)====
    
    void keyReleased() {
      if (key == 'c' || key == 'C' || key == '1' || key == '2' ||
          key == '3' || key == '4' || key == '5' || key == '6' ||
          key == '7' || key == '8' || key == '9') {
        azaroso = int(random(1, 10))%2;
      }
    
      if (key == 'f' || key == 'F') {
        guardarDatos();
        exit();
      }
    }
    
    //======NAME OF THE FILE WITH THE ANSWERS===============
    
    String estampaTiempo() {
     Calendar now = Calendar.getInstance();
     return String.format("%1$ty%1$tm%1$td_%1$tH%1$tM%1$tS", now);
    }
    

    Flag2.pde

    class Estimulos {
      void mostrarTodo() {
      if (inicio < estimulos.length) {
      if (azaroso != 0) {
      image(estimulos[inicio], displayWidth/2, displayHeight/2, 134, 98);
      inicio ++;
        } else {
       pushMatrix();
       translate(displayWidth/2, displayHeight/2);
       rotate(PI);
       image(estimulos[inicio], 0, 0, 134, 98);
       popMatrix();
       inicio ++;
          }
        }
      }
    }
    
  • Just to warn you -- this is not a very reliable setup at all, because the sketch will do its best to display each frame as close to the rate as it can (or later if it must) -- over time it will drift off the clock, and even other applications running on the same computer could cause frame lags.

    If you really need to simulate a 16ms "tachistoscope" window for the purposes of a perception study then you need a computer and a display that can refresh the screen much faster than 60fps -- for example, a 120fps sketch running on a 120Hz monitor would be better.

    Also, your display matters (for example -- screen tearing or afterimage could affect your results significantly).

  • Thank you jeremydouglass. That's very kind of you. I will take your warning into account.

Sign In or Register to comment.