😎

processingの勉強1

に公開

とりあえず何も考えずぽちぽちしていく。

// 4x4 -> 3 -> 2 3D NN Visualizer + 2D「AIくん」判定吹き出し付き
// Processing 4 / Java mode / P3D

// ====== レイアウト / 見た目 ======
final int CANVAS_W = 1280;
final int CANVAS_H = 900;

final int GRID_ROWS = 4, GRID_COLS = 4;  // 入力は4x4=16
final float GRID_SP = 70;                // 上部グリッド点間隔
final float GRID_Y  = -360;              // 上部グリッド高さ

final float L0_Y = -180f;                // 入力(16)
final float L1_Y =  -20f;                // 隠れ(3)
final float L2_Y =  140f;                // 出力(2)
final float L1_SPAN_X = 260;             // 隠れの横幅
final float L2_SPAN_X = 150;             // 出力の横幅

final float NODE_R = 4.0f;               // ノード球半径

final boolean ADDITIVE_GLOW = true;      // 加算合成で発光
final int COL_BG   = 0xff0E0E10;         // 背景
final int COL_DOT  = 0xffE8E8E8;         // 白点(入力グリッド)
final int COL_NODE = 0xffC82828;         // ノード
final int COL_LINE_POS = 0xffFF3A3A;     // 正の重み(赤)
final int COL_LINE_NEG = 0xff2A6AFF;     // 負の重み(青)
final int LINE_ALPHA = 85;               // 線の透明度
final float LINE_W_MIN = 0.4f;           // 線太さ min
final float LINE_W_MAX = 5.0f;           // 線太さ max

// 出力シンボル(3D ○/×)のスケール
final float O_SCALE_MIN = 0.65f, O_SCALE_MAX = 1.35f; // o0用
final float X_SCALE_MIN = 0.65f, X_SCALE_MAX = 1.35f; // o1用
final float O_MAJOR_R   = 26;   // ○の大半径(ベース)
final float O_TUBE_R    = 6;    // ○のチューブ半径(ベース)
final float X_BAR_LEN   = 55;   // ×の棒の長さ(ベース)
final float X_BAR_THICK = 10;   // ×の棒の太さ(ベース)

// 右側 UI(入力パネル)
final int PANEL_X = 900, PANEL_Y = 120;
final int CELL = 80, GAP = 10;
final int COL_PANEL_BORDER = 0xffC8C8C8;

// ====== NN パラメータ(16->3->2:提供値) ======
float[][][] weights = new float[][][] {
  { // 入力(16) -> 隠れ(3)
    {
      -0.19556837184815432, -2.0731887662694675, -0.7652514121341945, -1.9884231323546566,
      -4.952637298076189,   5.659829256271411,  -0.5713695038053532, -1.4846930025289022,
      -1.4970230006948473,  2.5726393101553975, -0.2504789124544582,  1.480466604318017,
      -0.8298236501477579, -4.5199641859013955, -1.4981634293951982,  2.011290351680955
    },
    {
      -3.4835715114090715,  5.010347806892839,  2.9247242936422113, -4.194203236742492,
      -0.17924275715765517, -2.403425422788088,  0.40385725801892475, 1.8787273651616636,
       2.047521825332689,  -0.16374517197602814, 0.47853555594913116, 5.6545162920862575,
      -6.058153846339116,   0.42470424485595815,-0.8371742892189643, -2.879606740652077
    },
    {
       2.884860808289696,   0.23975579237277614,-1.2403687366139762, -2.3025676808322486,
      -0.7475210018222626, -6.5294340577398575, -0.559934356147751,   4.0010381968700575,
       5.0747200536885,    -4.225817417542366,   0.3105588568588239,  3.308254461438346,
       4.348740489196476,   1.5652833651727298, -4.873553428254676,  -1.3786319596139733
    }
  },
  { // 隠れ(3) -> 出力(2)
    { -8.832622109783433,  8.303120604609045, -6.0039452831036275 },
    {  8.827857967700506, -8.306736033721686,  6.003293693110446  }
  }
};
float[][] biases = new float[][] {
  { 6.286020903686753, 3.2317338139016565, 2.8075640195084577 },  // 隠れ
  { 2.626446601080335, -2.618405339273877 }                        // 出力
};

// ====== 内部ワーク ======
PVector[] topGrid;   // 上部 4x4 白点
PVector[] lay0;      // 入力(16)
PVector[] lay1;      // 隠れ(3)
PVector[] lay2;      // 出力(2)  [0]=○, [1]=×
boolean[][] grid = new boolean[GRID_ROWS][GRID_COLS]; // 入力パネル(0/1)
float angleY = 0, angleX = 0;

// 正規化用:それぞれの層で |activation*weight| の最大想定
float maxW_IH = 1f; // 入力->隠れ
float maxW_HO = 1f; // 隠れ->出力

void settings() {
  size(CANVAS_W, CANVAS_H, P3D);
  smooth(8);
}
void setup() {
  surface.setTitle("4x4 -> 3 -> 2 : 3D O/X + 2D AI-kun speech");
  buildTopGrid();
  buildLayers();
  clearGrid();
  computeWeightMaxima();
}

void draw() {
  background(COL_BG);
  lights();

  // ==== 左:3D ビジュアライズ ====
  float[] o; // 出力活性(後でAIくんの吹き出しに渡す)
  {
    pushMatrix();
    translate(width*0.33f, height*0.56f, 0);

    // ゆっくり回転(3D感)
    rotateY(angleY);
    rotateX(angleX);
    angleY += 0.005;
    angleX += 0;

    // 上部 4x4 白グリッド(ONセルは赤)
    drawTopGrid();

    // 入力ベクトル x(0/1)
    float[] x = currentInputVector();

    // 前向き計算(sigmoid)
    float[] h = new float[3];
    for (int j = 0; j < 3; j++) {
      float s = 0;
      for (int i = 0; i < 16; i++) s += weights[0][j][i] * x[i];
      s += biases[0][j];
      h[j] = sigmoid(s);
    }
    o = new float[2];
    for (int k = 0; k < 2; k++) {
      float s = 0;
      for (int j = 0; j < 3; j++) s += weights[1][k][j] * h[j];
      s += biases[1][k];
      o[k] = sigmoid(s);
    }

    // 線の描画:太さ ∝ |activation * weight|
    if (ADDITIVE_GLOW) blendMode(ADD);
    drawEdges_InputToHidden(x);
    drawEdges_HiddenToOutput(h);
    blendMode(BLEND);

    // ノード(入力・隠れは球)
    drawNodes(lay0);
    drawNodes(lay1);

    // 出力は 3Dの○ と 3Dの× に置換(大きさ = o0, o1)
    drawOutputSymbols(o);

    popMatrix();
  }

  // ==== 右:入力パネル & 判定表示(2D HUD) ====
  drawGridPanel();
  drawPrediction(o);

  // ==== 右:AIくん(2D) ====
  drawAIKunWithSpeech(o);
}

// ================= 構築 =================
void buildTopGrid() {
  topGrid = new PVector[GRID_ROWS * GRID_COLS];
  float totalW = (GRID_COLS - 1) * GRID_SP;
  float totalH = (GRID_ROWS - 1) * GRID_SP;
  float ox = -totalW / 2f;
  float oz = -totalH / 2f;
  int idx = 0;
  for (int r = 0; r < GRID_ROWS; r++) {
    for (int c = 0; c < GRID_COLS; c++) {
      float x = ox + c * GRID_SP;
      float z = oz + r * GRID_SP;
      topGrid[idx++] = new PVector(x, GRID_Y, z);
    }
  }
}
void buildLayers() {
  lay0 = new PVector[GRID_ROWS * GRID_COLS];
  for (int i = 0; i < lay0.length; i++) {
    PVector g = topGrid[i];
    lay0[i] = new PVector(g.x, L0_Y, g.z);
  }
  lay1 = new PVector[3];
  float left1 = -L1_SPAN_X/2f, right1 = L1_SPAN_X/2f;
  for (int i = 0; i < 3; i++) {
    float t = i / 2f;
    lay1[i] = new PVector(lerp(left1, right1, t), L1_Y, 0);
  }
  lay2 = new PVector[2];
  float left2 = -L2_SPAN_X/2f, right2 = L2_SPAN_X/2f;
  for (int i = 0; i < 2; i++) {
    float t = i / 1f;
    lay2[i] = new PVector(lerp(left2, right2, t), L2_Y, 0);
  }
}
void computeWeightMaxima() {
  float mIH = 0;
  for (int j = 0; j < 3; j++)
    for (int i = 0; i < 16; i++)
      mIH = max(mIH, abs(weights[0][j][i]));
  maxW_IH = (mIH < 1e-6f) ? 1f : mIH;

  float mHO = 0;
  for (int k = 0; k < 2; k++)
    for (int j = 0; j < 3; j++)
      mHO = max(mHO, abs(weights[1][k][j]));
  maxW_HO = (mHO < 1e-6f) ? 1f : mHO;
}

// ================= 描画(3D) =================
void drawTopGrid() {
  int idx = 0;
  for (int r = 0; r < GRID_ROWS; r++) {
    for (int c = 0; c < GRID_COLS; c++) {
      PVector p = topGrid[idx++];
      boolean on = grid[r][c];
      stroke(on ? 0xffFF3A3A : COL_DOT);
      strokeWeight(on ? 3 : 2);
      point(p.x, p.y, p.z);
    }
  }
}
void drawNodes(PVector[] nodes) {
  noStroke();
  fill(COL_NODE);
  for (PVector p : nodes) {
    pushMatrix();
    translate(p.x, p.y, p.z);
    sphereDetail(6);
    sphere(NODE_R);
    popMatrix();
  }
}
void drawEdges_InputToHidden(float[] x) {
  for (int j = 0; j < 3; j++) {
    for (int i = 0; i < 16; i++) {
      float w = weights[0][j][i];
      float strength = abs(x[i] * w); // xは0/1
      float sw = map(strength, 0, maxW_IH, LINE_W_MIN, LINE_W_MAX);
      int col = (w >= 0) ? COL_LINE_POS : COL_LINE_NEG;
      stroke(red(col), green(col), blue(col), LINE_ALPHA);
      strokeWeight(sw);
      PVector a = lay0[i], b = lay1[j];
      line(a.x, a.y, a.z, b.x, b.y, b.z);
    }
  }
}
void drawEdges_HiddenToOutput(float[] h) {
  for (int k = 0; k < 2; k++) {
    for (int j = 0; j < 3; j++) {
      float w = weights[1][k][j];
      float strength = abs(h[j] * w); // h∈(0,1)
      float sw = map(strength, 0, maxW_HO, LINE_W_MIN, LINE_W_MAX);
      int col = (w >= 0) ? COL_LINE_POS : COL_LINE_NEG;
      stroke(red(col), green(col), blue(col), LINE_ALPHA);
      strokeWeight(sw);
      PVector a = lay1[j], b = lay2[k];
      line(a.x, a.y, a.z, b.x, b.y, b.z);
    }
  }
}

// 出力シンボル(3Dの○ と 3Dの×)
void drawOutputSymbols(float[] o) {
  // o[0] → ○(トーラス)
  float sO = map(constrain(o[0], 0, 1), 0, 1, O_SCALE_MIN, O_SCALE_MAX);
  pushMatrix();
  translate(lay2[0].x, lay2[0].y, lay2[0].z);
  scale(sO);
  noStroke();
  fill(240, 40, 40);
  drawTorus(O_MAJOR_R, O_TUBE_R, 42, 18);
  popMatrix();

  // o[1] → ×(交差する2本の棒)
  float sX = map(constrain(o[1], 0, 1), 0, 1, X_SCALE_MIN, X_SCALE_MAX);
  pushMatrix();
  translate(lay2[1].x, lay2[1].y, lay2[1].z);
  scale(sX);
  noStroke();
  fill(240, 40, 40);
  pushMatrix(); rotateZ(radians(45));  box(X_BAR_LEN, X_BAR_THICK, X_BAR_THICK); popMatrix();
  pushMatrix(); rotateZ(radians(-45)); box(X_BAR_LEN, X_BAR_THICK, X_BAR_THICK); popMatrix();
  popMatrix();
}

// トーラス描画(R:大半径, r:チューブ半径, segR/segT:セグメント数)
void drawTorus(float R, float r, int segR, int segT) {
  for (int i = 0; i < segR; i++) {
    float u0 = TWO_PI * i / segR;
    float u1 = TWO_PI * (i+1) / segR;
    beginShape(QUAD_STRIP);
    for (int j = 0; j <= segT; j++) {
      float v = TWO_PI * j / segT;
      float x1 = (R + r * cos(v)) * cos(u0);
      float y1 = (R + r * cos(v)) * sin(u0);
      float z1 =  r * sin(v);
      float x2 = (R + r * cos(v)) * cos(u1);
      float y2 = (R + r * cos(v)) * sin(u1);
      float z2 =  r * sin(v);
      vertex(x1, y1, z1);
      vertex(x2, y2, z2);
    }
    endShape();
  }
}

// ================= 入力パネル / HUD =================
void drawGridPanel() {
  hint(DISABLE_DEPTH_TEST);
  camera();
  noLights();

  // パネル枠
  fill(255, 255, 255, 12);
  stroke(COL_PANEL_BORDER);
  rect(PANEL_X - 20, PANEL_Y - 50, 4*CELL + 3*GAP + 40, 4*CELL + 3*GAP + 140, 12);

  fill(255);
  textSize(16);
  textAlign(LEFT, TOP);
  text("4×4 入力(クリックでON/OFF)", PANEL_X - 8, PANEL_Y - 36);

  // グリッド
  for (int r = 0; r < GRID_ROWS; r++) {
    for (int c = 0; c < GRID_COLS; c++) {
      int x = PANEL_X + c*(CELL + GAP);
      int y = PANEL_Y + r*(CELL + GAP);
      boolean on = grid[r][c];

      if (on) fill(35); else fill(240);
      stroke(170);
      rect(x, y, CELL, CELL, 10);

      fill(on ? 255 : 90);
      textAlign(CENTER, CENTER);
      textSize(12);
      int idx = r*GRID_COLS + c;
      text("In(" + idx + ")", x + CELL/2, y + CELL/2);
    }
  }

  // Clear ボタン
  int bx = PANEL_X - 8, by = PANEL_Y + 4*(CELL + GAP) + 20, bw = 120, bh = 32;
  fill(250); stroke(180); rect(bx, by, bw, bh, 6);
  fill(0); textAlign(CENTER, CENTER); textSize(14); text("Clear [C]", bx + bw/2, by + bh/2);

  hint(ENABLE_DEPTH_TEST);
}
void drawPrediction(float[] o) {
  int label = (o[0] >= o[1]) ? 0 : 1; // 0:〇, 1:×
  String verdict = (label == 0) ? "〇 (Maru)" : "× (Batsu)";

  hint(DISABLE_DEPTH_TEST);
  camera();
  noLights();
  fill(255);
  textAlign(LEFT, TOP);
  textSize(18);
  int y0 = PANEL_Y + 4*(CELL + GAP) + 70;
  text("判定: " + verdict, PANEL_X - 8, y0);
  textSize(14);
  text(String.format("o0(〇)=%.3f   o1(×)=%.3f", o[0], o[1]), PANEL_X - 8, y0 + 26);
  hint(ENABLE_DEPTH_TEST);
}

// ==== 置き換え版:AIくんを入力グリッドの「下」に配置 ====
void drawAIKunWithSpeech(float[] o) {
  // 2D HUD座標系
  hint(DISABLE_DEPTH_TEST);
  camera();
  noLights();

  // 入力パネルの寸法
  float panelContentW = 4*CELL + 3*GAP;                // パネル内の正味幅
  float panelBottomY  = PANEL_Y + 4*(CELL + GAP);      // グリッド最下端Y

  // --- AIくんの位置(パネルの中央X、グリッドの下に余白をとって配置) ---
  float headR = 36;
  float ax = PANEL_X - panelContentW/2f;               // 中央X
  float ay = panelBottomY + headR + 60;                // 下に60px余白

  // 1) AIくん(棒人間:顔に「AI」)
  noStroke();
  fill(255);
  ellipse(ax, ay, headR*2, headR*2);                   // 顔
  stroke(60); strokeWeight(2); noFill();
  ellipse(ax, ay, headR*2, headR*2);                   // 顔の縁
  fill(30); textAlign(CENTER, CENTER); textSize(16);
  text("AI", ax, ay);

  // 体
  stroke(220); strokeWeight(3);
  line(ax, ay + headR, ax, ay + headR + 70);           // 胴
  line(ax, ay + headR + 20, ax - 28, ay + headR + 55); // 左腕
  line(ax, ay + headR + 20, ax + 28, ay + headR + 55); // 右腕
  line(ax, ay + headR + 70, ax - 24, ay + headR + 110);// 左脚
  line(ax, ay + headR + 70, ax + 24, ay + headR + 110);// 右脚

  // 2) 吹き出し(楕円):「これはXX%で(○/×)です。」
  float p0 = o[0], p1 = o[1];
  boolean isMaru = (p0 >= p1);
  float p = max(p0, p1);
  int percent = round(p * 100);
  String symbol = isMaru ? "o" : "×";
  String msg = String.format("This is a  %s  with  %d%%  probability", symbol, percent);

  // 吹き出しを AIくんの「上」に出す(下に配置したので上方向に)
  float bw = 280, bh = 95;                             // バブルの幅・高さ
  float bx = ax;                                       // 中央X
  float by = ay - headR - 70;                          // 頭の少し上

  // 楕円バブル
  noStroke();
  fill(255, 245);
  ellipse(bx, by, bw, bh);
  stroke(60); strokeWeight(2); noFill();
  ellipse(bx, by, bw, bh);

  // 尻尾(バブル右下→AIくんの口元へ)
  float tailX = ax; 
  float tailY = ay - headR * 0.2f;
  line(bx + bw*0.22f, by + bh*0.28f, tailX, tailY);

  // 吹き出しテキスト
  fill(0);
  textAlign(CENTER, CENTER);
  textSize(16);
  text(msg, bx, by);

  hint(ENABLE_DEPTH_TEST);
}


// ================= 入力操作 =================
void mousePressed() {
  // グリッドクリックでON/OFF
  for (int r = 0; r < GRID_ROWS; r++) {
    for (int c = 0; c < GRID_COLS; c++) {
      int x = PANEL_X + c*(CELL + GAP);
      int y = PANEL_Y + r*(CELL + GAP);
      if (mouseX >= x && mouseX <= x + CELL && mouseY >= y && mouseY <= y + CELL) {
        grid[r][c] = !grid[r][c];
        return;
      }
    }
  }
  // クリアボタン
  int bx = PANEL_X - 8, by = PANEL_Y + 4*(CELL + GAP) + 20, bw = 120, bh = 32;
  if (mouseX >= bx && mouseX <= bx + bw && mouseY >= by && mouseY <= by + bh) clearGrid();
}
void keyPressed() {
  if (key == 'c' || key == 'C') clearGrid();
}
void clearGrid() {
  for (int r = 0; r < GRID_ROWS; r++)
    for (int c = 0; c < GRID_COLS; c++)
      grid[r][c] = false;
}

// ================= ユーティリティ =================
float[] currentInputVector() {
  float[] x = new float[16];
  for (int r = 0; r < GRID_ROWS; r++)
    for (int c = 0; c < GRID_COLS; c++)
      x[r*GRID_COLS + c] = grid[r][c] ? 1f : 0f;
  return x;
}
float sigmoid(float v) { return 1.0f / (1.0f + exp(-v)); }

実行すると、、

ヒストグラムあり版

// 4x4 -> 3 -> 2 3D NN Visualizer
// - 最上段:独立した4×4タッチ風グリッド(ONで赤点)
 //  中段:16→3→2ネットワーク(回転、線太さ=活性×重み)
 //  右:4×4入力ボタン(クリックON/OFF)
 //  中央右:〇/×の横棒ヒストグラム(確率表示)
// Processing 4 / Java mode / P3D

// ====== レイアウト / 見た目 ======
final int CANVAS_W = 1280;
final int CANVAS_H = 900;

final int GRID_ROWS = 4, GRID_COLS = 4;  // 入力は4x4=16
final float GRID_SP = 70;                // 最上段グリッド点間隔
final float GRID_Y  = -360;              // 最上段グリッド高さ

final float L0_Y = -180f;                // 入力(16)
final float L1_Y =  -20f;                // 隠れ(3)
final float L2_Y =  140f;                // 出力(2)
final float L1_SPAN_X = 260;             // 隠れの横幅
final float L2_SPAN_X = 150;             // 出力の横幅

final float NODE_R = 4.0f;               // ノード球半径

final boolean ADDITIVE_GLOW = true;      // 加算合成で発光
final int COL_BG   = 0xff0E0E10;         // 背景
final int COL_DOT  = 0xffE8E8E8;         // 白点(最上段)
final int COL_NODE = 0xffC82828;         // ノード
final int COL_LINE_POS = 0xffFF3A3A;     // 正の重み(赤)
final int COL_LINE_NEG = 0xff2A6AFF;     // 負の重み(青)
final int LINE_ALPHA = 85;               // 線の透明度
final float LINE_W_MIN = 0.4f;           // 線太さ min
final float LINE_W_MAX = 5.0f;           // 線太さ max

// 出力シンボル(3D ○/× の代わりに今回は横棒ヒストグラムを追加)
final int  BAR_AREA_X = 640;     // ヒストグラム描画領域の開始X(3D原点からの相対ではなく2D HUDで描く)
final int  BAR_AREA_Y = 160;
final int  BAR_W_MAX  = 280;     // 横棒の最大幅
final int  BAR_H      = 22;
final int  BAR_GAP    = 16;
final int  COL_BAR_BG = 0xff2A2A2E;
final int  COL_BAR_O  = 0xff38D973;   // 〇 のバー色(緑寄り)
final int  COL_BAR_X  = 0xffFF6B6B;   // × のバー色(赤寄り)
final int  COL_TEXT   = 0xffEDEDED;

// 右側 UI(入力パネル)
final int PANEL_X = 900, PANEL_Y = 120;
final int CELL = 80, GAP = 10;
final int COL_PANEL_BORDER = 0xffC8C8C8;

// ====== NN パラメータ(16->3->2:提供値) ======
float[][][] weights = new float[][][] {
  { // 入力(16) -> 隠れ(3)
    {
      -0.19556837184815432, -2.0731887662694675, -0.7652514121341945, -1.9884231323546566,
      -4.952637298076189,   5.659829256271411,  -0.5713695038053532, -1.4846930025289022,
      -1.4970230006948473,  2.5726393101553975, -0.2504789124544582,  1.480466604318017,
      -0.8298236501477579, -4.5199641859013955, -1.4981634293951982,  2.011290351680955
    },
    {
      -3.4835715114090715,  5.010347806892839,  2.9247242936422113, -4.194203236742492,
      -0.17924275715765517, -2.403425422788088,  0.40385725801892475, 1.8787273651616636,
       2.047521825332689,  -0.16374517197602814, 0.47853555594913116, 5.6545162920862575,
      -6.058153846339116,   0.42470424485595815,-0.8371742892189643, -2.879606740652077
    },
    {
       2.884860808289696,   0.23975579237277614,-1.2403687366139762, -2.3025676808322486,
      -0.7475210018222626, -6.5294340577398575, -0.559934356147751,   4.0010381968700575,
       5.0747200536885,    -4.225817417542366,   0.3105588568588239,  3.308254461438346,
       4.348740489196476,   1.5652833651727298, -4.873553428254676,  -1.3786319596139733
    }
  },
  { // 隠れ(3) -> 出力(2)
    { -8.832622109783433,  8.303120604609045, -6.0039452831036275 },
    {  8.827857967700506, -8.306736033721686,  6.003293693110446  }
  }
};
float[][] biases = new float[][] {
  { 6.286020903686753, 3.2317338139016565, 2.8075640195084577 },  // 隠れ
  { 2.626446601080335, -2.618405339273877 }                        // 出力
};

// ====== 内部ワーク ======
PVector[] topGrid;   // 最上段 4x4 白点(独立の“タッチ面”)
PVector[] lay0;      // 入力(16)
PVector[] lay1;      // 隠れ(3)
PVector[] lay2;      // 出力(2)
boolean[][] grid = new boolean[GRID_ROWS][GRID_COLS]; // 入力パネル(0/1)
float angleY = 0, angleX = 0;

// 正規化用:|activation*weight| の最大想定
float maxW_IH = 1f; // 入力->隠れ
float maxW_HO = 1f; // 隠れ->出力

void settings() {
  size(CANVAS_W, CANVAS_H, P3D);
  smooth(8);
}
void setup() {
  surface.setTitle("4x4 -> 3 -> 2 : top touch-like grid + bars (○/×)");
  buildTopGrid();
  buildLayers();
  clearGrid();
  computeWeightMaxima();
}

void draw() {
  background(COL_BG);
  lights();

  // ==== 左:3Dビジュアライズ ====
  float[] o; // 出力
  {
    pushMatrix();
    translate(width*0.30f, height*0.56f, 0);

    // ゆっくり回転(3D感)
    rotateY(angleY);
    rotateX(angleX);
    angleY += 0.005;
    angleX += 0;

    // 最上段 4x4 タッチ風グリッド(ONで赤)
    drawTopGrid();   // ←ネットワーク非接続の“見た目面”

    // 入力ベクトル x(0/1)— 右パネルのON/OFFが反映
    float[] x = currentInputVector();

    // 前向き計算(sigmoid)
    float[] h = new float[3];
    for (int j = 0; j < 3; j++) {
      float s = 0;
      for (int i = 0; i < 16; i++) s += weights[0][j][i] * x[i];
      s += biases[0][j];
      h[j] = sigmoid(s);
    }
    o = new float[2];
    for (int k = 0; k < 2; k++) {
      float s = 0;
      for (int j = 0; j < 3; j++) s += weights[1][k][j] * h[j];
      s += biases[1][k];
      o[k] = sigmoid(s);
    }

    // 線の描画:太さ ∝ |activation * weight|
    if (ADDITIVE_GLOW) blendMode(ADD);
    drawEdges_InputToHidden(x);
    drawEdges_HiddenToOutput(h);
    blendMode(BLEND);

    // ノード
    drawNodes(lay0);
    drawNodes(lay1);
    drawNodes(lay2);
    popMatrix();
  }

  // ==== 中央右:2D横棒ヒストグラム(○/×) ====
  drawBars(o);

  // ==== 右:入力パネル(クリックON/OFF) ====
  drawGridPanel();
}

// ================= 構築 =================
void buildTopGrid() {
  topGrid = new PVector[GRID_ROWS * GRID_COLS];
  float totalW = (GRID_COLS - 1) * GRID_SP;
  float totalH = (GRID_ROWS - 1) * GRID_SP;
  float ox = -totalW / 2f;
  float oz = -totalH / 2f;
  int idx = 0;
  for (int r = 0; r < GRID_ROWS; r++) {
    for (int c = 0; c < GRID_COLS; c++) {
      float x = ox + c * GRID_SP;
      float z = oz + r * GRID_SP;
      topGrid[idx++] = new PVector(x, GRID_Y, z);
    }
  }
}
void buildLayers() {
  // 入力(16):最上段グリッドと同じ x,z に“少し下”へ配置
  lay0 = new PVector[GRID_ROWS * GRID_COLS];
  for (int i = 0; i < lay0.length; i++) {
    PVector g = topGrid[i];
    lay0[i] = new PVector(g.x, L0_Y, g.z);
  }
  // 隠れ(3)
  lay1 = new PVector[3];
  float left1 = -L1_SPAN_X/2f, right1 = L1_SPAN_X/2f;
  for (int i = 0; i < 3; i++) {
    float t = i / 2f;
    lay1[i] = new PVector(lerp(left1, right1, t), L1_Y, 0);
  }
  // 出力(2)
  lay2 = new PVector[2];
  float left2 = -L2_SPAN_X/2f, right2 = L2_SPAN_X/2f;
  for (int i = 0; i < 2; i++) {
    float t = i / 1f;
    lay2[i] = new PVector(lerp(left2, right2, t), L2_Y, 0);
  }
}
void computeWeightMaxima() {
  float mIH = 0;
  for (int j = 0; j < 3; j++)
    for (int i = 0; i < 16; i++)
      mIH = max(mIH, abs(weights[0][j][i]));
  maxW_IH = (mIH < 1e-6f) ? 1f : mIH;

  float mHO = 0;
  for (int k = 0; k < 2; k++)
    for (int j = 0; j < 3; j++)
      mHO = max(mHO, abs(weights[1][k][j]));
  maxW_HO = (mHO < 1e-6f) ? 1f : mHO;
}

// ================= 描画(3D) =================
void drawTopGrid() {
  // 右パネルの状態(grid[][])をミラーして、ONセルだけ赤点に
  int idx = 0;
  for (int r = 0; r < GRID_ROWS; r++) {
    for (int c = 0; c < GRID_COLS; c++) {
      PVector p = topGrid[idx++];
      boolean on = grid[r][c];
      stroke(on ? 0xffFF3A3A : COL_DOT);
      strokeWeight(on ? 3 : 2);
      point(p.x, p.y, p.z);
    }
  }
}
void drawNodes(PVector[] nodes) {
  noStroke();
  fill(COL_NODE);
  for (PVector p : nodes) {
    pushMatrix();
    translate(p.x, p.y, p.z);
    sphereDetail(6);
    sphere(NODE_R);
    popMatrix();
  }
}
void drawEdges_InputToHidden(float[] x) {
  for (int j = 0; j < 3; j++) {
    for (int i = 0; i < 16; i++) {
      float w = weights[0][j][i];
      float strength = abs(x[i] * w); // xは0/1
      float sw = map(strength, 0, maxW_IH, LINE_W_MIN, LINE_W_MAX);
      int col = (w >= 0) ? COL_LINE_POS : COL_LINE_NEG;
      stroke(red(col), green(col), blue(col), LINE_ALPHA);
      strokeWeight(sw);
      PVector a = lay0[i], b = lay1[j];
      line(a.x, a.y, a.z, b.x, b.y, b.z);
    }
  }
}
void drawEdges_HiddenToOutput(float[] h) {
  for (int k = 0; k < 2; k++) {
    for (int j = 0; j < 3; j++) {
      float w = weights[1][k][j];
      float strength = abs(h[j] * w); // h∈(0,1)
      float sw = map(strength, 0, maxW_HO, LINE_W_MIN, LINE_W_MAX);
      int col = (w >= 0) ? COL_LINE_POS : COL_LINE_NEG;
      stroke(red(col), green(col), blue(col), LINE_ALPHA);
      strokeWeight(sw);
      PVector a = lay1[j], b = lay2[k];
      line(a.x, a.y, a.z, b.x, b.y, b.z);
    }
  }
}

// ================= 2D 横棒ヒストグラム(○/×) =================
void drawBars(float[] o) {
  // 2D HUD(画面座標)で描画
  hint(DISABLE_DEPTH_TEST);
  camera();
  noLights();

  // 背景パネル
  fill(255, 255, 255, 12);
  stroke(140);
  rect(BAR_AREA_X, BAR_AREA_Y - 50, BAR_W_MAX + 160, BAR_H*2 + BAR_GAP + 120, 12);

  // タイトル
  fill(COL_TEXT);
  textAlign(LEFT, TOP);
  textSize(16);
  text("〇 / × の確率(横棒)", BAR_AREA_X + 10, BAR_AREA_Y - 36);

  // 値
  float pO = constrain(o[0], 0, 1);
  float pX = constrain(o[1], 0, 1);

  // 1本目(○)
  int x0 = BAR_AREA_X + 20;
  int y0 = BAR_AREA_Y;
  fill(COL_BAR_BG); noStroke();
  rect(x0, y0, BAR_W_MAX, BAR_H, 6);
  fill(COL_BAR_O);
  rect(x0, y0, BAR_W_MAX * pO, BAR_H, 6);

  // 2本目(×)
  int y1 = y0 + BAR_H + BAR_GAP;
  fill(COL_BAR_BG);
  rect(x0, y1, BAR_W_MAX, BAR_H, 6);
  fill(COL_BAR_X);
  rect(x0, y1, BAR_W_MAX * pX, BAR_H, 6);

  // ラベル&数値
  fill(COL_TEXT);
  textAlign(LEFT, CENTER);
  textSize(14);
  text("〇", x0 - 20, y0 + BAR_H/2f);
  text("×", x0 - 20, y1 + BAR_H/2f);

  textAlign(LEFT, CENTER);
  text(nf(pO*100, 0, 1) + "%", x0 + BAR_W_MAX + 10, y0 + BAR_H/2f);
  text(nf(pX*100, 0, 1) + "%", x0 + BAR_W_MAX + 10, y1 + BAR_H/2f);

  hint(ENABLE_DEPTH_TEST);
}

// ================= 右側:入力パネル(ローカル操作) =================
void drawGridPanel() {
  hint(DISABLE_DEPTH_TEST);
  camera();
  noLights();

  // パネル枠
  fill(255, 255, 255, 12);
  stroke(COL_PANEL_BORDER);
  rect(PANEL_X - 20, PANEL_Y - 50, 4*CELL + 3*GAP + 40, 4*CELL + 3*GAP + 140, 12);

  fill(255);
  textSize(16);
  textAlign(LEFT, TOP);
  text("4×4 入力(クリックでON/OFF)", PANEL_X - 8, PANEL_Y - 36);

  // グリッド
  for (int r = 0; r < GRID_ROWS; r++) {
    for (int c = 0; c < GRID_COLS; c++) {
      int x = PANEL_X + c*(CELL + GAP);
      int y = PANEL_Y + r*(CELL + GAP);
      boolean on = grid[r][c];

      if (on) fill(35); else fill(240);
      stroke(170);
      rect(x, y, CELL, CELL, 10);

      fill(on ? 255 : 90);
      textAlign(CENTER, CENTER);
      textSize(12);
      int idx = r*GRID_COLS + c;
      text("In(" + idx + ")", x + CELL/2, y + CELL/2);
    }
  }

  // Clear ボタン
  int bx = PANEL_X - 8, by = PANEL_Y + 4*(CELL + GAP) + 20, bw = 120, bh = 32;
  fill(250); stroke(180); rect(bx, by, bw, bh, 6);
  fill(0); textAlign(CENTER, CENTER); textSize(14); text("Clear [C]", bx + bw/2, by + bh/2);

  hint(ENABLE_DEPTH_TEST);
}

// ================= 入力操作 =================
void mousePressed() {
  // グリッドクリックでON/OFF
  for (int r = 0; r < GRID_ROWS; r++) {
    for (int c = 0; c < GRID_COLS; c++) {
      int x = PANEL_X + c*(CELL + GAP);
      int y = PANEL_Y + r*(CELL + GAP);
      if (mouseX >= x && mouseX <= x + CELL && mouseY >= y && mouseY <= y + CELL) {
        grid[r][c] = !grid[r][c];
        return;
      }
    }
  }
  // クリアボタン
  int bx = PANEL_X - 8, by = PANEL_Y + 4*(CELL + GAP) + 20, bw = 120, bh = 32;
  if (mouseX >= bx && mouseX <= bx + bw && mouseY >= by && mouseY <= by + bh) clearGrid();
}
void keyPressed() {
  if (key == 'c' || key == 'C') clearGrid();
}
void clearGrid() {
  for (int r = 0; r < GRID_ROWS; r++)
    for (int c = 0; c < GRID_COLS; c++)
      grid[r][c] = false;
}

// ================= ユーティリティ =================
float[] currentInputVector() {
  float[] x = new float[16];
  for (int r = 0; r < GRID_ROWS; r++)
    for (int c = 0; c < GRID_COLS; c++)
      x[r*GRID_COLS + c] = grid[r][c] ? 1f : 0f;
  return x;
}
float sigmoid(float v) { return 1.0f / (1.0f + exp(-v)); }

ノード数16→3→2、4×4の入力パネルで入力をし、まるばつ判定をするニューラルネットワークの可視化に成功した。だが実際はもっとぬるぬる動くようなプログラムを書きたい。だが、実力が足りない、、
こんな感じで作成してみたがまだまだわからないことばかりだ。
現在大学4年生。社会人まで残り半年だが、有意義な研究をしたい。
この夏はprocessingでできることを学びつつ、図形の作成をしていきたい。

Discussion