Luiz Zanotello
Published © CC BY-NC

Nama

A textile interface for controlling generative audiovisual output.

BeginnerShowcase (no instructions)6,947
Nama

Things used in this project

Story

Read more

Schematics

Materials and components

Design

How to sew the components

Fritzing Schematic

Code

Arduino Code

Arduino
///////////////////////////////////////////////////////////////
// Nama Project v1.0
// Instrument Arduino code.
// ------------------------------------------------------------
// Created by Luiz Zanotello on May/2012.
// Under Creative Commons License: CC BY-NC-SA 3.0.
// More information at: http://www.viraseres.com/nama
///////////////////////////////////////////////////////////////

//Accelerometer 1 (z) - bottom right
////////////////////////////////////
const int a1pinV = 6; //output current pin
const int a1pinZ = A0; //data pin

//Accelerometer 2 (z) - top right
/////////////////////////////////
const int a2pinV = 7; //output current pi
const int a2pinZ = A1; //data pin

//Central accelerometer (x/y)
/////////////////////////////
const int aCpinV = 8; //output current pi
const int aCpinX = A2; //data pin
const int aCpinY = A3; //data pin
//Accelerometer 3 (z) - top left
////////////////////////////////
const int a3pinV = 2; //output current pi
const int a3pinZ = A4; //data pin

//Accelerometer 4 (z) - bottom left
///////////////////////////////////
const int a4pinV = 3; //output current pi
const int a4pinZ = A5; //data pin

////////////////////////////////////////////////////////

void setup()
{
  //Begin serial transmition:
  Serial.begin(115200);
  //Set output pins (+ current):
  pinMode(a1pinV, OUTPUT);
  digitalWrite(a1pinV, HIGH);
  pinMode(a2pinV, OUTPUT); 
  digitalWrite(a2pinV, HIGH);
  pinMode(aCpinV, OUTPUT); 
  digitalWrite(aCpinV, HIGH);
  pinMode(a3pinV, OUTPUT); 
  digitalWrite(a3pinV, HIGH);
  pinMode(a4pinV, OUTPUT); 
  digitalWrite(a4pinV, HIGH);
}

////////////////////////////////////////////////////////

void loop()
{ 
  // Printing analog data received from accelerometers
  Serial.println();
  Serial.print(5); // Number of accelerometers
  Serial.print(',');
  Serial.print(analogRead(a1pinZ)); // Accelerometer 1 reading (z)
  Serial.print(',');
  Serial.print(analogRead(a2pinZ)); // Accelerometer 2 reading (z)
  Serial.print(',');
  Serial.print(analogRead(a3pinZ)); // Accelerometer 3 reading (z)
  Serial.print(',');
  Serial.print(analogRead(a4pinZ)); // Accelerometer 4 reading (z)
  Serial.print(',');
  Serial.print(analogRead(aCpinX)); // Central accelerometer reading (x)
  Serial.print(',');
  Serial.print(analogRead(aCpinY)); // Central accelerometer reading (y)
  Serial.print(tranG);
  delay(10);
}

////////////////////////////////////////////////////////

Processing Code

Java
A sample code of how to link the Instrument to Processing, with a simple visualization software.
//////////////////////////////////////////////////////////////////////////////////////////////
// Nama Project v1.0
// Instrument data receiver.
// -------------------------------------------------------------------------------------------
// All codes created by Luiz Zanotello on May/2012.
// Under Creative Commons License: CC BY-NC-SA 3.0.
// More information at: http://www.viraseres.com/nama
//////////////////////////////////////////////////////////////////////////////////////////////

// General configuration
int baudRate = 115200; //Baud rate 
int serialPort = 0; //Serial port
boolean isPrinting = true; //Print to post?

// Libraries
import processing.serial.*;
import processing.opengl.*;

//////////////////////////////////////////////////////////////////////////////////////////////

void setup() {
  size(1200, 600, OPENGL);
  beginSerial();
  setupFonts();
  smooth();
}

void draw() {
  background(230, 230, 212);
  visualizeData();
  if (isPrinting==true) {
    printValues();
  }
}

void printValues() {
  println("AC NAME / RAW / FILTERED / DIFFERENCE / FILTERED DIFFERENCE");
  println("A1(z): "+acData[1][0]+" / "+acData[1][1]+" / "+acData[1][2]+" / "+acData[1][3]);
  println("A2(z): "+acData[2][0]+" / "+acData[2][1]+" / "+acData[2][2]+" / "+acData[2][3]);
  println("A3(z): "+acData[3][0]+" / "+acData[3][1]+" / "+acData[3][2]+" / "+acData[3][3]);
  println("A4(z): "+acData[4][0]+" / "+acData[4][1]+" / "+acData[4][2]+" / "+acData[4][3]);
  println("A5(x): "+acData[5][0]+" / "+acData[5][1]+" / "+acData[5][2]+" / "+acData[5][3]);
  println("A5(y): "+acData[6][0]+" / "+acData[6][1]+" / "+acData[6][2]+" / "+acData[6][3]);
  println("AV(z): "+acData[0][0]+" / "+acData[0][1]+" / "+acData[0][2]+" / "+acData[0][3]);
  println();
}

//////////////////////////////////////////////////////////////////////////////////////////////
// Kalman Filter Class.
// Created by Interactive Matter.
// (http://github.com/interactive-matter/Processing/tree/master/lis302dl_kalman)
// -------------------------------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////////////////////////

public class KalmanFilter {
  private float q;
  private float r;
  private float x;
  private float p;
  private float k;
  public KalmanFilter(float q, float r, float p, float initial_value) {
    this.q=q;
    this.r=r;
    this.p=p;
    this.x=initial_value;
  }
  public float addSample(float measurement) {
    //omit x=x
    p=p+q;
    k=p/(p+r);
    x= x + k*(measurement-x);
    p=(1-k)*p;
    return x;
  }
  public String toString() {
    return "KalmanFilter with p="+p+", k="+k;
  }
  public float getQ() {
    return q;
  }
  public float getR() {
    return r;
  }
  public float getP() {
    return p;
  }
  public float getK() {
    return k;
  }
}

//////////////////////////////////////////////////////////////////////////////////////////////
// Serial communication and configuration to the Lilypad accelerometer soft circuit.
// -------------------------------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////////////////////////

// Use the following variables to calibrate the readings from the accelerometers.
// Change the "low" and "high" values of each accelerometer
// according to the average lowest and highest values received.

int a1lowZ = 307;
int a1highZ = 759;
int a2lowZ = 294;
int a2highZ = 727;
int a3lowZ = 281;
int a3highZ = 711;
int a4lowZ = 286;
int a4highZ = 717;
int a5lowX = 303;
int a5highX = 769;
int a5lowY = 298;
int a5highY = 747;

//////////////////////////////////////////////////////////////////////////////////////////////

// Mapping variables
Serial myPort;
float acData[][] = new float[7][4]; // [raw, filt, dif, dif filt] [average z, 1, 2, 3, 4, 5x, 5y, 5z]
float acDataOld[] = new float[7]; // used for calculating the differences
float newValue, newValueFiltered, masterAverage, newDif, rawDif; //mapping

// Kalman filter instances
// q(process noise), r(sensor noise), p(estimated error), initial_value

KalmanFilter filterDif0 = new KalmanFilter(0.1, 16, 0.1, 1);
KalmanFilter filterDif1 = new KalmanFilter(0.1, 16, 0.1, 1);
KalmanFilter filterDif2 = new KalmanFilter(0.1, 16, 0.1, 1);
KalmanFilter filterDif3 = new KalmanFilter(0.1, 16, 0.1, 1);
KalmanFilter filterDif4 = new KalmanFilter(0.1, 16, 0.1, 1);
KalmanFilter filterDif5 = new KalmanFilter(0.1, 16, 0.1, 1);
KalmanFilter filterDif6 = new KalmanFilter(0.1, 16, 0.1, 1);
KalmanFilter filterDif7 = new KalmanFilter(0.1, 16, 0.1, 1);
KalmanFilter filterA1 = new KalmanFilter(0.5, 256, 100, 510);
KalmanFilter filterA2 = new KalmanFilter(0.5, 256, 100, 510);
KalmanFilter filterA3 = new KalmanFilter(0.5, 256, 100, 510);
KalmanFilter filterA4 = new KalmanFilter(0.5, 256, 100, 510);
KalmanFilter filterA5X = new KalmanFilter(0.5, 256, 100, 510);
KalmanFilter filterA5Y = new KalmanFilter(0.5, 256, 100, 510);
KalmanFilter filterMaster = new KalmanFilter(0.5, 256, 100, 510);

//////////////////////////////////////////////////////////////////////////////////////////////

// Serial Event
void beginSerial() {
  myPort = new Serial(this, Serial.list()[serialPort], baudRate);
  myPort.bufferUntil(10);
}

// Map Data
void serialEvent(Serial p) {
  String inString;
  try {
    inString = (myPort.readString());
    if (inString != null) {
      String[] listSt = split(inString, ',');     
      if (listSt.length == 7) {
        masterAverage = 0;

        //////////////////////
        // Accelerometer 1 - Z
        newValue = float(listSt[1]);
        if (Float.isNaN(newValue)) {
          newValue = acData[1][0];
        }
        acData[1][0] = newValue;
        masterAverage = masterAverage+newValue;
        newValueFiltered = constrain(map(filterA1.addSample(newValue), a1lowZ, a1highZ, 0.01, 1.0), 0.001, 2.0);
        if (Float.isNaN(newValueFiltered)) {
          newValueFiltered = acData[1][1];
        }
        acData[1][1] = newValueFiltered;

        //////////////////////
        // Accelerometer 2 - Z
        newValue = float(listSt[2]);
        if (Float.isNaN(newValue)) {
          newValue = acData[2][0];
        }
        acData[2][0] = newValue;
        masterAverage = masterAverage+newValue;
        newValueFiltered = constrain(map(filterA2.addSample(newValue), a2lowZ, a2highZ, 0.01, 1.0), 0.001, 2.0);
        if (Float.isNaN(newValueFiltered)) {
          newValueFiltered = acData[2][1];
        }
        acData[2][1] = newValueFiltered;

        //////////////////////
        // Accelerometer 3 - Z
        newValue = float(listSt[3]);
        if (Float.isNaN(newValue)) {
          newValue = acData[3][0];
        }
        acData[3][0] = newValue;
        masterAverage = masterAverage+newValue;
        newValueFiltered = constrain(map(filterA3.addSample(newValue), a3lowZ, a3highZ, 0.01, 1.0), 0.001, 2.0);
        if (Float.isNaN(newValueFiltered)) {
          newValueFiltered = acData[3][1];
        }
        acData[3][1] = newValueFiltered;

        //////////////////////
        // Accelerometer 4 - Z
        newValue = float(listSt[4]);
        if (Float.isNaN(newValue)) {
          newValue = acData[4][0];
        }
        acData[4][0] = newValue;
        masterAverage = masterAverage+newValue;
        newValueFiltered = constrain(map(filterA4.addSample(newValue), a4lowZ, a4highZ, 0.01, 1.0), 0.001, 2.0);
        if (Float.isNaN(newValueFiltered)) {
          newValueFiltered = acData[4][1];
        }
        acData[4][1] = newValueFiltered;

        //////////////////////
        // Accelerometer C - X
        newValue = float(listSt[5]);
        if (Float.isNaN(newValue)) {
          newValue = acData[5][0];
        }
        acData[5][0] = newValue;
        newValueFiltered = constrain(map(filterA5X.addSample(newValue), a5lowX, a5highX, 0.01, 1.0), 0.001, 2.0);
        if (Float.isNaN(newValueFiltered)) {
          newValueFiltered = acData[5][1];
        }
        acData[5][1] = newValueFiltered;

        //////////////////////
        // Accelerometer C - Y
        newValue = float(listSt[6]);
        if (Float.isNaN(newValue)) {
          newValue =acData[6][0];
        }
        acData[6][0] = newValue;
        newValueFiltered = constrain(map(filterA5Y.addSample(newValue), a5lowY, a5highY, 0.01, 1.0), 0.001, 2.0);
        if (Float.isNaN(newValueFiltered)) {
          newValueFiltered = acData[6][1];
        }
        acData[6][1] = newValueFiltered;

        //////////////////////
        // Master average calc
        newValue = masterAverage/4;
        if (Float.isNaN(newValue)) {
          newValue = acData[0][0];
        }
        acData[0][0] = newValue;
        newValueFiltered = constrain(map(filterMaster.addSample(newValue), (a4lowZ+a3lowZ+a2lowZ+a1lowZ)*0.25, (a4highZ+a3highZ+a2highZ+a1highZ)*0.25, 0.01, 1.0), 0.001, 2.0);
        if (Float.isNaN(newValueFiltered)) {
          newValueFiltered = acData[0][1];
        }
        acData[0][1] = newValueFiltered;
        setDifferences();
      }
    }
  }
  catch(Exception e) {
    println(e);
  }
}

// Set differences
void setDifferences() {
  for (int i=0;i<acData.length;i++) {
    acData[i][2] =  acData[i][1]-acDataOld[i];
    if (i==0) {
      acData[i][3] = filterDif0.addSample(acData[i][2]);
    }
    if (i==1) {
      acData[i][3] = filterDif1.addSample(acData[i][2]);
    }
    if (i==2) {
      acData[i][3] = filterDif2.addSample(acData[i][2]);
    }
    if (i==3) {
      acData[i][3] = filterDif3.addSample(acData[i][2]);
    }
    if (i==4) {
      acData[i][3] = filterDif4.addSample(acData[i][2]);
    }
    if (i==5) {
      acData[i][3] = filterDif5.addSample(acData[i][2]);
    }
    if (i==6) {
      acData[i][3] = filterDif6.addSample(acData[i][2]);
    }
    if (Float.isNaN(acData[i][3])) {
      acData[i][3] = acDataOld[i];
    }
    if (acData[i][3]<0.0001) {
      acData[i][3]=0;
    }
    if (acData[i][2]<0.0001) {
      acData[i][2]=0;
    }
    acDataOld[i] = acData[i][1];
  }
}

//////////////////////////////////////////////////////////////////////////////////////////////
// Graphical visualization of the interface data.
// -------------------------------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////////////////////////

// Config
int interfaceDimensionX = 128;
int interfaceDimensionY = 134;

// Mapping variables
PFont titleFont, contentFont, copyrightFont;
float goPoint[][] = new float[5][3];
float baseX, baseY;

void setupFonts() {
  titleFont = loadFont("ArialNarrow-23.vlw");
  contentFont = loadFont("Courier-15.vlw");
  copyrightFont = loadFont("Courier-9.vlw");
}

void visualizeData() {
  // Bg  
  fill(35, 41, 44);
  rect(-2, -2, 380, height+4);
  
  // Base position of text
  baseX = 60;
  baseY = 214;

  // Title
  textFont(titleFont);
  fill(230, 230, 212);
  text("Nama Instrument v1.0", baseX-6, baseY-60); 
  textFont(contentFont);
  fill(230, 230, 212);
  text("", baseX-6, baseY-28);
  textFont(copyrightFont);
  text("ACC:       RAW  FILTER  DIF  DIF+FILTER", baseX-6, baseY);
  
  // 1
  baseY = baseY+34;
  textFont(titleFont);
  fill(215, 102, 124);
  text("A1z: ", baseX-6, baseY); 
  fill(230, 230, 212);
  textFont(contentFont);
  text(int(acData[1][0]), baseX+48, baseY-3);
  text("", baseX+79, baseY-3);
  text(acData[1][1], baseX+84, baseY-3);
  text("", baseX+142, baseY-3);
  text(acData[1][2], baseX+147, baseY-3);
  text("", baseX+205, baseY-3);
  text(acData[1][3], baseX+210, baseY-3);

  // 2
  baseY = baseY+34;
  textFont(titleFont);
  fill(215, 181, 102);
  text("A2z: ", baseX-6, baseY); 
  fill(230, 230, 212);
  textFont(contentFont);
  text(int(acData[2][0]), baseX+48, baseY-3);
  text("", baseX+79, baseY-3);
  text(acData[2][1], baseX+84, baseY-3);
  text("", baseX+142, baseY-3);
  text(acData[2][2], baseX+147, baseY-3);
  text("", baseX+205, baseY-3);
  text(acData[2][3], baseX+210, baseY-3);

  // 3
  baseY = baseY+34;
  textFont(titleFont);
  fill(102, 215, 161);
  text("A3z: ", baseX-6, baseY); 
  fill(230, 230, 212);
  textFont(contentFont);
  text(int(acData[3][0]), baseX+48, baseY-3);
  text("", baseX+79, baseY-3);
  text(acData[3][1], baseX+84, baseY-3);
  text("", baseX+142, baseY-3);
  text(acData[3][2], baseX+147, baseY-3);
  text("", baseX+205, baseY-3);
  text(acData[3][3], baseX+210, baseY-3);

  // 4
  baseY = baseY+34;
  textFont(titleFont);
  fill(102, 130, 215);
  text("A4z: ", baseX-6, baseY); 
  fill(230, 230, 212);
  textFont(contentFont);
  text(int(acData[4][0]), baseX+48, baseY-3);
  text("", baseX+79, baseY-3);
  text(acData[4][1], baseX+84, baseY-3);
  text("", baseX+142, baseY-3);
  text(acData[4][2], baseX+147, baseY-3);
  text("", baseX+205, baseY-3);
  text(acData[4][3], baseX+210, baseY-3);

  // 5x
  baseY = baseY+34;
  textFont(titleFont);
  fill(139, 137, 127);
  text("A5x: ", baseX-6, baseY); 
  fill(230, 230, 212);
  textFont(contentFont);
  text(int(acData[5][0]), baseX+48, baseY-3);
  text("", baseX+79, baseY-3);
  text(acData[5][1], baseX+84, baseY-3);
  text("", baseX+142, baseY-3);
  text(acData[5][2], baseX+147, baseY-3);
  text("", baseX+205, baseY-3);
  text(acData[5][3], baseX+210, baseY-3);

  // 5y
  baseY = baseY+34;
  textFont(titleFont);
  fill(139, 137, 127);
  text("A5y: ", baseX-6, baseY); 
  fill(230, 230, 212);
  textFont(contentFont);
  text(int(acData[6][0]), baseX+48, baseY-3);
  text("", baseX+79, baseY-3);
  text(acData[6][1], baseX+84, baseY-3);
  text("", baseX+142, baseY-3);
  text(acData[6][2], baseX+147, baseY-3);
  text("", baseX+205, baseY-3);
  text(acData[6][3], baseX+210, baseY-3);

  // separador
  baseY = baseY+34;
  textFont(contentFont);
  fill(230, 230, 212);
  text("", baseX-6, baseY-3);

  // Average
  baseY = baseY+34;
  textFont(titleFont);
  fill(230, 230, 212);
  text("AVz: ", baseX-6, baseY); 
  fill(230, 230, 212);
  textFont(contentFont);
  text(int(acData[0][0]), baseX+48, baseY-3);
  text("", baseX+79, baseY-3);
  text(acData[0][1], baseX+84, baseY-3);
  text("", baseX+142, baseY-3);
  text(acData[0][2], baseX+147, baseY-3);
  text("", baseX+205, baseY-3);
  text(acData[0][3], baseX+210, baseY-3);
  
  // Credits
  textFont(copyrightFont);
  fill(35, 41, 44);
  text("created by luiz gustavo zanotello at jun/2012", width-233, height-30);
  text("http://www.viraseres.com/nama", width-154, height-20);
  text("under CC BY-NC-SA 3.0", width-114, height-10);

  ////////////////////////////////////////////////////////////////////////////////////////////
  // Graph
  noStroke();
  fill(255);
  baseX = width-width/3+map(acData[5][1], 0, 1, -1, 1)*100;
  baseY = height/2+map(acData[6][1], 0, 1, -1, 1)*100+map(acData[0][1], 0, 1, -1, 1)*50;
  translate(baseX, baseY, map(acData[0][1], 0, 1, 50, -100));
  perspective();
  rotateX(radians(acData[5][1]*360));
  rotateY(radians(acData[6][1]*360));
  rotateZ(radians(acData[0][1]*360));
  directionalLight(120, 120, 120, 0, -1, -1);
  ambientLight(230, 230, 230);

  // 4
  fill(142-(acData[2][3]*255*100), 170-(acData[2][3]*255*100), 255-(acData[2][3]*255*100), 90+(acData[4][3]*255*100));
  goPoint[4][0] = 0-interfaceDimensionX+map(acData[4][1], 0, 1, -100, 100);
  goPoint[4][1] = interfaceDimensionY/2+map(acData[4][1], 0, 1, -100, 100);
  goPoint[4][2] = map(acData[4][1], 0, 1, -400, 400);
  pushMatrix();
  translate(goPoint[4][0], goPoint[4][1], goPoint[4][2]);
  sphere(6);
  popMatrix();

  // 3
  fill(142-(acData[1][3]*255*100), 255-(acData[1][3]*255*100), 201-(acData[1][3]*255*100), 90+(acData[4][3]*255*100));
  goPoint[3][0] = 0-interfaceDimensionX+map(acData[3][1], 0, 1, -100, 100);
  goPoint[3][1] = 0-interfaceDimensionY/2+map(acData[3][1], 0, 1, -100, 100);
  goPoint[3][2] = map(acData[3][1], 0, 1, -400, 400);
  pushMatrix();
  translate(goPoint[3][0], goPoint[3][1], goPoint[3][2]);
  sphere(6);
  popMatrix();

  // 2
  fill(255-(acData[3][3]*255*100), 221-(acData[3][3]*255*100), 142-(acData[3][3]*255*100), 90+(acData[4][3]*255*100));
  goPoint[2][0] = interfaceDimensionX+map(acData[2][1], 0, 1, -100, 100);
  goPoint[2][1] = 0-interfaceDimensionY/2+map(acData[2][1], 0, 1, -100, 100);
  goPoint[2][2] = map(acData[2][1], 0, 1, -400, 400);
  pushMatrix();
  translate(goPoint[2][0], goPoint[2][1], goPoint[2][2]);
  sphere(6);
  popMatrix();

  // 1
  fill(255-(acData[4][3]*255*100), 142-(acData[4][3]*255*100), 164-(acData[4][3]*255*100), 90+(acData[4][3]*255*100));
  goPoint[1][0] = interfaceDimensionX+map(acData[1][1], 0, 1, -100, 100);
  goPoint[1][1] = interfaceDimensionY/2+map(acData[1][1], 0, 1, -100, 100);
  goPoint[1][2] = map(acData[1][1], 0, 1, -400, 400);
  pushMatrix();
  translate(goPoint[1][0], goPoint[1][1], goPoint[1][2]);
  sphere(6);
  popMatrix();

  // C
  fill(179-(acData[5][3]*255*100)-(acData[6][3]*255*100), 177-(acData[5][3]*255*100)-(acData[6][3]*255*100), 167-(acData[5][3]*255*100)-(acData[6][3]*255*100), 90+(acData[5][3]*255*100)+(acData[6][3]*255*100));
  goPoint[0][0] = map(acData[5][1], 0, 1, -400, 400);
  goPoint[0][1] = map(acData[6][1], 0, 1, -400, 400);
  goPoint[0][2] = map(acData[0][1], 0, 1, -100, 100);
  pushMatrix();
  translate(goPoint[0][0], goPoint[0][1], goPoint[0][2]);
  sphere(6);
  popMatrix();

  // Lines
  stroke(255);
  line(goPoint[0][0], goPoint[0][1], goPoint[0][2], goPoint[1][0], goPoint[1][1], goPoint[1][2]);
  line(goPoint[0][0], goPoint[0][1], goPoint[0][2], goPoint[2][0], goPoint[2][1], goPoint[2][2]);
  line(goPoint[0][0], goPoint[0][1], goPoint[0][2], goPoint[3][0], goPoint[3][1], goPoint[3][2]);
  line(goPoint[0][0], goPoint[0][1], goPoint[0][2], goPoint[4][0], goPoint[4][1], goPoint[4][2]);
  line(goPoint[1][0], goPoint[1][1], goPoint[1][2], goPoint[2][0], goPoint[2][1], goPoint[2][2]);
  line(goPoint[1][0], goPoint[1][1], goPoint[1][2], goPoint[3][0], goPoint[3][1], goPoint[3][2]);
  line(goPoint[1][0], goPoint[1][1], goPoint[1][2], goPoint[4][0], goPoint[4][1], goPoint[4][2]);
  line(goPoint[2][0], goPoint[2][1], goPoint[2][2], goPoint[3][0], goPoint[3][1], goPoint[3][2]);
  line(goPoint[2][0], goPoint[2][1], goPoint[2][2], goPoint[4][0], goPoint[4][1], goPoint[4][2]);
  line(goPoint[3][0], goPoint[3][1], goPoint[3][2], goPoint[4][0], goPoint[4][1], goPoint[4][2]);
}

Credits

Luiz Zanotello

Luiz Zanotello

3 projects • 17 followers

Comments