html,body{margin: 0;padding: 0;}
body{margin:0px; padding:0px;overflow:hidden;}
let positions, velocities, forces, masses, fps, maxSpeed;
let graphData = [];
const graphWidth = 200;
const graphHeight = 100;
let target;
let targetSet = false;
let dragging = false;
// Dracula theme colors
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);
positions = [createVector(-100, -100)];
velocities = [createVector(0, 0)];
forces = [createVector(0, 0)];
masses = [1];
fps = 60;
maxSpeed = 256;
target = createVector(0, 0);
targetSet = true;
}
function draw() {
clear();
background(colors.background);
let i = width / 2;
let o = height / 2;
translate(i, o);
stroke(colors.comment);
line(-i, 0, i, 0);
line(0, -o, 0, o);
drawSubgrid(i, o, 50);
for (let s = 0; s < positions.length; s++) {
let e = positions[s].x + velocities[s].x;
let t = positions[s].y + velocities[s].y;
let n = positions[s].x + forces[s].x;
let c = positions[s].y + forces[s].y;
strokeWeight(1);
stroke(colors.pink);
drawArrow(positions[s].x, positions[s].y, e, t);
drawCircleMarker(positions[s], 4 * sqrt(masses[s]), s);
strokeWeight(2);
stroke(colors.green);
drawArrow(positions[s].x, positions[s].y, n, c);
strokeWeight(0);
fill(colors.foreground);
drawLabel(
positions[s].x,
positions[s].y,
`Posição (${positions[s].x.toPrecision(4)}, ${positions[s].y.toPrecision(
4
)})`,
LEFT
);
drawLabel(
e,
t,
`Velocidade (${velocities[s].x.toPrecision(4)}, ${velocities[
s
].y.toPrecision(4)})`,
LEFT
);
drawLabel(
n,
c,
`Aceleração (${forces[s].x.toPrecision(4)}, ${forces[s].y.toPrecision(
4
)})`,
LEFT
);
if (targetSet) {
forces[s] = p5.Vector.sub(target, positions[s]).div(2);
}
let r = forces[s].copy().div(masses[s]).div(fps);
velocities[s].add(r);
if (velocities[s].mag() > maxSpeed) {
velocities[s].setMag(maxSpeed);
}
positions[s].add(velocities[s].copy().div(fps));
if (positions[s].x > i || positions[s].x < -i) {
velocities[s].x = -velocities[s].x;
}
if (positions[s].y > o || positions[s].y < -o) {
velocities[s].y = -velocities[s].y;
}
// Store data for the graph
graphData.push({
velocity: velocities[s].mag(),
acceleration: forces[s].mag() / masses[s]
});
// Limit the graph data to the last 100 frames
if (graphData.length > 100) {
graphData.shift();
}
}
// Draw target
if (targetSet) {
fill(colors.red);
noStroke();
ellipse(target.x, target.y, 10, 10);
}
updateTargetAppearance();
// Draw the graph
strokeWeight(1);
drawGraph();
}
function drawSubgrid(hw, hh, gridSize) {
stroke("#44475a");
strokeWeight(0.5);
for (let x = -hw + (hw % gridSize); x <= hw; x += gridSize) {
line(x, -hh, x, hh);
}
for (let y = -hh + (hh % gridSize); y <= hh; y += gridSize) {
line(-hw, y, hw, y);
}
}
function updateTargetAppearance() {
let i = width / 2;
let o = height / 2;
// Check hover state
let d = dist(mouseX - i, mouseY - o, target.x, target.y);
if (d < 20) {
hover = true;
if (dragging) {
cursor("grabbing");
} else {
cursor("grab");
}
} else {
hover = false;
cursor("default");
}
// Draw target with hover effect
if (targetSet) {
if (hover || dragging) {
fill(colors.yellow);
ellipse(target.x, target.y, 20, 20); // Larger "Alvo" when hovered or dragged
} else {
fill(colors.red);
ellipse(target.x, target.y, 10, 10); // Normal size "Alvo"
}
}
}
function mousePressed() {
let i = width / 2;
let o = height / 2;
let d = dist(mouseX - i, mouseY - o, target.x, target.y);
// Start dragging if near the target
if (d < 10) {
dragging = true;
cursor("grabbing");
}
}
function mouseDragged() {
if (dragging) {
let i = width / 2;
let o = height / 2;
target.set(mouseX - i, mouseY - o);
cursor("grabbing");
}
}
function mouseReleased() {
if (dragging) {
dragging = false; // Stop dragging when the mouse is released
cursor("grab");
}
}
function mouseClicked() {
if (!dragging) {
// Fixate target only if not dragging
let i = width / 2;
let o = height / 2;
target.set(mouseX - i, mouseY - o);
targetSet = true;
}
}
function drawCircleMarker(i, o, s) {
fill(s === 0 ? colors.cyan : colors.purple);
noStroke();
ellipse(i.x, i.y, 2 * o, 2 * o);
}
function drawArrow(i, o, s, e) {
line(i, o, s, e);
let t = createVector(s - i, e - o)
.normalize()
.mult(4);
line(s, e, s - t.y - t.x, e + t.x - t.y);
line(s, e, s + t.y - t.x, e - t.x - t.y);
}
function drawLabel(i, o, s, e = CENTER) {
push();
textFont("monospace");
textSize(14);
textAlign(e);
if (e == LEFT) {
i += 6;
}
if (e == RIGHT) {
i -= 6;
}
text(s, i, o);
pop();
}
function drawGraph() {
push();
translate(width / 2 - graphWidth - 20, -height / 2 + 20);
// Draw graph background
fill(0, 127);
rect(0, 0, graphWidth, graphHeight);
// Find max values for scaling
let maxVelocity = 0;
let maxAcceleration = 0;
for (let data of graphData) {
maxVelocity = max(max(maxVelocity, data.velocity), 25);
maxAcceleration = max(max(maxAcceleration, data.acceleration), 25);
}
// Define thresholds for increasing resolution
const velocityThreshold = 100;
const accelerationThreshold = 50;
// Calculate step sizes based on thresholds
const velocityStep = maxVelocity > velocityThreshold ? 50 : 25;
const accelerationStep = maxAcceleration > accelerationThreshold ? 25 : 10;
// Draw y-axis labels and grid lines
stroke(colors.comment);
fill(colors.foreground);
textAlign(RIGHT, CENTER);
textSize(10);
for (let i = 0; i <= maxVelocity; i += velocityStep) {
const y = map(i, 0, maxVelocity, graphHeight, 0);
line(0, y, graphWidth, y);
text(i.toFixed(0), -5, y);
}
// Draw graph lines
strokeWeight(1);
for (let i = 0; i < graphData.length - 1; i++) {
// Velocity line
stroke(colors.pink);
let x1 = map(i, 0, 100, 0, graphWidth);
let y1 = map(graphData[i].velocity, 0, maxVelocity, graphHeight, 0);
let x2 = map(i + 1, 0, 100, 0, graphWidth);
let y2 = map(graphData[i + 1].velocity, 0, maxVelocity, graphHeight, 0);
line(x1, y1, x2, y2);
// Acceleration line
stroke(colors.green);
y1 = map(graphData[i].acceleration, 0, maxAcceleration, graphHeight, 0);
y2 = map(graphData[i + 1].acceleration, 0, maxAcceleration, graphHeight, 0);
line(x1, y1, x2, y2);
}
// Draw x-axis labels
fill(colors.foreground);
noStroke();
textAlign(CENTER, TOP);
text("0", 0, graphHeight + 5);
text("50", graphWidth / 2, graphHeight + 5);
text("100", graphWidth, graphHeight + 5);
// Draw legend
fill(colors.foreground);
noStroke();
textAlign(LEFT, TOP);
text("Velocidade", 10, 10);
text("Aceleração", 10, 30);
stroke(colors.pink);
line(80, 17, 100, 17);
stroke(colors.green);
line(80, 37, 100, 37);
pop();
}
function windowResized() {
resizeCanvas(windowWidth, windowHeight);
}
This Pen doesn't use any external CSS resources.