Processingのサンプルを参考にコードを書いた。(立方体: 回転、拡大、照明の調整、ポリモーフィズム)

追記: 2012/12/20

現在、processing の最新版が Ver 2.0b7 です。
バージョンアップに伴い、いくつか変更があるようです。

本エントリのコード「textureMode(NORMALIZED);」は
"NORMALIZED"を"NORMAL"に読み替えてください。

追記: 2012/12/21

本エントリのサンプルをキャプチャしたものを
youtube にアップしました。


改良点

ポリモーフィズムを利用した。class TexturedCubeを書いて、if文を消した。

コード

//変数の宣言
PImage tex;
float rotx = PI/4;
float roty = PI/4;
int valuex = 0;
int valuey = 1;

//初期設定
void setup() 
{
  size(809, 500, P3D);
  tex = loadImage("01.JPG");
  textureMode(NORMALIZED);
  fill(255);
  stroke(color(44,48,32));
}

//描画
void draw() 
{
  int ColorValue = #dddddd;
  background(ColorValue);
  noStroke();
  directionalLight(126, 126, 126, 0, 0, -1);
  ambientLight(10+valuex, 10+valuex, 10+valuex);

  translate(width/2.0, height/2.0, -100);
  rotateX(rotx);
  rotateY(roty);
  scale(90);
  TexturedCube t = new TexturedCube();
  t.Type1(1+mouseY * 0.002);
  autorotate();
}

//自動回転のための関数
void autorotate()
{
  float rate = 0.01;
  float speed = mouseX * 0.02;
  rotx += speed * rate;
  roty += speed * rate;;
}

//出力する図形の種類について記述したクラス(6面ある立方体と、3面だけのものを2個を用意した。)
class TexturedCube {
  void Type1(float i){
    beginShape(QUADS);
    texture(tex);
    CubeFace c1 = new CubeFace();
    c1.PZface(i);
    c1.MZface(i);
    c1.BYface(i);
    c1.TYface(i);
    c1.RXface(i);
    c1.LXface(i);
    endShape();
    }
  void Type2(float i){
    beginShape(QUADS);
    texture(tex);
    CubeFace c1 = new CubeFace();
    c1.PZface(i);
    c1.BYface(i);
    c1.RXface(i);
    endShape();
  }
  void Type3(float i){
    beginShape(QUADS);
    texture(tex);
    CubeFace c1 = new CubeFace();
    c1.MZface(i);
    c1.BYface(i);
    c1.LXface(i);
    endShape();
  }
}

//出力する図形の面を記述したクラス
class CubeFace {
  void PZface(float i) {
   vertex(-i, -i,  i, 0, 0);
   vertex( i, -i,  i, i, 0);
   vertex( i,  i,  i, i, i);
   vertex(-i,  i,  i, 0, i);
 }

 void MZface(float i) {
  vertex( i, -i, -i, 0, 0);
  vertex(-i, -i, -i, i, 0);
  vertex(-i,  i, -i, i, i);
  vertex( i,  i, -i, 0, i);
 }

 void BYface(float i) {
  vertex(-i,  i,  i, 0, 0);
  vertex( i,  i,  i, i, 0);
  vertex( i,  i, -i, i, i);
  vertex(-i,  i, -i, 0, i);
 }

 void TYface(float i) {
  vertex(-i, -i, -i, 0, 0);
  vertex( i, -i, -i, i, 0);
  vertex( i, -i,  i, i, i);
  vertex(-i, -i,  i, 0, i);
 }

 void RXface(float i) {
  vertex( i, -i,  i, 0, 0);
  vertex( i, -i, -i, i, 0);
  vertex( i,  i, -i, i, i);
  vertex( i,  i,  i, 0, i);
}

 void LXface(float i) {
  vertex(-i, -i, -i, 0, 0);
  vertex(-i, -i,  i, i, 0);
  vertex(-i,  i,  i, i, i);
  vertex(-i,  i, -i, 0, i);
 }
}

//マウスのドラッグを、値に反映させる関数。
void mouseDragged(){
  valuex += (mouseX-pmouseX)*0.25; 
  valuey += (mouseY-pmouseY)*0.25; 
}

Processingのサンプルを参考にコードを書いた。(立方体: 回転、拡大、照明の調整)

改良点

マウスを右にドラッグすることで、立方体が明るくなります。マウスを左にドラッグすることで、立方体が暗くなります。
"0.25"の値を変えることで、増加の割合を調節できます。

valuex += (mouseX-pmouseX)*0.25;

表現の軸

現時点では、以下のような「表現の軸」と呼べるようなものがあります。

1. マウスの縦方向の移動による、立方体の辺の長さに関する軸。
2. マウスの横方向の移動による、立方体の回転速度に関する軸。
3. マウスの横方向のドラッグ操作による、照明の光の強さに関する軸。

このマウスというインターフェースが生み出す「表現の軸」について、考えていきたいと思います。



コード

PImage tex;
float rotx = PI/4;
float roty = PI/4;
int valuex = 0;
int valuey = 126;

void setup() 
{

  size(809, 500, P3D);
  tex = loadImage("01.JPG");
  textureMode(NORMALIZED);
  fill(255);
  stroke(color(44,48,32));
}

void draw() 
{
  int ColorValue = #dddddd;
  background(ColorValue);
  noStroke();
  directionalLight(valuey, valuey, valuey, 0, 0, -1);
  ambientLight(10+valuex, 10+valuex, 10+valuex);
  translate(width/2.0, height/2.0, -100);
  rotateX(rotx);
  rotateY(roty);
  scale(90);
  TexturedCube(tex, 1+mouseY * 0.002, 1);
  autorotate();
}

void autorotate()
{
  float rate = 0.01;
  float speed = mouseX * 0.02;
  rotx += speed * rate;
  roty += speed * rate;;
}

void NoTexturedCube(float i, int type) {
  beginShape(QUADS);
  CubeFace c1 = new CubeFace();
  
  if(type == 1){
    c1.PZface(i);
    c1.MZface(i);
    c1.BYface(i);
    c1.TYface(i);
    c1.RXface(i);
    c1.LXface(i);
  }
  else if(type == 2){ 
    c1.PZface(i);
    c1.BYface(i);
    c1.RXface(i); 
  }
  else{
    c1.MZface(i);
    c1.BYface(i);
    c1.LXface(i);
  }
  
  endShape();
}

void TexturedCube(PImage tex, float i, int type) {
  beginShape(QUADS);
  texture(tex);
  
  CubeFace c1 = new CubeFace();
  
  if(type == 1){
    c1.PZface(i);
    c1.MZface(i);
    c1.BYface(i);
    c1.TYface(i);
    c1.RXface(i);
    c1.LXface(i);
  }
  else if(type == 2){ 
    c1.PZface(i);
    c1.BYface(i);
    c1.RXface(i); 
  }
  else{
    c1.MZface(i);
    c1.BYface(i);
    c1.LXface(i);
  }
  
  endShape();
}

class CubeFace {
  void PZface(float i) {
   vertex(-i, -i,  i, 0, 0);
   vertex( i, -i,  i, i, 0);
   vertex( i,  i,  i, i, i);
   vertex(-i,  i,  i, 0, i);
 }

 void MZface(float i) {
  vertex( i, -i, -i, 0, 0);
  vertex(-i, -i, -i, i, 0);
  vertex(-i,  i, -i, i, i);
  vertex( i,  i, -i, 0, i);
 }

 void BYface(float i) {
  vertex(-i,  i,  i, 0, 0);
  vertex( i,  i,  i, i, 0);
  vertex( i,  i, -i, i, i);
  vertex(-i,  i, -i, 0, i);
 }

 void TYface(float i) {
  vertex(-i, -i, -i, 0, 0);
  vertex( i, -i, -i, i, 0);
  vertex( i, -i,  i, i, i);
  vertex(-i, -i,  i, 0, i);
 }

 void RXface(float i) {
  vertex( i, -i,  i, 0, 0);
  vertex( i, -i, -i, i, 0);
  vertex( i,  i, -i, i, i);
  vertex( i,  i,  i, 0, i);
}

 void LXface(float i) {
  vertex(-i, -i, -i, 0, 0);
  vertex(-i, -i,  i, i, 0);
  vertex(-i,  i,  i, i, i);
  vertex(-i,  i, -i, 0, i);
 }
}

void mouseDragged(){
  valuex += (mouseX-pmouseX)*0.25;
}

Processingのサンプルを参考にコードを書いた。(立方体の回転と拡大)

改良点
マウスを右に移動することで、回転速度が増加。"0.02"の値を変えることで、増加の割合を調節できます。

float speed = (mouseX) * 0.02;

マウスを下に移動することで、立方体の辺の長さが増加。"0.002"の値を変えることで、増加の割合を調節できます。

TexturedCube(tex, 1+mouseY * 0.002, 1);

PImage tex;
float rotx = PI/4;
float roty = PI/4;
int value = 0;

void setup() 
{
  size(809, 500, P3D);
  tex = loadImage("01.JPG");
  textureMode(NORMALIZED);
  fill(255);
  stroke(color(44,48,32));
}

void draw() 
{
  background(#dddddd);
  noStroke();
  translate(width/2.0, height/2.0, -100);
  rotateX(rotx);
  rotateY(roty);
  scale(90);
  TexturedCube(tex, 1+mouseY * 0.002, 1);
  autorotate();
}

void autorotate()
{
  float rate = 0.01;
  float speed = (mouseX) * 0.02;
  rotx += speed * rate;
  roty += speed * rate;;
}

void TexturedCube(PImage tex, float i, int type) {
  beginShape(QUADS);
  texture(tex);
  
  CubeFace c1 = new CubeFace();
  
  if(type == 1){
    c1.PZface(i);
    c1.MZface(i);
    c1.BYface(i);
    c1.TYface(i);
    c1.RXface(i);
    c1.LXface(i);
  }
  else if(type == 2){ 
    c1.PZface(i);
    c1.BYface(i);
    c1.RXface(i); 
  }
  else{
    c1.MZface(i);
    c1.BYface(i);
    c1.LXface(i);
  }
  
  endShape();
}

class CubeFace {
  void PZface(float i) {
   vertex(-i, -i,  i, 0, 0);
   vertex( i, -i,  i, i, 0);
   vertex( i,  i,  i, i, i);
   vertex(-i,  i,  i, 0, i);
 }

 void MZface(float i) {
  vertex( i, -i, -i, 0, 0);
  vertex(-i, -i, -i, i, 0);
  vertex(-i,  i, -i, i, i);
  vertex( i,  i, -i, 0, i);
 }

 void BYface(float i) {
  vertex(-i,  i,  i, 0, 0);
  vertex( i,  i,  i, i, 0);
  vertex( i,  i, -i, i, i);
  vertex(-i,  i, -i, 0, i);
 }

 void TYface(float i) {
  vertex(-i, -i, -i, 0, 0);
  vertex( i, -i, -i, i, 0);
  vertex( i, -i,  i, i, i);
  vertex(-i, -i,  i, 0, i);
 }

 void RXface(float i) {
  vertex( i, -i,  i, 0, 0);
  vertex( i, -i, -i, i, 0);
  vertex( i,  i, -i, i, i);
  vertex( i,  i,  i, 0, i);
}

 void LXface(float i) {
  vertex(-i, -i, -i, 0, 0);
  vertex(-i, -i,  i, i, 0);
  vertex(-i,  i,  i, i, i);
  vertex(-i,  i, -i, 0, i);
 }
}

Processingのサンプルを参考にコードを書いた。(立方体の回転)

概要

以下のようなコードです。

  • 2つの立方体が、回転する。
  • マウスを画面の右下に移動するほど、回転速度が上がる。
  • マウスの画面の左上に移動するほど、回転速度が下がる。
  • 関数setupの中の01.jpgを、サブフォルダdataを作って格納する。この画像が立方体に貼られる。

[,w485,h300]

コード

PImage tex;
float rotx = PI/4;
float roty = PI/4;

void setup() 
{
  size(809, 500, P3D);
  tex = loadImage("01.JPG");
  textureMode(NORMALIZED);
  fill(255);
  stroke(color(44,48,32));
}

void draw() 
{
  background(#dddddd);
  noStroke();
  translate(width/2.0, height/2.0, -100);
  rotateX(rotx);
  rotateY(roty);
  scale(90);
  TexturedCube(tex);
  TexturedCube2(tex);
  autorotate();
}

void autorotate()
{
  float rate = 0.01;
  float speed = (mouseX+mouseY) * 0.02;
  rotx += speed * rate;
  roty += speed * rate;;
}

void TexturedCube2 (PImage tex) {
  beginShape(QUADS);
  texture(tex);

  // +Z "front" face
  vertex(-2, -2,  2, 0, 0);
  vertex( 2, -2,  2, 2, 0);
  vertex( 2,  2,  2, 2, 2);
  vertex(-2,  2,  2, 0, 2);

  // +Y "bottom" face
  vertex(-2,  2,  2, 0, 0);
  vertex( 2,  2,  2, 2, 0);
  vertex( 2,  2, -2, 2, 2);
  vertex(-2,  2, -2, 0, 2);

  // +X "right" face
  vertex( 2, -2,  2, 0, 0);
  vertex( 2, -2, -2, 2, 0);
  vertex( 2,  2, -2, 2, 2);
  vertex( 2,  2,  2, 0, 2);

  endShape();
}

void TexturedCube(PImage tex) {
  beginShape(QUADS);
  texture(tex);
  
  // +Z "front" face
  vertex(-1, -1,  1, 0, 0);
  vertex( 1, -1,  1, 1, 0);
  vertex( 1,  1,  1, 1, 1);
  vertex(-1,  1,  1, 0, 1);

  // -Z "back" face
  vertex( 1, -1, -1, 0, 0);
  vertex(-1, -1, -1, 1, 0);
  vertex(-1,  1, -1, 1, 1);
  vertex( 1,  1, -1, 0, 1);

  // +Y "bottom" face
  vertex(-1,  1,  1, 0, 0);
  vertex( 1,  1,  1, 1, 0);
  vertex( 1,  1, -1, 1, 1);
  vertex(-1,  1, -1, 0, 1);

  // -Y "top" face
  vertex(-1, -1, -1, 0, 0);
  vertex( 1, -1, -1, 1, 0);
  vertex( 1, -1,  1, 1, 1);
  vertex(-1, -1,  1, 0, 1);

  // +X "right" face
  vertex( 1, -1,  1, 0, 0);
  vertex( 1, -1, -1, 1, 0);
  vertex( 1,  1, -1, 1, 1);
  vertex( 1,  1,  1, 0, 1);

  // -X "left" face
  vertex(-1, -1, -1, 0, 0);
  vertex(-1, -1,  1, 1, 0);
  vertex(-1,  1,  1, 1, 1);
  vertex(-1,  1, -1, 0, 1);

  endShape();
}

解説

autorotate関数の、(mouseX+mouseY) * 0.02が、マウスカーソルの座標の縦軸と、横軸の和が速度に影響するようにするための部分です。
0.02をかけているのは、そのままマウスカーソルの座標を入力として与えると大きすぎるのでそれを小さくするために使っています。
この値を操作することで、マウスカーソルの移動による速度の増加を制御できます。

参考

Processingのサンプル, TextureCube

Processingのサンプルを参考にコードを書いた。(立方体の回転3)

改良点

  • TexturedCubeP1, TexturedCubeP2を削除。代わりに、TexturedCubeに描画する面を指定する引数を追加。

引数は、1: 全部の面, 2: 半分の面, 3: 2と対称な半分の面

[,w485,h300]

PImage tex;
float rotx = PI/4;
float roty = PI/4;
int value = 0;

void setup() 
{
  size(809, 500, P3D);
  tex = loadImage("01.JPG");
  textureMode(NORMALIZED);
  fill(255);
  stroke(color(44,48,32));
}

void draw() 
{
  background(#dddddd);
  noStroke();
  translate(width/2.0, height/2.0, -100);
  rotateX(rotx);
  rotateY(roty);
  scale(90);
  TexturedCube(tex, 1, 1);
  TexturedCube(tex, 1.5, 2);
  TexturedCube(tex, 1.7, 3);
  autorotate();
}

void autorotate()
{
  float rate = 0.01;
  float speed = (mouseX+mouseY) * 0.02;
  rotx += speed * rate;
  roty += speed * rate;;
}

void PZface(float i) {
  vertex(-i, -i,  i, 0, 0);
  vertex( i, -i,  i, i, 0);
  vertex( i,  i,  i, i, i);
  vertex(-i,  i,  i, 0, i);
}

void MZface(float i) {
  vertex( i, -i, -i, 0, 0);
  vertex(-i, -i, -i, i, 0);
  vertex(-i,  i, -i, i, i);
  vertex( i,  i, -i, 0, i);
}

void BYface(float i) {
  vertex(-i,  i,  i, 0, 0);
  vertex( i,  i,  i, i, 0);
  vertex( i,  i, -i, i, i);
  vertex(-i,  i, -i, 0, i);
}

void TYface(float i) {
  vertex(-i, -i, -i, 0, 0);
  vertex( i, -i, -i, i, 0);
  vertex( i, -i,  i, i, i);
  vertex(-i, -i,  i, 0, i);
}

void RXface(float i) {
  vertex( i, -i,  i, 0, 0);
  vertex( i, -i, -i, i, 0);
  vertex( i,  i, -i, i, i);
  vertex( i,  i,  i, 0, i);
}

void LXface(float i) {
  vertex(-i, -i, -i, 0, 0);
  vertex(-i, -i,  i, i, 0);
  vertex(-i,  i,  i, i, i);
  vertex(-i,  i, -i, 0, i);
}

void TexturedCube(PImage tex, float i, int type) {
  beginShape(QUADS);
  texture(tex);
  
  switch(type) {
  case 1: 
    PZface(i);
    MZface(i);
    BYface(i);
    TYface(i);
    RXface(i);
    LXface(i);
    break;
  case 2: 
    PZface(i);
    BYface(i);
    RXface(i); 
    break;
  default:
    MZface(i);
    BYface(i);
    LXface(i);
    break;
  }
  
  endShape();
}

(追記)
class CubeFaceを書いた。

PImage tex;
float rotx = PI/4;
float roty = PI/4;
int value = 0;

void setup() 
{
  size(809, 500, P3D);
  tex = loadImage("01.JPG");
  textureMode(NORMALIZED);
  fill(255);
  stroke(color(44,48,32));
}

void draw() 
{
  background(#dddddd);
  noStroke();
  translate(width/2.0, height/2.0, -100);
  rotateX(rotx);
  rotateY(roty);
  scale(90);
  TexturedCube(tex, 1, 1);
  TexturedCube(tex, 1.5, 2);
  TexturedCube(tex, 1.7, 3);
  autorotate();
}

void autorotate()
{
  float rate = 0.01;
  float speed = (mouseX+mouseY) * 0.02;
  rotx += speed * rate;
  roty += speed * rate;;
}

void TexturedCube(PImage tex, float i, int type) {
  beginShape(QUADS);
  texture(tex);
  
  CubeFace c1 = new CubeFace();
  
  if(type == 1){
    c1.PZface(i);
    c1.MZface(i);
    c1.BYface(i);
    c1.TYface(i);
    c1.RXface(i);
    c1.LXface(i);
  }
  else if(type == 2){ 
    c1.PZface(i);
    c1.BYface(i);
    c1.RXface(i); 
  }
  else{
    c1.MZface(i);
    c1.BYface(i);
    c1.LXface(i);
  }
  
  endShape();
}

class CubeFace {
  void PZface(float i) {
   vertex(-i, -i,  i, 0, 0);
   vertex( i, -i,  i, i, 0);
   vertex( i,  i,  i, i, i);
   vertex(-i,  i,  i, 0, i);
 }

 void MZface(float i) {
  vertex( i, -i, -i, 0, 0);
  vertex(-i, -i, -i, i, 0);
  vertex(-i,  i, -i, i, i);
  vertex( i,  i, -i, 0, i);
 }

 void BYface(float i) {
  vertex(-i,  i,  i, 0, 0);
  vertex( i,  i,  i, i, 0);
  vertex( i,  i, -i, i, i);
  vertex(-i,  i, -i, 0, i);
 }

 void TYface(float i) {
  vertex(-i, -i, -i, 0, 0);
  vertex( i, -i, -i, i, 0);
  vertex( i, -i,  i, i, i);
  vertex(-i, -i,  i, 0, i);
 }

 void RXface(float i) {
  vertex( i, -i,  i, 0, 0);
  vertex( i, -i, -i, i, 0);
  vertex( i,  i, -i, i, i);
  vertex( i,  i,  i, 0, i);
}

 void LXface(float i) {
  vertex(-i, -i, -i, 0, 0);
  vertex(-i, -i,  i, i, 0);
  vertex(-i,  i,  i, i, i);
  vertex(-i,  i, -i, 0, i);
 }
}

Processingのサンプルを参考にコードを書いた。(立方体の回転2)

コードを改良した。

昨日書いたコードを改良した。

改良点

  • TexturedCubeに立方体の大きさを指定する引数を追加。
  • 関数TexturedCubeP1, TexturedCubeP2を追加。立方体の面の半分を描画。この2つの関数で描画されたものは対称になっている。

(ここでは、TexturedCubeP1のみを使用。このコードを走らせると、入れ子になった立方体の面が描画される。)

[,w485,h300]

PImage tex;
float rotx = PI/4;
float roty = PI/4;
int value = 0;

void setup() 
{
  size(809, 500, P3D);
  tex = loadImage("01.JPG");
  textureMode(NORMALIZED);
  fill(255);
  stroke(color(44,48,32));
}

void draw() 
{
  background(#dddddd);
  noStroke();
  translate(width/2.0, height/2.0, -100);
  rotateX(rotx);
  rotateY(roty);
  scale(90);
  TexturedCubeP1(tex, 1);
  TexturedCubeP1(tex, 1.5);
  TexturedCubeP1(tex, 1.7);
  autorotate();
}

void autorotate()
{
  float rate = 0.01;
  float speed = (mouseX+mouseY) * 0.02;
  rotx += speed * rate;
  roty += speed * rate;;
}

void TexturedCube(PImage tex, float i) {
  beginShape(QUADS);
  texture(tex);
  // +Z "front" face
  vertex(-i, -i,  i, 0, 0);
  vertex( i, -i,  i, i, 0);
  vertex( i,  i,  i, i, i);
  vertex(-i,  i,  i, 0, i);

  // -Z "back" face
  vertex( i, -i, -i, 0, 0);
  vertex(-i, -i, -i, i, 0);
  vertex(-i,  i, -i, i, i);
  vertex( i,  i, -i, 0, i);

  // +Y "bottom" face
  vertex(-i,  i,  i, 0, 0);
  vertex( i,  i,  i, i, 0);
  vertex( i,  i, -i, i, i);
  vertex(-i,  i, -i, 0, i);

  // -Y "top" face
  vertex(-i, -i, -i, 0, 0);
  vertex( i, -i, -i, i, 0);
  vertex( i, -i,  i, i, i);
  vertex(-i, -i,  i, 0, i);

  // +X "right" face
  vertex( i, -i,  i, 0, 0);
  vertex( i, -i, -i, i, 0);
  vertex( i,  i, -i, i, i);
  vertex( i,  i,  i, 0, i);

  // -X "left" face
  vertex(-i, -i, -i, 0, 0);
  vertex(-i, -i,  i, i, 0);
  vertex(-i,  i,  i, i, i);
  vertex(-i,  i, -i, 0, i);

  endShape();
}

void TexturedCubeP1(PImage tex, float i) {
  beginShape(QUADS);
  texture(tex);
  // +Z "front" face
  vertex(-i, -i,  i, 0, 0);
  vertex( i, -i,  i, i, 0);
  vertex( i,  i,  i, i, i);
  vertex(-i,  i,  i, 0, i);

  // +Y "bottom" face
  vertex(-i,  i,  i, 0, 0);
  vertex( i,  i,  i, i, 0);
  vertex( i,  i, -i, i, i);
  vertex(-i,  i, -i, 0, i);

  // +X "right" face
  vertex( i, -i,  i, 0, 0);
  vertex( i, -i, -i, i, 0);
  vertex( i,  i, -i, i, i);
  vertex( i,  i,  i, 0, i);

  endShape();
}

void TexturedCubeP2(PImage tex, float i) {
  beginShape(QUADS);
  texture(tex);

  // -Z "back" face
  vertex( i, -i, -i, 0, 0);
  vertex(-i, -i, -i, i, 0);
  vertex(-i,  i, -i, i, i);
  vertex( i,  i, -i, 0, i);

  // -Y "top" face
  vertex(-i, -i, -i, 0, 0);
  vertex( i, -i, -i, i, 0);
  vertex( i, -i,  i, i, i);
  vertex(-i, -i,  i, 0, i);

  // -X "left" face
  vertex(-i, -i, -i, 0, 0);
  vertex(-i, -i,  i, i, 0);
  vertex(-i,  i,  i, i, i);
  vertex(-i,  i, -i, 0, i);

  endShape();
}