Dot Product

1
a.dot(b) = a.x * b.x + a.y * b.y
dot-product.js (source code)
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
{
  const MARKER_COUNT = 16;
  const ROOT = window.getComputedStyle(document.documentElement);

  const ANGLE_INCREMENT = Two.Utils.TWO_PI / MARKER_COUNT;
  const REM = parseInt(ROOT.fontSize);

  const COLOR_SQUARE_SIZE = REM;
  const MARKER_LINE_LENGTH = REM / 2;
  const MARKER_MARGIN = REM * 2.5;
  const RADIUS = REM * 10;

  ///////////
  // Setup //
  ///////////

  const two = new Two({
    autostart: true,
    fitted: true,
  }).appendTo(document.currentScript.parentNode);

  const center = new Two.Vector(two.width, two.height).divide(2);

  ////////////
  // Circle //
  ////////////

  const circle = two.makeCircle(center.x, center.y, RADIUS);
  circle.fill = "transparent";
  circle.stroke = Colors.gray[700];

  /////////////
  // Vectors //
  /////////////

  const dotProductProjection = two.makeLine();
  dotProductProjection.dashes = [REM / 2];
  dotProductProjection.position = center.clone();
  dotProductProjection.stroke = Colors.gray[700];

  // Static vector.
  two.makeVector({
    color: Colors.green[500],
    position: center.clone(),
    value: Two.Vector.up.clone().multiply(RADIUS),
  });

  const dotProductVector = two.makeVector({
    color: Colors.blue[500],
    position: center.clone(),
    value: Two.Vector.zero.clone(),
  });

  const movingVector = two.makeVector({
    color: Colors.red[500],
    drawCircle: true,
    position: center.clone(),
    value: Two.Vector.zero.clone(),
  });

  //////////
  // Text //
  //////////

  const topLeftTextStyle = {
    alignment: "left",
    baseline: "top",
    family: "Hack",
    fill: Colors.gray[50],
    size: REM,
  };

  two.makeText("a.length() = 1.00", REM * 3, REM * 1.75, topLeftTextStyle);
  two.makeText("b.length() = 1.00", REM * 3, REM * 3.75, topLeftTextStyle);
  const dotProductText = two.makeText("", REM * 3, REM * 5.75, topLeftTextStyle);

  const redSquare = two.makeRectangle(REM * 1.5, REM * 1.5, COLOR_SQUARE_SIZE, COLOR_SQUARE_SIZE);
  redSquare.fill = Colors.red[500];
  redSquare.stroke = "transparent";

  const greenSquare = two.makeRectangle(REM * 1.5, REM * 3.5, COLOR_SQUARE_SIZE, COLOR_SQUARE_SIZE);
  greenSquare.fill = Colors.green[500];
  greenSquare.stroke = "transparent";

  const blueSquare = two.makeRectangle(REM * 1.5, REM * 5.5, COLOR_SQUARE_SIZE, COLOR_SQUARE_SIZE);
  blueSquare.fill = Colors.blue[500];
  blueSquare.stroke = "transparent";

  for (let i = 0; i < MARKER_COUNT; i++) {
    const angle = ANGLE_INCREMENT * i;
    const x = Math.cos(angle);
    const y = Math.sin(angle);
    const direction = new Two.Vector(x, y);
    const border = direction.clone().multiply(RADIUS).add(center);
    const borderOffset = direction.clone().multiply(MARKER_LINE_LENGTH);
    const line = two.makeLine(
      border.x,
      border.y,
      border.x + borderOffset.x,
      border.y + borderOffset.y
    );
    line.stroke = Colors.gray[700];
    const dotProduct = direction.dot(Two.Vector.up).toFixed(2);
    const textOffset = direction.multiply(MARKER_MARGIN);
    const textPosition = border.add(textOffset);
    two.makeText(dotProduct, textPosition.x, textPosition.y, {
      family: "Hack",
      fill: Colors.gray[50],
      size: REM,
    });
  }

  let angle = 0.0;
  two.bind("update", () => {
    angle = (angle - two.timeDelta / 1000.0) % Two.Utils.TWO_PI;
    const x = Math.cos(angle) * RADIUS;
    const y = Math.sin(angle) * RADIUS;
    const a = new Two.Vector(x, y).normalize();
    const dotProduct = a.dot(Two.Vector.up).toFixed(2);
    movingVector.value.x = x;
    movingVector.value.y = y;
    dotProductVector.value.y = y;
    dotProductProjection.vertices[0].x = dotProductVector.value.x;
    dotProductProjection.vertices[0].y = dotProductVector.value.y;
    dotProductProjection.vertices[1].x = movingVector.value.x;
    dotProductProjection.vertices[1].y = movingVector.value.y;
    dotProductText.value = `a.dot(b) = ${dotProduct}`;
  });
}

Thanks to Freya Holmér for her pretty visualization.