Skip to content

Layout - Screen and World Space

Inevitably there will come a time where you need to convert between screen and world space.

The browser api for mouse events returns screen coordinates, and you'll likely want to know if the mouse is over a node at some point.

This example uses toodle.convertSpace to determine whether the mouse is over a node.

ts
import { Toodle } from "@blooper.gg/toodle";

const canvas = document.querySelector("canvas")!;
const toodle = await Toodle.attach(canvas, {
  filter: "nearest",
  limits: { textureArrayLayers: 1 },
});

await toodle.assets.loadFont(
  "Comic",
  new URL("https://toodle.gg/fonts/ComicNeue-Regular-msdf.json"),
);

let screenCoords = { x: 0, y: 0 };
canvas.addEventListener("mousemove", (ev) => {
  // get the screen coordinates of the mouse
  // accounting for the canvas's position in the document
  // and any viewport scrolling or css transforms
  const rect = canvas.getBoundingClientRect();
  screenCoords = { x: ev.clientX - rect.left, y: ev.clientY - rect.top };
});

const rect = toodle.shapes.Rect({
  idealSize: {
    width: 100,
    height: 100,
  },
});

function frame() {
  toodle.startFrame();

  // convert mouse screen coordinates to world space
  const worldCoords = toodle.convertSpace(screenCoords, {
    from: "screen",
    to: "world",
  });

  // Axis-Aligned Bounding Box check for containment
  const isHovering =
    worldCoords.x >= rect.bounds.left &&
    worldCoords.x <= rect.bounds.right &&
    worldCoords.y >= rect.bounds.bottom &&
    worldCoords.y <= rect.bounds.top;

  rect.color = isHovering
    ? { r: 1, g: 0, b: 0, a: 1 }
    : { r: 0, g: 0, b: 0, a: 1 };

  const hudTopLeft = toodle.convertSpace(
    { x: 0, y: 0 },
    {
      from: "screen",
      to: "world",
    },
  );

  toodle.draw(
    toodle
      .Text(
        "Comic",
        `screen x=${Math.round(screenCoords.x)} y=${Math.round(screenCoords.y)}\nworld x=${Math.round(worldCoords.x)} y=${Math.round(worldCoords.y)}`,
        {
          position: { x: 100, y: -100 },
          color: { r: 0, g: 0, b: 0, a: 1 },
          idealSize: { width: 100, height: 35 },
        },
      )
      .setBounds({
        left: hudTopLeft.x,
        top: hudTopLeft.y,
      }),
  );

  toodle.draw(rect);
  toodle.endFrame();
  requestAnimationFrame(frame);
}

frame();