Um append simples com vetores bidimensionais

Alguns estudantes têm perguntado como fazer um append — fazer crescer um vetor — quando usamos variáveis compostas/complexas. Em aulas passadas temos feito algumas operações sobre vetores (arrays), tanto em valores simples, como em strings. Os métodos são semelhantes e parecem eficientes. Para aqueles que estudaram a referência online, já viram que podemos utilizar métodos específicos para crescer ou adicionar valores as listas simples. Mas, na aula passada, usamos vetores bidimensionais.

Aqui ficam dois truques para manipularem este tipo de objetos.

int[][] nums;
int n;

void setup() {
  size(500, 500);
  n = 10;
  nums = new int[n][3];
  for (int i = 0; i < n; i++) {
    nums[i][0] = int( random(width) );
    nums[i][1] = int( random(height) );
    nums[i][2] = int( random(20, 50) );
  }
}

void draw() {
  background(255);
  //udapte
  for (int i = 0; i < n; i ++) {
    nums[i][1] = nums[i][1] + nums[i][2]/20;
    if (nums[i][1] > height) {
      nums[i][1] = 0;
    }
  }

  //display
  for (int i = 0; i < n; i ++) {
    radar(nums[i][0], nums[i][1], nums[i][2]);
  }
}

void mousePressed() {
  n++;
  // nums = append(nums, int( random(n) ) ); // <-- o append não funca

  // este é o bloco que precisas adaptar/copiar para dentro do teu sketch
  // vamos fazer abordagem tradicional… criar uma cópia do vetor
  // começamos por criar um vetor temoporário novo com mais um elemento (novo n)
  int[][] tArray = new int[n][3];
  
  // depois com um ciclo copiamos todas as posições do anteiror
  for (int i = 0; i < nums.length; i++) {
    tArray[i][0] = nums[i][0];
    tArray[i][1] = nums[i][1];
    tArray[i][2] = nums[i][2];
  }
  // e acrescentamos um último valor ao novo vetor
  tArray[n-1][0] = int( random(width) );
  tArray[n-1][1] = int( random(height) );
  tArray[n-1][2] = int( random(20, 50) );

  // agora, basta copiar o vetor novo para o antigo e substituir
  nums = tArray;

  // este método não é tão rápido, mas é a abordagem "tradicional"
  // próxima aula vamos ver ArrayLists ;)

  // printArray(nums); // <-- this is also only for simple arrays
  
  for (int i = 0; i < nums.length; i++) {
    printArray(i+": "+nums[i][0]+"-"+nums[i][1]+"-"+nums[i][2]);
  }
}

void radar(int x, int y, int w) {
  fill(255, 0, 0);
  
  if( dist(mouseX, mouseY, x, y) < w/2 ) {
    fill(0);
    
  }
  noStroke();
  ellipse(x, y, w, w);
}

Esta é uma abordagem tradicional. No entanto, acho que fico a cismar com isto todos os anos. E, todos os anos volto a ler o reference (vá lá que desta vez a pista já estava aqui no blog) e tento compreender uma forma alternativa de fazer isto com a função append do Processing.

Até porque na primeira linha eles mencionam que o append está reservado a vetores simples:

Expands a one-dimensional array by one element and adds data to the new position. The datatype of the element parameter must be the same as the datatype of the array.

Afinal de contas, funciona para vetores simples e de forma semelhante com o ArrayList… um vetor bidimensional trata-se apenas de um vetor simples (de vetores simples!).

Basta ler o reference online para perceber que, apesar deles não aconselharem, o append na realidade funciona em vetores bidimensionais. Só que com um ligeiro truque:

When using an array of objects, the data returned from the function must be cast to the object array's data type. For example: SomeClass[] items = (SomeClass[]) append(originalArray, element)

Então isto significa que podemos utilizar o append em qualquer tipo de vetor. Seja de variáveis simples, seja de variáveis compostas/complexas como os vetores tradicionais, seja com objetos! Hurray!

Mas… agora vem o problema recorrente de me lembrar como o fazer. Calma. O reference explica em “engenheirês”. O que eles estão a dizer é que, para adicionarmos um novo elemento complexo à lista (p. ex. um novo vetor), precisamos de dizer que a variável original é igual à conversão da soma do vetor original com o novo elemento num novo objeto do tipo complexo pretendido… ufff… fazendo é mais simples. Utilizando o exemplo anterior:

  // começamos por criar um vetor temoporário novo com mais um elemento (novo n)
  int[][] tArray = new int[n][3];
  
  // depois com um ciclo copiamos todas as posições do anteiror
  for (int i = 0; i < nums.length; i++) {
    tArray[i][0] = nums[i][0];
    …
  }
  // e acrescentamos um último valor ao novo vetor
  tArray[n-1][0] = int( random(width) );
  …

  // agora, basta copiar o vetor novo para o antigo e substituir
  nums = tArray;

Criamos um vetor temporário com uma dimensão igual à anterior, mais um elemento. Corremos um loop e copiamos todos os valores. No final acrescentamos o último valor à mão. E, copiamos este novo vetor para o vetor original.

O método append de objetos faz a mesma coisa da seguinte forma:

  // criamos um novo elemento
  int[] t = {int( random(width) ), int( random(height) ), int( random(20, 50) )};
  
  // designamos que o vetor original = conversão em bi-dimensional array da soma do vetor original com mais um elemento
  nums = (int[][])append(nums, t);

Basta criar um novo elemento que queremos adicionar (na teoria, pode estar tudo na mesma instrução?). E depois dizemos que o vetor original vai ser substituído por um append. O que significa que vai ser substituído pelo resultado da soma dele mesmo com um novo elemento que acabámos de criar. No entanto, o append gera um tipo de objeto específico (diferente do tipo original). Precisamos dizer que o resultado deste objeto vai ser “convertido” para um novo tipo de objeto igual ao tipo do vetor original [a operação de conversão de tipos pode ser feita com tipo(valor_a_converter), ou (tipo)valor].

O resultado o seguinte:

int[][] nums;
int n;

void setup() {
  size(500, 500);
  n = 10;
  nums = new int[n][3];
  for (int i = 0; i < n; i++) {
    nums[i][0] = int( random(width) );
    nums[i][1] = int( random(height) );
    nums[i][2] = int( random(20, 50) );
  }
}

void draw() {
  background(255);
  //udapte
  for (int i = 0; i < n; i ++) {
    nums[i][1] = nums[i][1] + nums[i][2]/20;
    if (nums[i][1] > height) {
      nums[i][1] = 0;
    }
  }

  //display
  for (int i = 0; i < n; i ++) {
    radar(nums[i][0], nums[i][1], nums[i][2]);
  }
}

void mousePressed() {
  n++;
  // criamos um novo elemento
  int[] t = {int( random(width) ), int( random(height) ), int( random(20, 50) )};

  // designamos que o vetor original = conversão em bi-dimensional array da soma do vetor original com mais um elemento
  nums = (int[][])append(nums, t);
}

void radar(int x, int y, int w) {
  fill(255, 0, 0);

  if ( dist(mouseX, mouseY, x, y) < w/2 ) {
    fill(0);
  }
  noStroke();
  ellipse(x, y, w, w);
}

É bastante elegante. E bastante confuso… mas é assim. No final do dia, poupámos algumas linhas e, em listas muito numerosas ganhamos alguma performance(?). De qualquer forma, se o objetivo for manipular listas complexas, o melhor mesmo é aprender a utilizar o ArrayList (com objetos).

Deixe um comentário

O seu endereço de email não será publicado.