Mafs

Ellipses & circles

Ellipses take a center vector, radius vector, and an angle. Note that if the coordinate plane is not square (if the aspect ratio is "squished"), ellipses undergo a sort of funny-looking shear, which can make visualizations confusing.

import { Mafs, Ellipse, Circle, CartesianCoordinates, useMovablePoint, Theme, } from "mafs"
import * as vec from "vec-la"

function MovableEllipse() {
  const rotationHintRadius = 3

  // This center point translates everything else.
  const center = useMovablePoint([0, 0], {
    color: Theme.orange,
  })
  const translation = vec
    .matrixBuilder()
    .translate(center.x, center.y)
    .get()

  // This outer point rotates the ellipse, and
  // is also translated by the center point.
  // Its position is used to build a rotation matrix.
  const rotate = useMovablePoint([rotationHintRadius, 0], {
    color: Theme.blue,
    transform: translation,
    // Constrain this point to only move in a circle
    constrain: (position) =>
      vec.scale(vec.norm(position), rotationHintRadius),
  })
  const angle = Math.PI / 2 - Math.atan2(...rotate.point)
  const rotation = vec.matrixBuilder().rotate(angle).get()

  // Lastly, these two points are rotated and translated
  // according to the outer two points.
  const width = useMovablePoint([2, 0], {
    transform: vec.composeTransform(translation, rotation),
    constrain: "horizontal",
  })
  const height = useMovablePoint([0, 1], {
    transform: vec.composeTransform(translation, rotation),
    constrain: "vertical",
  })

  return (
    <Mafs>
      <CartesianCoordinates />

      {/*
       * Display a little hint that the
       * point is meant to move radially
       */}
      <Circle
        center={center.point}
        radius={rotationHintRadius}
        strokeStyle="dashed"
        strokeOpacity={0.3}
        fillOpacity={0}
      />

      <Ellipse
        center={center.point}
        radius={[Math.abs(width.x), Math.abs(height.y)]}
        angle={angle}
      />

      {center.element}
      {width.element}
      {height.element}
      {rotate.element}
    </Mafs>
  )
}

Work in progress

Support for defining ellipses in terms of two foci is planned. In the meantime, you can accomplish this using a parametric function.

Make suggestions on GitHub

Circles

Circles have the same interface as Ellipses, but take a single value for their radius and don't take an angle.

import {
  Mafs,
  Circle,
  CartesianCoordinates,
  useMovablePoint,
} from "mafs"
import * as vec from "vec-la"

function MovableCircle() {
  const pointOnCircle = useMovablePoint([
    Math.sqrt(2) / 2,
    Math.sqrt(2) / 2,
  ])
  const r = vec.mag(pointOnCircle.point)

  return (
    <Mafs>
      <CartesianCoordinates />
      <Circle center={[0, 0]} radius={r} />
      {pointOnCircle.element}
    </Mafs>
  )
}