*,html,body{margin:0px;padding:0px;overflow:hidden;}
let msec = 0, prevInitMsec = 0, prevNow = 0;
let plotLabels = ["Posição    (y)", "Velocidade (y)", "Aceleração (y)"];
let plotStartMsec = 0, plotData = [[], [], []];
const gravity = 9.81;
const plotScale = 25;
const timeScale = 3;
let apple;

// Cores do tema Dracula
const colors = {
  background: "#282a36",
  currentLine: "#44475a",
  foreground: "#f8f8f2",
  comment: "#6272a4",
  cyan: "#8be9fd",
  green: "#50fa7b",
  orange: "#ffb86c",
  pink: "#ff79c6",
  purple: "#bd93f9",
  red: "#ff5555",
  yellow: "#f1fa8c"
};

function setup() {
  createCanvas(windowWidth, windowHeight);
  apple = new Body(1);
  prevNow = window.performance.now();
}

function draw() {
  clear();
  background(colors.background);

  ellapseTime();

  let minWH = min(width, height);

  // Renderiza a posição atual da maçã
  push();
  translate(min((width - minWH) / 2 + 32, width / 4), 32);
  fill(colors.cyan);
  ellipse(0, apple.position.y * plotScale, 24, 24);
  pop();

  // Plota o gráfico mostrando posição, velocidade e aceleração
  let graphW = minWH - 64;
  plotGraph(plotData, (width - graphW) / 2 + 32, 32, graphW, height - 64, 0, 8000, -(height - 64), 0, "Tempo", "Valores");
}

function ellapseTime() {
  let ellapsedMsec = (window.performance.now() - prevNow) * timeScale;
  prevNow = window.performance.now();

  updateSimulation(ellapsedMsec / 2000);
  msec += ellapsedMsec;

  if (msec > prevInitMsec + 5000) {
    apple = new Body(1);
    plotData = [[], [], []];
    prevInitMsec += 5000;
  }

  if (plotData[0].length == 0) {
    plotStartMsec = msec;
  }

  let protMsec = msec - plotStartMsec;
  plotData[0].push({x: protMsec * 2.5, y: -apple.position.y * plotScale});
  plotData[1].push({x: protMsec * 2.5, y: -apple.velocity.y * plotScale});
  plotData[2].push({x: protMsec * 2.5, y: -gravity * plotScale});
}

function updateSimulation(t) {
  apple.applyForce(getForce(apple), t);
  apple.update(t);
}

function getForce(o) {
  return createVector(0, gravity).mult(o.mass);
}

function plotGraph(data, ox, oy, w, h, minX, maxX, minY, maxY, xLabel, yLabel) {
  let left = ox - minX / (maxX - minX) * w;
  let top = oy - maxY / (maxY - minY) * h;
  let labelLeft = abs(left - ox) > abs(left + w - ox);
  let labelTop = abs(top - oy) > abs(top + h - oy);

  drawGrid(ox, oy, w, h, 10);

  push();
  noFill(); 
  stroke(colors.comment);
  line(left, oy, left + w, oy);
  line(ox, top, ox, top + h);

  const lineColors = [colors.yellow, colors.pink, colors.green];

  for (let j = 0; j < data.length; j++) {
    stroke(lineColors[j]);
    beginShape();
    for (let i = 0; i < data[j].length; i++) {
      let x = ox + data[j][i].x / (maxX - minX) * w;
      let y = oy - data[j][i].y / (maxY - minY) * h;
      vertex(x, y);
    }
    endShape();
  }

  fill(colors.foreground);
  drawLabel(labelLeft ? left : left + w, oy + (labelTop ? 16 : -8), xLabel, labelLeft ? LEFT : RIGHT);
  drawLabel(ox, labelTop ? top : top + h, yLabel, labelLeft ? RIGHT : LEFT);

  // Adiciona legenda
  for (let i = 0; i < plotLabels.length; i++) {
    fill(lineColors[i]);
    stroke(lineColors[i]);
    rect(ox + 10, oy + 20 + i * 20, 10, 10);
    fill(colors.foreground);
    drawLabel(ox + 25, oy + 30 + i * 20, plotLabels[i], LEFT);
  }

  pop();
}

class Body {
  constructor(m) {
    this.position = createVector(0, 0, 0);
    this.velocity = createVector(0, 0, 0);
    this.mass = m;
  }

  applyForce(f, t) {
    this.velocity.add(f.copy().mult(t / this.mass));
  }

  update(t) {
    this.position.add(this.velocity.copy().mult(t))
  }
}

function drawGrid(ox, oy, w, h, steps) {
  stroke(colors.currentLine);
  let stepW = w / steps;
  let stepH = h / steps;
  
  for (let i = 1; i < steps; i++) {
    line(ox + i * stepW, oy, ox + i * stepW, oy + h); // Vertical grid lines
    line(ox, oy + i * stepH, ox + w, oy + i * stepH); // Horizontal grid lines
  }
}

function drawLegend(x, y, colors) {
  for (let i = 0; i < plotLabels.length; i++) {
    fill(colors[i]);
    stroke(colors[i]);
    rect(x, y + 20 + i * 20, 10, 10);
    fill(colors.foreground);
    drawLabel(x + 20, y + 30 + i * 20, plotLabels[i], LEFT);
  }
}

function drawLabel(x, y, label, align = CENTER) {
  push();
  strokeWeight(0);
  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