Processing / ControlP5 > グラフの拡大縮小、上下移動

```txt:動作環境
Processing 3.1.1
ControlP5ライブラリ
Windows 8.1 pro (64bit)
```

http://qiita.com/7of9/items/c5d29901c52ec57cbd80
をベースにグラフの拡大縮小機能を検討した。

## グラフの拡大縮小、上下移動

### ボタン

- [*] : 値の拡大
- [/] : 値の縮小
- [+] : 値の増加
- [-] : 値の減少

値が拡大すると、グラフ内の表示範囲が拡大されるようにした。

### 実際の実装

map()関数でマップをする時に以下のような係数(multi1)とバイアス(bias1)を使う。
それらの値を先ほどのボタンで変更すれば、グラフ描画が拡大縮小、上下移動できる。

```java
    work = datavals[idx-1] * multi1 + bias1;
    float sty = map(work, 0, 100, grheight + grstarty, grstarty);
```

sty: 開始y値


## code

```java
import processing.serial.*;
import controlP5.*;

Serial myPort;

ControlP5 slider;
int sliderValue;
int numSerial = 5;
int curSerial = -1;

int grstartx = 100;
int grstarty = 100;
int grwidth = 600;
int grheight = 350;

int numData = 300;
float[] datavals = new float [numData];

ControlP5 btnOpen;
// for series1
ControlP5 btnEnlarge1;
ControlP5 btnShrink1;
ControlP5 btnUpper1;
ControlP5 btnLower1;
float multi1 = 1.0;
float bias1 = 0.0;

void data_setup() {
   for(int idx=0; idx < numData; idx++) {
     datavals[idx] = random(100);
   }
}

void comm_setup() {
  slider = new ControlP5(this);
  slider.addSlider("COM")
    .setRange(-1, numSerial - 1)
    .setValue(-1)
    .setPosition(50,40)
    .setSize(200, 20)
    .setNumberOfTickMarks(numSerial + 1);

  btnOpen = new ControlP5(this);
  btnOpen.addButton("openPort")
    .setLabel("Open")
    .setPosition(275, 40)
    .setSize(100, 30);       
}

void graph_setup() {
  // for series1
  btnEnlarge1 = new ControlP5(this);
  btnEnlarge1.addButton("enlarge1")
    .setLabel("*")
    .setPosition(40, 150)
    .setSize(20,20);

  btnEnlarge1 = new ControlP5(this);
  btnEnlarge1.addButton("shrink1")
    .setLabel("/")
    .setPosition(40, 180)
    .setSize(20,20);
    
  btnUpper1 = new ControlP5(this);
  btnUpper1.addButton("upper1")
    .setLabel("+")
    .setPosition(40, 350)
    .setSize(20,20);   

  btnLower1 = new ControlP5(this);
  btnLower1.addButton("lower1")
    .setLabel("-")
    .setPosition(40, 380)
    .setSize(20,20);
}

void setup() {
  size(800,500);
  data_setup();
  comm_setup();
  graph_setup();
}

void enlarge1() {
  multi1 *= 2.0;
}
void shrink1() {
  multi1 *= 0.5; 
}
void upper1() {
  bias1 += 10;
}  
void lower1() {
  bias1 -= 10;
}  

void controlEvent(ControlEvent theEvent) {
  if (theEvent.isController() == false) {
    return;
  }
  if (theEvent.getName() == "COM") {
    if (curSerial != slider.getValue("COM")) {
       curSerial = (int)slider.getValue("COM");
    }
  }
}

void serialEvent(Serial myPort) { 
   String mystr = myPort.readStringUntil('\n');
   mystr = trim(mystr);
   println(mystr);
}

void openPort() {
   println("Open Port"); 
   if (myPort != null) {
      myPort.stop(); 
   }
   myPort = new Serial(this, Serial.list()[(int)curSerial], 9600);
   myPort.bufferUntil('\n');
 }

void drawGraph() {
  fill(255);
  rect(grstartx, grstarty, grwidth, grheight);
  
  float work;
  for(int idx=1; idx < numData; idx++) {
    float stx = map(idx-1, 0, numData, grstartx, grstartx + grwidth);
    work = datavals[idx-1] * multi1 + bias1;
    float sty = map(work, 0, 100, grheight + grstarty, grstarty);
    float etx = map(idx, 0, numData, grstartx, grstartx + grwidth);
    work = datavals[idx] * multi1 + bias1;
    float ety = map(work, 0, 100, grheight + grstarty, grstarty);
    line(stx, sty, etx, ety);
  }
}

void draw() {
  background(150);  
  drawGraph();
}
```

## 実行例

### 初期状態

![qiita.png](https://qiita-image-store.s3.amazonaws.com/0/32870/eb402ca6-0885-0522-9049-3286c68581fc.png)

### 初期状態から[*]を1回押した時

![qiita.png](https://qiita-image-store.s3.amazonaws.com/0/32870/689823c6-78dd-88a5-9373-7feec58973f0.png)

### 初期状態から[/]を2回押した時

![qiita.png](https://qiita-image-store.s3.amazonaws.com/0/32870/af406056-e56f-dece-d6d2-43f185997fef.png)


### 初期状態から[/]を2回押して、[+]を3回押した時

![qiita.png](https://qiita-image-store.s3.amazonaws.com/0/32870/7226825d-df43-5399-ad72-fe35f0a2198a.png)

### 補足

- 表示枠からはみ出る分は描画しないなどの処理が必要
- [+],[-]ボタンで増減する値は固定値よりも物理量に合わせた方がいいだろうが、要検討


## 2シリーズ対応

### 実行例

それぞれのシリーズに対して拡大、縮小、上下移動が可能。
4シリーズ対応が当面の目標。

![qiita.png](https://qiita-image-store.s3.amazonaws.com/0/32870/3bc33a1d-4490-d6e1-1435-77afad3b750b.png)

### code

コピペコード
構造化してない。
そろそろgithubに入れた方がいいのかもしれない。

```java
import processing.serial.*;
import controlP5.*;

Serial myPort;

ControlP5 slider;
int sliderValue;
int numSerial = 5;
int curSerial = -1;

int grstartx = 100;
int grstarty = 100;
int grwidth = 600;
int grheight = 350;

int numData = 300;
float[] datavals1 = new float [numData];
float[] datavals2 = new float [numData];

ControlP5 btnOpen;
// for series1
ControlP5 btnEnlarge1;
ControlP5 btnShrink1;
ControlP5 btnUpper1;
ControlP5 btnLower1;
float multi1 = 1.0;
float bias1 = 0.0;
// for series2
ControlP5 btnEnlarge2;
ControlP5 btnShrink2;
ControlP5 btnUpper2;
ControlP5 btnLower2;
float multi2 = 1.0;
float bias2 = 0.0;

int btnX1 = 40;
int btnX2 = 70;

void data_setup() {
   for(int idx=0; idx < numData; idx++) {
     datavals1[idx] = random(100);
   }
   for(int idx=0; idx < numData; idx++) {
     datavals2[idx] = random(100);
   }
}

void comm_setup() {
  slider = new ControlP5(this);
  slider.addSlider("COM")
    .setRange(-1, numSerial - 1)
    .setValue(-1)
    .setPosition(50,40)
    .setSize(200, 20)
    .setNumberOfTickMarks(numSerial + 1);

  btnOpen = new ControlP5(this);
  btnOpen.addButton("openPort")
    .setLabel("Open")
    .setPosition(275, 40)
    .setSize(100, 30);       
}

void graph_setup() {
  // for series1
  btnEnlarge1 = new ControlP5(this);
  btnEnlarge1.addButton("enlarge1")
    .setLabel("*")
    .setPosition(btnX1, 150)
    .setSize(20,20);
    
  btnEnlarge1 = new ControlP5(this);
  btnEnlarge1.addButton("shrink1")
    .setLabel("/")
    .setPosition(btnX1, 180)
    .setSize(20,20);
    
  btnUpper1 = new ControlP5(this);
  btnUpper1.addButton("upper1")
    .setLabel("+")
    .setPosition(btnX1, 350)
    .setSize(20,20);   

  btnLower1 = new ControlP5(this);
  btnLower1.addButton("lower1")
    .setLabel("-")
    .setPosition(btnX1, 380)
    .setSize(20,20);

  // for series2
  btnEnlarge2 = new ControlP5(this);
  btnEnlarge2.addButton("enlarge2")
    .setLabel("*")
    .setPosition(btnX2, 150)
    .setSize(20,20);
    
  btnEnlarge2 = new ControlP5(this);
  btnEnlarge2.addButton("shrink2")
    .setLabel("/")
    .setPosition(btnX2, 180)
    .setSize(20,20);
    
  btnUpper2 = new ControlP5(this);
  btnUpper2.addButton("upper2")
    .setLabel("+")
    .setPosition(btnX2, 350)
    .setSize(20,20);   

  btnLower2 = new ControlP5(this);
  btnLower2.addButton("lower2")
    .setLabel("-")
    .setPosition(btnX2, 380)
    .setSize(20,20);
}

void setup() {
  size(800,500);
  data_setup();
  comm_setup();
  graph_setup();
}

void enlarge1() {
  multi1 *= 2.0;
}
void shrink1() {
  multi1 *= 0.5; 
}
void upper1() {
  bias1 += 10;
}  
void lower1() {
  bias1 -= 10;
}

void enlarge2() {
  multi2 *= 2.0;
}
void shrink2() {
  multi2 *= 0.5; 
}
void upper2() {
  bias2 += 10;
}  
void lower2() {
  bias2 -= 10;
}


void controlEvent(ControlEvent theEvent) {
  if (theEvent.isController() == false) {
    return;
  }
  if (theEvent.getName() == "COM") {
    if (curSerial != slider.getValue("COM")) {
       curSerial = (int)slider.getValue("COM");
    }
  }
}

void serialEvent(Serial myPort) { 
   String mystr = myPort.readStringUntil('\n');
   mystr = trim(mystr);
   println(mystr);
}

void openPort() {
   println("Open Port"); 
   if (myPort != null) {
      myPort.stop(); 
   }
   myPort = new Serial(this, Serial.list()[(int)curSerial], 9600);
   myPort.bufferUntil('\n');
 }

void drawGraph() {
  stroke(0, 0, 0);
  fill(255);
  rect(grstartx, grstarty, grwidth, grheight);
  
  float work;
  stroke(0, 0, 0); // for series1  
  for(int idx=1; idx < numData; idx++) {
    float stx = map(idx-1, 0, numData, grstartx, grstartx + grwidth);
    work = datavals1[idx-1] * multi1 + bias1;
    float sty = map(work, 0, 100, grheight + grstarty, grstarty);
    float etx = map(idx, 0, numData, grstartx, grstartx + grwidth);
    work = datavals1[idx] * multi1 + bias1;
    float ety = map(work, 0, 100, grheight + grstarty, grstarty);
    line(stx, sty, etx, ety);
  }

  stroke(255, 0, 0); // for series2
  for(int idx=1; idx < numData; idx++) {
    float stx = map(idx-1, 0, numData, grstartx, grstartx + grwidth);
    work = datavals2[idx-1] * multi2 + bias2;
    float sty = map(work, 0, 100, grheight + grstarty, grstarty);
    float etx = map(idx, 0, numData, grstartx, grstartx + grwidth);
    work = datavals2[idx] * multi2 + bias2;
    float ety = map(work, 0, 100, grheight + grstarty, grstarty);
    line(stx, sty, etx, ety);
  }
}

void draw() {
  background(150);  
  drawGraph();
}
```