parent
52c5462b08
commit
a05627e456
Larry the snail/larry_photos
docs
Binary file not shown.
After ![]() (image error) Size: 5.7 KiB |
Binary file not shown.
After ![]() (image error) Size: 5.7 KiB |
Binary file not shown.
After ![]() (image error) Size: 5.7 KiB |
Binary file not shown.
After ![]() (image error) Size: 5.7 KiB |
|
@ -12,6 +12,7 @@
|
||||||
<div id="toolbar">
|
<div id="toolbar">
|
||||||
<br>
|
<br>
|
||||||
<select id="shape-selector">
|
<select id="shape-selector">
|
||||||
|
<option value="RaysInShape">Rays</option>
|
||||||
<option value="NewWave">NewWave</option>
|
<option value="NewWave">NewWave</option>
|
||||||
<option value="EyePrototype">EyePrototype</option>
|
<option value="EyePrototype">EyePrototype</option>
|
||||||
<option value="Nodal_expanding">Nodal_expanding</option>
|
<option value="Nodal_expanding">Nodal_expanding</option>
|
||||||
|
|
|
@ -99,44 +99,97 @@ async function fetchConfig(className) {
|
||||||
{ type: "range", min: 1, max: 10, defaultValue: 4, property: "lineWidth" },
|
{ type: "range", min: 1, max: 10, defaultValue: 4, property: "lineWidth" },
|
||||||
{ type: "range", min: 100, max: 1000, defaultValue: 100, property: "limiter" },
|
{ type: "range", min: 100, max: 1000, defaultValue: 100, property: "limiter" },
|
||||||
],
|
],
|
||||||
|
RaysInShape: [
|
||||||
|
{ type: "range", min: 50, max: 1000, defaultValue: 300, property: "rays" },
|
||||||
|
{ type: "range", min: 1, max: 1000, defaultValue: 2, property: "speed" },
|
||||||
|
{ type: "range", min: 1, max: 200, defaultValue: 100, property: "speedVert" },
|
||||||
|
{ type: "range", min: 1, max: 200, defaultValue: 100, property: "speedHorr" },
|
||||||
|
{ type: "range", min: 10, max: 2000, defaultValue: 800, property: "boxSize" },
|
||||||
|
{ type: "range", min: 10, max: 200, defaultValue: 50, property: "trailLength" },
|
||||||
|
|
||||||
|
],
|
||||||
};
|
};
|
||||||
return config[className];
|
return config[className];
|
||||||
}
|
}
|
||||||
|
|
||||||
function addControl(item, instance) {
|
function addControl(item, instance) {
|
||||||
// console.log(item);
|
|
||||||
let parentDiv = document.getElementById("custom");
|
let parentDiv = document.getElementById("custom");
|
||||||
|
|
||||||
let title = document.createElement("p");
|
let title = document.createElement("p");
|
||||||
title.innerText = item.property + ": " + item.defaultValue;
|
title.innerText = item.property + ": " + item.defaultValue;
|
||||||
title.id = "elText" + item.property;
|
title.id = "elText" + item.property;
|
||||||
|
|
||||||
let control = document.createElement("input");
|
let control;
|
||||||
control.type = item.type;
|
|
||||||
|
|
||||||
if (item.type === "range") {
|
if (item.type === "range") {
|
||||||
|
control = document.createElement("input");
|
||||||
|
control.type = "range";
|
||||||
control.min = item.min;
|
control.min = item.min;
|
||||||
control.max = item.max;
|
control.max = item.max;
|
||||||
|
control.value = item.defaultValue;
|
||||||
|
control.addEventListener("input", (event) => {
|
||||||
|
const newValue = parseInt(event.target.value, 10);
|
||||||
|
instance[item.property] = newValue;
|
||||||
|
title.innerText = item.property + ": " + newValue;
|
||||||
|
|
||||||
|
if (item.callback) {
|
||||||
|
item.callback(instance, newValue);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (item.type === "button") {
|
||||||
|
control = document.createElement("button");
|
||||||
|
control.innerText = item.label;
|
||||||
|
control.addEventListener("click", () => {
|
||||||
|
instance[item.method]();
|
||||||
|
});
|
||||||
|
} else if (item.type === "dropdown") {
|
||||||
|
control = document.createElement("select");
|
||||||
|
item.options.forEach(option => {
|
||||||
|
let optionElement = document.createElement("option");
|
||||||
|
optionElement.value = option.value;
|
||||||
|
optionElement.innerText = option.label;
|
||||||
|
control.appendChild(optionElement);
|
||||||
|
});
|
||||||
|
control.value = item.defaultValue;
|
||||||
|
control.addEventListener("change", (event) => {
|
||||||
|
const newValue = event.target.value;
|
||||||
|
instance[item.property] = newValue;
|
||||||
|
title.innerText = item.property + ": " + newValue;
|
||||||
|
|
||||||
|
if (item.callback) {
|
||||||
|
item.callback(instance, newValue);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (item.type === "header") {
|
||||||
|
control = document.createElement("p");
|
||||||
|
control.innerText = item.text;
|
||||||
|
control.className = "header";
|
||||||
|
control.id = "elHeader" + item.text.replace(/\s+/g, '');
|
||||||
|
}
|
||||||
|
else if (item.type === "color") {
|
||||||
|
control = document.createElement("input");
|
||||||
|
control.type = item.type;
|
||||||
|
control.value = item.defaultValue;
|
||||||
|
control.id = "el" + item.property;
|
||||||
|
control.addEventListener("input", (event) => {
|
||||||
|
const newValue = event.target.value;
|
||||||
|
instance[item.property] = newValue;
|
||||||
|
title.innerText = item.property + ": " + newValue;
|
||||||
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
control.value = item.defaultValue;
|
if (item.type != "header") {
|
||||||
control.className = "control";
|
control.className = "control";
|
||||||
control.id = "el" + item.property;
|
control.id = "el" + item.property;
|
||||||
|
}
|
||||||
|
|
||||||
const listener = (event) => {
|
if (item.type != "button" && item.type != "header") {
|
||||||
const newValue = event.target.value;
|
parentDiv.appendChild(title);
|
||||||
instance[item.property] =
|
}
|
||||||
item.type === "range" ? parseInt(newValue, 10) : newValue;
|
|
||||||
|
|
||||||
title.innerText = item.property + ": " + newValue;
|
|
||||||
};
|
|
||||||
|
|
||||||
control.addEventListener("input", listener);
|
|
||||||
|
|
||||||
parentDiv.appendChild(title);
|
|
||||||
parentDiv.appendChild(control);
|
parentDiv.appendChild(control);
|
||||||
|
|
||||||
return { element: control, listener };
|
return { element: control };
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawEyelid(width, x1, y1, colour) {
|
function drawEyelid(width, x1, y1, colour) {
|
||||||
|
|
|
@ -12,13 +12,16 @@ let targetFps = 60;
|
||||||
let frameDuration = 1000 / targetFps;
|
let frameDuration = 1000 / targetFps;
|
||||||
|
|
||||||
let rotation = 0; //was = j = angle
|
let rotation = 0; //was = j = angle
|
||||||
let paused = true;
|
let paused = false;
|
||||||
|
let elapsedTime = 0;
|
||||||
|
let lastTimestamp = 0;
|
||||||
render_clear();
|
render_clear();
|
||||||
|
|
||||||
let drawObj = null;
|
let drawObj = null;
|
||||||
function createInstance(className, args) {
|
function createInstance(className, args) {
|
||||||
const classMap = {
|
const classMap = {
|
||||||
NewWave: NewWave,
|
NewWave: NewWave,
|
||||||
|
RaysInShape: RaysInShape,
|
||||||
PolyTwistColourWidth: PolyTwistColourWidth,
|
PolyTwistColourWidth: PolyTwistColourWidth,
|
||||||
FloralPhyllo: FloralPhyllo,
|
FloralPhyllo: FloralPhyllo,
|
||||||
Spiral1: Spiral1,
|
Spiral1: Spiral1,
|
||||||
|
@ -65,15 +68,31 @@ updateDrawObj();
|
||||||
|
|
||||||
function render() {
|
function render() {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame((timestamp) => {
|
||||||
render_clear();
|
if (!lastTimestamp) lastTimestamp = timestamp;
|
||||||
if (drawObj) {
|
const deltaTime = timestamp - lastTimestamp;
|
||||||
drawObj.draw(rotation);
|
const adjustedElapsed = elapsedTime / 100; // Convert to seconds
|
||||||
}
|
lastTimestamp = timestamp;
|
||||||
|
let adjustedDeltaTime;
|
||||||
if (!paused) {
|
if (!paused) {
|
||||||
rotation += deg_per_sec / targetFps;
|
rotation += deg_per_sec / targetFps;
|
||||||
|
elapsedTime += deltaTime;
|
||||||
|
adjustedDeltaTime = deltaTime / 100; // Convert to seconds
|
||||||
|
// console.log(adjustedDeltaTime)
|
||||||
}
|
}
|
||||||
|
// console.log(deltaTime)
|
||||||
|
// console.log(elapsedTime)
|
||||||
|
render_clear();
|
||||||
|
if (drawObj) {
|
||||||
|
// drawObj.draw(rotation);
|
||||||
|
|
||||||
|
drawObj.draw(adjustedElapsed, adjustedDeltaTime);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.font = "48px serif";
|
||||||
|
ctx.fillStyle = "white"
|
||||||
|
ctx.fillText(Math.floor(elapsedTime) + "ms", centerX - 100, centerY + 400);
|
||||||
// drawCenter(300)
|
// drawCenter(300)
|
||||||
});
|
});
|
||||||
render();
|
render();
|
||||||
|
|
|
@ -10,7 +10,7 @@ class BaseShape {
|
||||||
this.controls.push({ element, listener });
|
this.controls.push({ element, listener });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { element, listener } = addControl({ type: "range", min: 1, max: 500, defaultValue: 100, property: "speedMultiplier", }, this);
|
const { element, listener } = addControl({ type: "range", min: 1, max: 500, defaultValue: 100, property: "speedMultiplier" }, this);
|
||||||
this.controls.push({ element, listener });
|
this.controls.push({ element, listener });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,11 +18,14 @@ class BaseShape {
|
||||||
this.controls.forEach(({ element, listener }) => {
|
this.controls.forEach(({ element, listener }) => {
|
||||||
if (element && listener) {
|
if (element && listener) {
|
||||||
element.removeEventListener("input", listener);
|
element.removeEventListener("input", listener);
|
||||||
|
element.removeEventListener("click", listener);
|
||||||
}
|
}
|
||||||
if (element && element.parentElement) {
|
if (element && element.parentElement) {
|
||||||
element.parentElement.removeChild(element);
|
element.parentElement.removeChild(element);
|
||||||
const titleElement = document.getElementById("elText" + element.id.slice(2));
|
const titleElement = document.getElementById("elText" + element.id.slice(2));
|
||||||
titleElement.parentElement.removeChild(titleElement);
|
if (titleElement) {
|
||||||
|
titleElement.parentElement.removeChild(titleElement);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.controls = [];
|
this.controls = [];
|
||||||
|
@ -672,3 +675,302 @@ class NewWave extends BaseShape {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class RaysInShape extends BaseShape {
|
||||||
|
constructor(rays, speed, speedVert, speedHorr, boxSize, trailLength = 50) {
|
||||||
|
super();
|
||||||
|
this.rays = rays;
|
||||||
|
this.speed = speed;
|
||||||
|
this.speedVert = speedVert;
|
||||||
|
this.speedHorr = speedHorr;
|
||||||
|
this.boxSize = boxSize;
|
||||||
|
this.trailLength = trailLength;
|
||||||
|
this.rayObjects = [];
|
||||||
|
this.centerRays = []; // New array for rays heading to center
|
||||||
|
}
|
||||||
|
|
||||||
|
initialise(config) {
|
||||||
|
for (let item of config) {
|
||||||
|
const { element, listener } = addControl(item, this);
|
||||||
|
this.controls.push({ element, listener });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add controls for speed multiplier and trail length
|
||||||
|
const { element: speedElement, listener: speedListener } = addControl({
|
||||||
|
type: "range", min: 1, max: 500, defaultValue: 100, property: "speedMultiplier"
|
||||||
|
}, this);
|
||||||
|
this.controls.push({ element: speedElement, listener: speedListener });
|
||||||
|
|
||||||
|
const { element: trailElement, listener: trailListener } = addControl({
|
||||||
|
type: "range", min: 5, max: 200, defaultValue: this.trailLength, property: "trailLength"
|
||||||
|
}, this);
|
||||||
|
this.controls.push({ element: trailElement, listener: trailListener });
|
||||||
|
|
||||||
|
// Prepare rayObjects for the first draw
|
||||||
|
this.prepareRayObjects();
|
||||||
|
}
|
||||||
|
|
||||||
|
prepareRayObjects() {
|
||||||
|
this.rayObjects = [];
|
||||||
|
for (let i = 0; i < this.rays; i++) {
|
||||||
|
const angle = (360 / this.rays) * i;
|
||||||
|
this.rayObjects.push({
|
||||||
|
angle: angle,
|
||||||
|
lastX: centerX,
|
||||||
|
lastY: centerY,
|
||||||
|
positions: [{ x: centerX, y: centerY, angle: angle }]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.centerRays = []; // Initialize centerRays array
|
||||||
|
}
|
||||||
|
|
||||||
|
createCenterRay(x, y) {
|
||||||
|
// Calculate angle towards center
|
||||||
|
const dx = centerX - x;
|
||||||
|
const dy = centerY - y;
|
||||||
|
const angleToCenter = Math.atan2(dy, dx) * 180 / Math.PI;
|
||||||
|
|
||||||
|
// Create new center-bound ray
|
||||||
|
this.centerRays.push({
|
||||||
|
positions: [{ x: x, y: y }],
|
||||||
|
angle: angleToCenter,
|
||||||
|
reachedCenter: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
updateCenterRays(deltaTime) {
|
||||||
|
const centerThreshold = 5; // Distance threshold to consider "reached center"
|
||||||
|
const maxDistance = 2000;
|
||||||
|
|
||||||
|
// Process each center-bound ray
|
||||||
|
for (let i = 0; i < this.centerRays.length; i++) {
|
||||||
|
const ray = this.centerRays[i];
|
||||||
|
|
||||||
|
// Skip rays that have reached the center
|
||||||
|
if (ray.reachedCenter) {
|
||||||
|
// Remove the oldest position from the trail
|
||||||
|
if (ray.positions.length > 0) {
|
||||||
|
ray.positions.shift();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove ray if trail is empty
|
||||||
|
if (ray.positions.length <= 1) {
|
||||||
|
this.centerRays.splice(i, 1);
|
||||||
|
i--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Get current position
|
||||||
|
const currentPos = ray.positions[ray.positions.length - 1];
|
||||||
|
|
||||||
|
// Calculate new position
|
||||||
|
const dx = (this.speedHorr / 100) * this.speed * Math.cos(rad(ray.angle));
|
||||||
|
const dy = (this.speedVert / 100) * this.speed * Math.sin(rad(ray.angle));
|
||||||
|
const newX = currentPos.x + dx;
|
||||||
|
const newY = currentPos.y + dy;
|
||||||
|
|
||||||
|
// Check if ray has gone too far from origin
|
||||||
|
const distFromOrigin = Math.sqrt(
|
||||||
|
Math.pow(newX - centerX, 2) + Math.pow(newY - centerY, 2)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Remove rays that have gone too far
|
||||||
|
if (distFromOrigin > maxDistance) {
|
||||||
|
this.centerRays.splice(i, 1);
|
||||||
|
i--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add new position to ray
|
||||||
|
ray.positions.push({ x: newX, y: newY });
|
||||||
|
|
||||||
|
// Check if ray has reached center
|
||||||
|
const distToCenter = Math.sqrt(
|
||||||
|
Math.pow(newX - centerX, 2) + Math.pow(newY - centerY, 2)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (distToCenter <= centerThreshold) {
|
||||||
|
ray.reachedCenter = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove positions beyond trail length
|
||||||
|
while (ray.positions.length > this.trailLength) {
|
||||||
|
ray.positions.shift();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw all segments of the trail
|
||||||
|
ctx.lineWidth = 3;
|
||||||
|
for (let j = 1; j < ray.positions.length; j++) {
|
||||||
|
const prev = ray.positions[j - 1];
|
||||||
|
const curr = ray.positions[j];
|
||||||
|
|
||||||
|
// Fade color based on position in trail (newer = brighter)
|
||||||
|
const alpha = (j / ray.positions.length) * 0.8 + 0.2;
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(prev.x, prev.y);
|
||||||
|
ctx.lineTo(curr.x, curr.y);
|
||||||
|
|
||||||
|
// Center-bound rays are pink
|
||||||
|
ctx.strokeStyle = `rgba(255, 51, 170, ${alpha})`;
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
draw(elapsed, deltaTime) {
|
||||||
|
deltaTime *= this.speedMultiplier / 100;
|
||||||
|
|
||||||
|
// Define the box boundaries
|
||||||
|
const boxLeft = centerX - this.boxSize / 2;
|
||||||
|
const boxRight = centerX + this.boxSize / 2;
|
||||||
|
const boxTop = centerY - this.boxSize / 2;
|
||||||
|
const boxBottom = centerY + this.boxSize / 2;
|
||||||
|
|
||||||
|
// Draw the box boundary for visualization
|
||||||
|
ctx.strokeStyle = "white";
|
||||||
|
ctx.lineWidth = 1;
|
||||||
|
ctx.strokeRect(boxLeft, boxTop, this.boxSize, this.boxSize);
|
||||||
|
|
||||||
|
// Process ray movements and collisions
|
||||||
|
for (let j = 0; j < this.rayObjects.length; j++) {
|
||||||
|
const ray = this.rayObjects[j];
|
||||||
|
|
||||||
|
// Get current position
|
||||||
|
const currentPos = ray.positions[ray.positions.length - 1];
|
||||||
|
|
||||||
|
// Calculate potential new position
|
||||||
|
let dx = (this.speedHorr / 100) * this.speed * Math.cos(rad(ray.angle));
|
||||||
|
let dy = (this.speedVert / 100) * this.speed * Math.sin(rad(ray.angle));
|
||||||
|
let newX = currentPos.x + dx;
|
||||||
|
let newY = currentPos.y + dy;
|
||||||
|
let collisionType = null;
|
||||||
|
const oldAngle = ray.angle;
|
||||||
|
|
||||||
|
// Check for horizontal collision
|
||||||
|
if (newX < boxLeft || newX > boxRight) {
|
||||||
|
// Calculate exact collision point with horizontal wall
|
||||||
|
const collisionX = newX < boxLeft ? boxLeft : boxRight;
|
||||||
|
const collisionRatio = (collisionX - currentPos.x) / dx;
|
||||||
|
const collisionY = currentPos.y + dy * collisionRatio;
|
||||||
|
|
||||||
|
// Add collision point to positions array
|
||||||
|
ray.positions.push({
|
||||||
|
x: collisionX,
|
||||||
|
y: collisionY,
|
||||||
|
angle: oldAngle,
|
||||||
|
collision: 'horizontal'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create a center-bound ray at the collision point
|
||||||
|
this.createCenterRay(collisionX, collisionY);
|
||||||
|
|
||||||
|
// Reflect horizontally
|
||||||
|
ray.angle = 180 - ray.angle;
|
||||||
|
// Normalize angle
|
||||||
|
ray.angle = ((ray.angle % 360) + 360) % 360;
|
||||||
|
|
||||||
|
// Calculate remaining movement after collision
|
||||||
|
const remainingRatio = 1 - collisionRatio;
|
||||||
|
dx = remainingRatio * (this.speedHorr / 100) * this.speed * Math.cos(rad(ray.angle));
|
||||||
|
dy = remainingRatio * (this.speedVert / 100) * this.speed * Math.sin(rad(ray.angle));
|
||||||
|
newX = collisionX + dx;
|
||||||
|
newY = collisionY + dy;
|
||||||
|
collisionType = 'horizontal';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for vertical collision
|
||||||
|
if (newY < boxTop || newY > boxBottom) {
|
||||||
|
if (collisionType === null) {
|
||||||
|
// Calculate exact collision point with vertical wall
|
||||||
|
const collisionY = newY < boxTop ? boxTop : boxBottom;
|
||||||
|
const collisionRatio = (collisionY - currentPos.y) / dy;
|
||||||
|
const collisionX = currentPos.x + dx * collisionRatio;
|
||||||
|
|
||||||
|
// Add collision point to positions array
|
||||||
|
ray.positions.push({
|
||||||
|
x: collisionX,
|
||||||
|
y: collisionY,
|
||||||
|
angle: oldAngle,
|
||||||
|
collision: 'vertical'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create a center-bound ray at the collision point
|
||||||
|
this.createCenterRay(collisionX, collisionY);
|
||||||
|
|
||||||
|
// Reflect vertically
|
||||||
|
ray.angle = 360 - ray.angle;
|
||||||
|
// Normalize angle
|
||||||
|
ray.angle = ((ray.angle % 360) + 360) % 360;
|
||||||
|
|
||||||
|
// Calculate remaining movement after collision
|
||||||
|
const remainingRatio = 1 - collisionRatio;
|
||||||
|
dx = remainingRatio * (this.speedHorr / 100) * this.speed * Math.cos(rad(ray.angle));
|
||||||
|
dy = remainingRatio * (this.speedVert / 100) * this.speed * Math.sin(rad(ray.angle));
|
||||||
|
newX = collisionX + dx;
|
||||||
|
newY = collisionY + dy;
|
||||||
|
} else {
|
||||||
|
// Second collision in the same frame (corner case)
|
||||||
|
// Simply ensure we stay inside the box
|
||||||
|
newX = Math.max(boxLeft, Math.min(newX, boxRight));
|
||||||
|
newY = Math.max(boxTop, Math.min(newY, boxBottom));
|
||||||
|
ray.positions.push({
|
||||||
|
x: newX,
|
||||||
|
y: newY,
|
||||||
|
angle: ray.angle,
|
||||||
|
collision: 'corner'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create a center-bound ray at the collision point (corner)
|
||||||
|
this.createCenterRay(newX, newY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure rays stay inside the box
|
||||||
|
newX = Math.max(boxLeft, Math.min(newX, boxRight));
|
||||||
|
newY = Math.max(boxTop, Math.min(newY, boxBottom));
|
||||||
|
|
||||||
|
// Add new position to history if there was no collision yet
|
||||||
|
if (collisionType === null) {
|
||||||
|
ray.positions.push({
|
||||||
|
x: newX,
|
||||||
|
y: newY,
|
||||||
|
angle: ray.angle
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Limit positions array to trail length
|
||||||
|
while (ray.positions.length > this.trailLength) {
|
||||||
|
ray.positions.shift();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw the trail
|
||||||
|
ctx.lineWidth = 3;
|
||||||
|
|
||||||
|
// Draw all segments of the trail
|
||||||
|
for (let i = 1; i < ray.positions.length; i++) {
|
||||||
|
const prev = ray.positions[i - 1];
|
||||||
|
const curr = ray.positions[i];
|
||||||
|
|
||||||
|
// Fade color based on position in trail (newer = brighter)
|
||||||
|
const alpha = (i / ray.positions.length) * 0.8 + 0.2;
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(prev.x, prev.y);
|
||||||
|
ctx.lineTo(curr.x, curr.y);
|
||||||
|
|
||||||
|
// Highlight collision points with different color
|
||||||
|
if (curr.collision) {
|
||||||
|
ctx.strokeStyle = `rgba(255, 255, 0, ${alpha})`;
|
||||||
|
} else {
|
||||||
|
ctx.strokeStyle = `rgba(50, 50, 50, ${alpha})`; // Changed from pink to gray
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update and draw center-bound rays
|
||||||
|
this.updateCenterRays(deltaTime);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue