Gobble up pudding

プログラミングの記事がメインのブログです。

MENU

Processingで内積・外積の勉強

スポンサードリンク

f:id:fa11enprince:20200121024039j:plain ひょんなことから内積・外積をちゃんと学びなおしたいと思い…
文系であった自分はあまりなじみがないのです。物理も選択せず生物選択でした。
数学は嫌いではないんだけど、計算が得意でなかったです。
とはいえ行列をやっていた関係もあり、全く知らないというわけではないのです。
それに最近避けたいのに避けられない機械学習でもというかnumpyというか、dot、dot、dotばっかりな気がしていますので改めて復習。 グラフィカルに確認したいなと思い、いろいろ見てました。 そもそも何に使うか…

  • 内積:矩形は別の判定方法が簡単に思いつくけど、三角形や円にたいしてある点が当たっているかどうか。(衝突判定)とか2つのベクトルがわかっているときにその角度が鋭角か鈍角か求めたいとき
  • 外積:キャラがジャンプした時の高さを求める

内積や外積が難しいのはそもそも何に使うんよ…ってのが分かりにくいからだと思います。 自分の理解したざっくり定義では、

  • 内積:ある方向に引っ張ったときに元となるベクトルと方向を合わせて水平方向に引っ張れる力や量的なモノ
  • 外積:ある方向に引っ張ったときに元となるベクトルと垂直方向に引っ張れる力や量的なモノ

と理解しました(あってんのか?)てかこれ物理とってたら習う?
ちなみにですが内積については類似度っていう考え方もあるみたいです。

個人的にはここがわかりやすかったです。 基礎の基礎編その1 内積と外積の使い方
理屈だけわかっても仕方ないんで、「内積 衝突判定」でググってみました。
すると、なんかコード例が書いてて素敵!
円と線分の当たり判定を行うには | 自己啓発。人生について考える
なんだこのJavaっぽい言語は…一瞬TypeScriptにも見えるしな…と思い、調べたらProcessingでした。 自分もProcessingで書いてみました。ほうほう…内部でJavaのSwing使ってんのね。
余談ですがProcessing書いてるとPythonとかJavaScript(ES6以降)っぽいなーとうっすら思います。

ソースコード

class Point2D {
  float x;
  float y;
  Point2D(float x, float y) {
    this.x = x;
    this.y = y;
  }
  void disp(color c, float diameter) {
    stroke(c);
    strokeWeight(diameter);
    point(this.x, this.y);
  }
};

class Vector2D {
  float x;
  float y;
  float len;
  Vector2D(float x, float y) {
    this.x = x;
    this.y = y;
  }
  Vector2D(Point2D pointA, Point2D pointB) {
    this.x = pointB.x - pointA.x;
    this.y = pointB.y - pointA.y;
    this.len = dist(pointA.x, pointA.y, pointB.x, pointB.y);
  }
  Vector2D unit() {
    return new Vector2D(this.x / this.len, this.y / this.len);
  }
  float dot(Vector2D vec) {
    return this.x * vec.x + this.y * vec.y;
  }
  float corss(Vector2D vec) {
    return this.x * vec.y - this.y * vec.x;
  }
}

final color BLACK = color(0, 0, 0);
final color WHITE = color(255, 255, 255);
final color BLUE = color(0, 0, 255);
final color GREEN = color(0, 255, 0);
final color RED = color(255, 0, 0);
final float diameter = 20;
final float diameterCenter = 100;
Point2D pointA;
Point2D pointB;
Point2D pointP;

void setup() {
  size(300, 300);
  pointA = new Point2D(20, 20);
  pointB = new Point2D(260, 260);
  pointP = new Point2D(150, 150);
}

void draw() {
  background(WHITE);
  noFill();
  
  stroke(BLACK);
  strokeWeight(2);
  line(pointA.x, pointA.y, pointB.x, pointB.y);
  pointA.disp(BLUE, diameter);
  pointB.disp(GREEN, diameter);

  Vector2D vecAB = new Vector2D(pointA, pointB);
  Vector2D vecAP = new Vector2D(pointA, pointP);
  Vector2D vecBP = new Vector2D(pointB, pointP);

  color c;
  if (isCollision(vecAB, vecAP, vecBP)) {
    drawInnerPointOfCircle(vecAB, vecAP);
    c = RED;
  } else {
    c = BLACK;
  }
  drawCircle(c, pointP);
}

void drawInnerPointOfCircle(Vector2D vecAB, Vector2D vecAP) {
  Vector2D unit = vecAB.unit();
  float len = unit.dot(vecAP);
  Point2D pointX = new Point2D(pointA.x + (unit.x * len), pointA.y + (unit.y * len));
  pointX.disp(color(100,100,255), diameter);
}

void drawCircle(color c, Point2D pointP) {
  strokeWeight(2);
  stroke(c);
  ellipse(pointP.x, pointP.y, diameterCenter, diameterCenter);
}

boolean isCollision(Vector2D vecAB, Vector2D vecAP, Vector2D vecBP) {
  float lenAX = vecAB.unit().dot(vecAP);
  return shortestDistance(lenAX, vecAB, vecAP, vecBP) < diameterCenter / 2;
}

float shortestDistance(float len, Vector2D ab, Vector2D ap, Vector2D bp) {
  if (len < 0 ) {
    return ap.len;
  } else if (len > ab.len) {
    return bp.len;
  } else {
    return abs(ab.unit().corss(ap));
  }
}

void mouseDragged() {
  if (isDragged(pointA, diameter)) {
    pointA.x = mouseX;
    pointA.y = mouseY;
    return;
  }
  if (isDragged(pointB, diameter)) {
    pointB.x = mouseX;
    pointB.y = mouseY;
    return;
  }
}

boolean isDragged(Point2D point, float diameter) {
  return dist(mouseX, mouseY, point.x, point.y) < diameter / 2;
}

スクリーンショット

f:id:fa11enprince:20200121022851p:plain

ちなみにProcessingのダウンロードはこちら https://processing.org/ 最新版だとJava 8が内包されている様子。
JavaFXは外部でメンテナンスだけどSwingは残りましたね…。