*,html,body{margin:0px;padding:0px; overflow:hidden;}
let position, velocity, fps;

function setup() {
  canvas = createCanvas(windowWidth, windowHeight);

  // Initial position and velocity vectors
  position = createVector(0, 0);
  velocity = createVector(50, -50);
  fps = 60;
}

function draw() {
  clear();
  const hw = width / 2, hh = height / 2;

  // Calculate the end point of the velocity vector
  const endVelX = position.x + velocity.x;
  const endVelY = position.y + velocity.y;
  
  // Dracula theme background
  background('#282a36');
  push();
  translate(hw, hh);

  drawSubgrid(hw, hh, 50);
  
  // Draw axes (light blue lines)
  stroke('#6272a4');
  strokeWeight(1);
  line(-hw, 0, hw, 0);  // X-axis
  line(0, -hh, 0, hh);  // Y-axis
  drawLabel(0, -4, "Origem", LEFT);
  
  // Vector and marker styling
  fill('#8be9fd');
  stroke('#ff79c6');
  strokeWeight(2);

  // Draw the velocity vector
  drawArrow(position.x, position.y, endVelX, endVelY);
  
  // Draw the position marker (green circle)
  noStroke();
  drawCircleMarker(position, 4);

  // Display position and velocity vector components
  drawLabel(position.x, position.y, `Posição (${position.x.toPrecision(4)}, ${position.y.toPrecision(4)})`, LEFT);
  drawLabel(endVelX, endVelY, `Velocidade (${velocity.x.toPrecision(4)}, ${velocity.y.toPrecision(4)})`, LEFT);

  pop();

  // Update the position based on the velocity, scaled by frame rate
  position.add(velocity.copy().div(fps));

  // Reverse velocity if the point goes beyond the canvas boundaries
  if (position.x > hw || position.x < -hw) {
    velocity.x = -velocity.x;
  }
  if (position.y > hh || position.y < -hh) {
    velocity.y = -velocity.y;
  }
}

// Update velocity based on mouse click
function mousePressed() {
  const hw = width / 2, hh = height / 2;
  
  // Set velocity direction based on mouse click, relative to current position
  velocity.set(mouseX - position.x - hw, mouseY - position.y - hh).mult(0.5);
}

// Function to draw a circular marker at a position
function drawCircleMarker(p, size) {
  ellipse(p.x, p.y, size * 2, size * 2);
}

// Function to draw an arrow representing a vector
function drawArrow(x0, y0, x1, y1) {
  line(x0, y0, x1, y1);
  let v = createVector(x1 - x0, y1 - y0).normalize().mult(4);
  line(x1, y1, x1 - v.y - v.x, y1 + v.x - v.y);
  line(x1, y1, x1 + v.y - v.x, y1 - v.x - v.y);
}

function drawSubgrid(hw, hh, gridSize) {
    stroke('#44475a'); // Subgrid lines color (Dark gray)
    strokeWeight(0.5);

    // Align the grid with the axes by calculating the first grid line relative to the center (0, 0)

    // Vertical subgrid lines (aligned with Y axis)
    for (let x = -hw + (hw % gridSize); x <= hw; x += gridSize) {
        line(x, -hh, x, hh);
    }

    // Horizontal subgrid lines (aligned with X axis)
    for (let y = -hh + (hh % gridSize); y <= hh; y += gridSize) {
        line(-hw, y, hw, y);
    }
}

// Function to display text labels
function drawLabel(x, y, label, align = CENTER) {
  push();
  strokeWeight(0);
  fill('#f8f8f2'); // Dracula theme: Light gray for labels
  textFont("monospace");
  textSize(14);
  textAlign(align);

  if (align == LEFT) x += 6;
  if (align == RIGHT) x -= 6;

  text(label, x, y);
  pop();
}

function windowResized() {
  resizeCanvas(windowWidth, windowHeight);
}
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdn.jsdelivr.net/npm/p5@latest/lib/p5.min.js