{
const frame = 5;
const focus_agent_id = 44;
const data = sim;
const app = new PIXI.Application({
width: width,
height: height,
backgroundColor: 0x000000,
resolution: window.devicePixelRatio || 1,
});
invalidation.then(() => app.destroy(true, true));
const left_margin = 200;
const top_margin = 20;
const container = new PIXI.Container();
app.stage.addChild(container);
const x_pos = d3.scaleLinear()
.domain([0,49])
.range([left_margin,width-left_margin]);
const y_pos = d3.scaleLinear()
.domain([0,49])
.range([top_margin,height-top_margin]);
const texture = PIXI.Texture.from(await FileAttachment("bunny@1.png").image());
const agents = new Map();
for (let i=0; i<data[0].length; i++) {
const agent = new PIXI.Sprite(texture);
agent.x = x_pos(data[0][i].x);
agent.y = y_pos(data[0][i].y);
agent.tint = get_tint(data[0][i].stage);
agent.scale.set(0.5);
if (data[0][i].id == focus_agent_id) agent.scale.set(1);
agent.anchor.set(0.5);
container.addChild(agent);
agents.set(data[0][i].id, agent);
// next_step_index.set(data[0][i].id, find_agent_index(data[0][i].id, 1));
}
function find_agent_index(id, step) {
return data[step].findIndex(d => d.id == id);
}
// Get the texture for rope.
const trailTexture = PIXI.Texture.from(await FileAttachment("trail.png").image());
// historySize determines how long the trail will be.
const historySize = 1000;
const historyX = new Array(historySize).fill(0);
const historyY = new Array(historySize).fill(0);
// ropeSize determines how smooth the trail will be.
const ropeSize = 500;
const points = [];
// Create rope points.
for (let i = 0; i < ropeSize; i++) {
points.push(new PIXI.Point(0, 0));
}
// Create the rope
const rope = new PIXI.SimpleRope(trailTexture, points);
rope.blendMode = PIXI.BLEND_MODES.ADD;
app.stage.addChild(rope);
let step = 0;
let t = 1;
app.ticker.add(() => {
for (let i=0; i<data[step].length; i++) {
if (agents.has(data[step][i].id)) {
const agent = agents.get(data[step][i].id);
if (t == 0) {
agent.x = x_pos(data[step][i].x);
agent.y = y_pos(data[step][i].y);
agent.tint = get_tint(data[step][i].stage);
agent.alpha = 1;
// next_step_index.set(data[step][i].id, find_agent_index(data[step][i].id, (step+1)%data.length));
}
else {
// let _i = next_step_index.get(data[step][i].id);
let _i = i;
if (Math.abs(data[(step+1)%data.length][_i].x - data[step][i].x) ==49) agent.alpha -= 1/frame;
else if (Math.abs(data[(step+1)%data.length][_i].y - data[step][i].y) ==49) agent.alpha -= 1/frame;
else {
agent.x = x_pos((data[(step+1)%data.length][_i].x - data[step][i].x)*t/frame + data[step][i].x);
agent.y = y_pos((data[(step+1)%data.length][_i].y - data[step][i].y)*t/frame + data[step][i].y);
}
}
if (data[step][i].id == focus_agent_id) {
historyX.pop();
historyX.unshift(agent.x);
historyY.pop();
historyY.unshift(agent.y);
// Update the points to correspond with history.
for (let i = 0; i < ropeSize; i++) {
const p = points[i];
// Smooth the curve with cubic interpolation to prevent sharp edges.
const ix = cubicInterpolation(historyX, i / ropeSize * historySize);
const iy = cubicInterpolation(historyY, i / ropeSize * historySize);
p.x = ix;
p.y = iy;
}
}
}
}
t = (t+1)%frame;
if (t == 0) step = (step+1)%data.length;
if (step == 0) {
const agent = agents.get(focus_agent_id);
for (let i = 0; i < ropeSize; i++) {
points[i].x = agent.x;
points[i].y = agent.y;
}
for (let i = 0; i < historySize; i++) {
historyX[i] = agent.x;
historyY[i] = agent.y;
}
}
});
return Object.assign(app.view, { style: `width: ${width}px` });
}