Skip to content

Charming Vector ​

Charming Vector provides 2D vectors for simulating physical laws, such as positions, velocities and forces. Each operation comes in two forms: a function such as cm.vecAdd(a, b), which returns a new vector, and a method such as vec.add(b), which changes the vector in place and returns it.

js
(() => {
  const renderer = cm.canvas({width: 640, height: 200});
  const root = cm.flow().renderer(renderer);
  const node = renderer.node();
  const mover = {location: cm.vec(320, 50), velocity: cm.vec(3, 0)};
  const gravity = cm.vec(0, 0.3);
  let started = false;
  let frame = requestAnimationFrame(function tick() {
    if (!node.isConnected && started) return;
    started = true;
    mover.velocity.add(gravity);
    mover.location.add(mover.velocity);
    if (mover.location.y > 180) mover.velocity.setY(-Math.abs(mover.velocity.y) * 0.95);
    if (mover.location.x < 20 || mover.location.x > 620) mover.velocity.setX(-mover.velocity.x);
    root.append(cm.clear, {fill: "white"});
    root.datum(mover).append(cm.circle, {x: (d) => d.location.x, y: (d) => d.location.y, r: 20, fill: "black"});

    root.render();
    frame = requestAnimationFrame(tick);
  });
  unsubscribe(() => cancelAnimationFrame(frame));

  return node;
})();
js
const mover = {location: cm.vec(320, 50), velocity: cm.vec(3, 0)};
const gravity = cm.vec(0, 0.3);

requestAnimationFrame(function frame() {
  mover.velocity.add(gravity);
  mover.location.add(mover.velocity);
  if (mover.location.y > 180) mover.velocity.setY(-Math.abs(mover.velocity.y) * 0.95);
  if (mover.location.x < 20 || mover.location.x > 620) mover.velocity.setX(-mover.velocity.x);
  root.append(cm.clear, {fill: "white"});
  root.datum(mover).append(cm.circle, {x: (d) => d.location.x, y: (d) => d.location.y, r: 20, fill: "black"});

  root.render();
  requestAnimationFrame(frame);
});

cm.vec([x[, y]]) ​

Returns a vector with the specified x and y components, each defaulting to 0.

js
cm.vec(); // {x: 0, y: 0}
cm.vec(1); // {x: 1, y: 0}
cm.vec(2, 3); // {x: 2, y: 3}

cm.vecFromAngle(angle) ​

Returns the unit vector at the specified angle in radians.

js
cm.vecFromAngle(Math.PI / 4); // {x: 0.7071…, y: 0.7071…}

cm.vecRandom() ​

Returns a unit vector in a random direction.

js
cm.vecRandom().mag(); // 1

cm.vecAdd(a, b) ​

Returns the sum of the vectors a and b.

js
cm.vecAdd(cm.vec(1, 2), cm.vec(2, 3)); // {x: 3, y: 5}

cm.vecSub(a, b) ​

Returns the difference of the vectors a and b.

js
cm.vecSub(cm.vec(1, 2), cm.vec(2, 4)); // {x: -1, y: -2}

cm.vecMult(a, value) ​

Returns the vector a multiplied by value.

js
cm.vecMult(cm.vec(3, 4), 2); // {x: 6, y: 8}

cm.vecDiv(a, value) ​

Returns the vector a divided by value.

js
cm.vecDiv(cm.vec(3, 4), 0.5); // {x: 6, y: 8}

cm.vecMag(a[, value]) ​

With one argument, returns the magnitude of the vector a. With two, returns a vector in the direction of a with magnitude value.

js
cm.vecMag(cm.vec(3, 4)); // 5
cm.vecMag(cm.vec(3, 4), 10); // {x: 6, y: 8}

cm.vecNorm(a) ​

Returns the unit vector in the direction of a. The zero vector stays the zero vector.

js
cm.vecNorm(cm.vec(3, 4)); // {x: 0.6, y: 0.8}

cm.vecClamp(a, min[, max]) ​

Returns a copy of the vector a with its magnitude constrained to [min, max]. With two arguments, the second is the maximum and the minimum is 0.

js
cm.vecClamp(cm.vec(3, 4), 10, 15); // {x: 6, y: 8}
cm.vecClamp(cm.vec(6, 8), 5); // {x: 3, y: 4}

cm.vecDist(a, b) ​

Returns the distance between the vectors a and b.

js
cm.vecDist(cm.vec(4, 6), cm.vec(1, 2)); // 5

cm.vecDist2(a, b) ​

Returns the squared distance between the vectors a and b, which is cheaper to compute when only comparing distances.

js
cm.vecDist2(cm.vec(4, 6), cm.vec(1, 2)); // 25

cm.vecDot(a, b) ​

Returns the dot product of the vectors a and b.

js
cm.vecDot(cm.vec(3, 4), cm.vec(1, 2)); // 11

cm.vecCross(a, b) ​

Returns the cross product of the vectors a and b: the z component of their 3D cross product.

js
cm.vecCross(cm.vec(3, 4), cm.vec(1, 2)); // 2

cm.vecAngle(a) ​

Returns the angle of the vector a in radians.

js
cm.vecAngle(cm.vec(1, 1)); // Math.PI / 4

vec.set(x[, y]) ​

Sets the components to those of the vector x, or to the numbers x and y, and returns this vector. vec.setX(x) and vec.setY(y) set one component.

js
const a = cm.vec(1, 2);
a.set(cm.vec(3, 4)); // a, {x: 3, y: 4}
a.set(5, 6); // a, {x: 5, y: 6}
a.setX(7); // a, {x: 7, y: 6}

vec.clone() ​

Returns a copy of this vector.

js
const a = cm.vec(1, 2);
const b = a.clone();
a === b; // false

vec.add(b) ​

Adds the vector b to this vector, and returns this vector. Likewise, vec.sub(b) subtracts, vec.mult(value) multiplies and vec.div(value) divides.

js
const a = cm.vec(1, 2);
a.add(cm.vec(3, 4)); // a, {x: 4, y: 6}
a.mult(2); // a, {x: 8, y: 12}

vec.mag([value]) ​

Without an argument, returns the magnitude of this vector. With a value, sets the magnitude and returns this vector.

js
const a = cm.vec(3, 4);
a.mag(); // 5
a.mag(10); // a, {x: 6, y: 8}

vec.norm() ​

Normalizes this vector to magnitude 1, and returns it.

js
cm.vec(3, 4).norm(); // {x: 0.6, y: 0.8}

vec.clamp(min[, max]) ​

Constrains the magnitude of this vector to [min, max], and returns it. With one argument, it's the maximum and the minimum is 0.

js
cm.vec(6, 8).clamp(5); // {x: 3, y: 4}

vec.random() ​

Sets this vector to a unit vector in a random direction, and returns it.

js
cm.vec(3, 4).random().mag(); // 1

vec.dist(b) ​

Returns the distance to the vector b. vec.dist2(b) returns the squared distance.

js
cm.vec(4, 6).dist(cm.vec(1, 2)); // 5
cm.vec(4, 6).dist2(cm.vec(1, 2)); // 25

vec.dot(b) ​

Returns the dot product with the vector b. vec.cross(b) returns the cross product.

js
cm.vec(3, 4).dot(cm.vec(1, 2)); // 11
cm.vec(3, 4).cross(cm.vec(1, 2)); // 2

vec.angle() ​

Returns the angle of this vector in radians.

js
cm.vec(1, 1).angle(); // Math.PI / 4