xxxxxxxxxx
470
let armSegmentLengths;
let keep_going;
let arm;
let objectA;
let targetB;
let tickAmountInput;
let tickButton;
let finishButton;
let finishingTask = false;
var armSounds = []
var armHasItem = false;
var t = 0;
var armStartSound;
var armClickSound;
var speedSlider;
var segmentAmountInput;
var createNewArmButton;
var totalLengthSlider;
function preload() {
// Load your sound files here
// armSounds.push(loadSound('joint.mp3')); // sounds[0]
armSounds.push(loadSound('joint2.mp3')); // sounds[1]
// armSounds.push(loadSound('base.mp3')); // sounds[2]
armSounds.push(loadSound('mechanicclamp.mp3')); // sounds[3]
armStartSound = loadSound('armstart.mp3')
armClickSound = loadSound('armclick.mp3')
}
function createArrayWithSum(n, sum) {
if (n <= 0) return [];
// Ensure sum is a multiple of 10 and at least 10n
sum = Math.max(Math.floor(sum / 10) * 10, 10 * n);
let array = new Array(n).fill(10);
sum -= 10 * n; // Adjust sum to account for initial distribution
// Incrementally distribute the remaining sum
let i = 0;
while (sum > 0) {
array[i % n] += 10;
sum -= 10;
i++;
}
// Introduce some randomness by redistributing values
for (let j = 0; j < n; j++) {
if (array[j] > 20) {
let indexToIncrease = Math.floor(Math.random() * n);
if (indexToIncrease !== j) {
array[j] -= 10;
array[indexToIncrease] += 10;
}
}
}
return array;
}
function createNewArm() {
var input = segmentAmountInput.value().trim();
var segments, armSegmentLengths;
var speed = map(speedSlider.value(), 0, 10, 0.005, 0.05);
var armLength = totalLengthSlider.value()
// Check if input is a single number or a comma-separated list
if (/^\d+$/.test(input)) {
// Case 1: Single number
segments = int(input);
armSegmentLengths = createArrayWithSum(segments, 250);
} else {
// Case 2 and 3: Comma-separated list, possibly with brackets
input = input.replace(/[\[\]]/g, ''); // Remove brackets if any
armSegmentLengths = input.split(',').map(function(item) {
return int(item.trim());
});
segments = armSegmentLengths.length;
}
print(segments, armSegmentLengths);
arm = new InverseKinematicArm(width / 2, height / 2, armSegmentLengths, speed);
spawnObjects();
}
function setup() {
cnv = createCanvas(300, 400);
let mainDiv = document.getElementById('main');
let controlPanelDiv = document.getElementById('control-panel');
mainDiv.insertBefore(cnv.canvas, controlPanelDiv);
for (const s of armSounds) {
s.setVolume(0.05);
}
totalLengthSlider=select("#lengthSlider")
speedSlider = select('#speedSlider');
segmentAmountInput = select('#segmentAmountInput');
createNewArmButton = select('#createNewArmButton');
createNewArmButton.mousePressed(createNewArm);
createNewArm()
tickAmountInput = select('#tickAmountInput');
tickButton = select('#tickButton');
tickButton.mousePressed(performTick);
finishButton = select('#finishButton');
finishButton.mousePressed(finishTask);
keepGoingCheckbox = select('#keepGoingCheckbox');
keep_going = keepGoingCheckbox
}
function InverseKinematicArm(x, y, segmentLengths, speed) {
this.base = createVector(x, y);
this.segments = [];
if (Array.isArray(segmentLengths)) {
segmentLengths.forEach((length, index) => {
this.segments.push(new Segment(length, speed, index === 0 ? null : this.segments[index - 1]));
});
} else {
for (let i = 0; i < segmentLengths; i++) {
this.segments.push(new Segment(50, speed, i === 0 ? null : this.segments[i - 1]));
}
}
this.segments.map(e => e.isLast=false)
this.segments[this.segments.length-1].isLast=true
}
InverseKinematicArm.prototype.reach = function(target) {
let endEffector = this.segments[this.segments.length - 1];
endEffector.follow(target);
endEffector.update();
for (let i = this.segments.length - 2; i >= 0; i--) {
this.segments[i].follow(this.segments[i + 1].base);
this.segments[i].update();
}
this.segments[0].setBase(this.base);
for (let i = 1; i < this.segments.length; i++) {
this.segments[i].setBase(this.segments[i - 1].end);
}
};
InverseKinematicArm.prototype.display = function() {
for (let segment of this.segments) {
segment.display();
}
};
var ii = 0;
function Segment(length, speed, parent) {
this.length = length;
this.angle = ii+=3;
this.parent = parent;
this.speed = speed;
this.isLast = false;
this.color = random()*75
this.w = random(1,5)
this.gearSize = random(4, 6);
this.gearTeeth = random(8, 32);
if (parent) {
this.base = parent.end.copy();
} else {
this.base = createVector(width/2,height/2);
}
this.calculateEnd();
}
Segment.prototype.follow = function(target) {
let dir = p5.Vector.sub(target, this.base);
let targetAngle = dir.heading();
let distanceToTarget = dir.mag();
var s = this
this.angle = lerp(this.angle, targetAngle, this.speed);
dir.setMag(this.length);
dir.mult(-1);
this.base = p5.Vector.add(target, dir);
};
Segment.prototype.calculateEnd = function() {
let dx = this.length * cos(this.angle);
let dy = this.length * sin(this.angle);
this.end = p5.Vector.add(this.base, createVector(dx, dy));
};
Segment.prototype.setBase = function(base) {
this.base = base.copy();
this.calculateEnd();
};
Segment.prototype.update = function() {
this.calculateEnd();
};
Segment.prototype.display = function() {
stroke(this.color)
strokeWeight(this.w)
line(this.base.x, this.base.y, this.end.x, this.end.y);
// Drawing a gear at each segment joint
drawGear(this.base.x, this.base.y, this.gearSize, this.gearTeeth, this.angle);
if (this.isLast && armHasItem) {
circle,(this.end.x, this.end.y, 10, 10);
fill(255, 0, 0);
ellipse(this.end.x, this.end.y, 10, 10);
}
};
// Function to draw a gear
function drawGear(x, y, size, teeth, rotation) {
push(); // Save the current drawing settings
translate(x, y); // Move the origin to the gear's center
rotate(rotation); // Rotate the gear
strokeWeight(1);
fill(128); // Set the color of the gear
beginShape(); // Start drawing the gear
for (let i = 0; i < teeth; i++) {
let angle = map(i, 0, teeth, 0, TWO_PI);
let x = cos(angle) * size;
let y = sin(angle) * size;
vertex(x, y);
x = cos(angle + 0.1) * size * 0.9;
y = sin(angle + 0.1) * size * 0.9;
vertex(x, y);
}
endShape(CLOSE); // Finish drawing the gear
pop(); // Restore the original drawing settings
}
function draw() {
background(225,225,225,15);
drawBaseBoxThingy() // draw some kind of base for under the arm lol
arm.display();
if (finishingTask)
{
work_on_task_till_finished()
}
t+=.05
drawObjects();
line(0,0, width, 0);
line(0,0, 0, height)
line(width,0,width,height)
line(0,height,width,height)
}
function drawBaseBoxThingy(){
push();
fill('saddlebrown');
let baseX = width / 2 - 30;
let baseY = height / 2 - 30;
rect(baseX, baseY, 60, 60);
fill('dimgray');
let rivetSize = 6;
for (let i = 0; i < 4; i++) {
let x = baseX + (i % 2) * 60;
let y = baseY + floor(i / 2) * 60;
circle(x, y, rivetSize);
}
stroke('rgb(117,117,117)');
strokeWeight(4);
noFill();
rect(baseX - 5, baseY - 5, 70, 70);
fill('gray')
stroke('black')
rect(width/2 -40, height/2+5,80, 40)
fill('darkslategray');
drawCog(width / 2, height / 2, 15, 12, t);
drawCog(width / 2+15, height / 2 -30,11, 7, t, -1);
drawCog(width / 2+33, height / 2 -15,6, 3, t, 1);
drawCog(width / 2+33, height / 2 -15,6, 3, t, 1, 7);
pop();
drawSteamStuff(width / 2 - 7, height / 2 -3);
drawSteamStuff(width / 2 + 7, height / 2 + 3);
// Drawing pressure gauges
drawPressureGauge(width / 2 + 20, height / 2 + 25, 7);
drawPressureGauge(width / 2 + 30, height / 2 + 25, 7 ,453.);
drawPressureGauge(width / 2 + 20, height / 2 + 35, 7,65.);
drawPressureGauge(width / 2 + 30, height / 2 + 35, 7 ,4553.);
drawPressureGauge(width / 2 - 20, height / 2 + 25, 20);
}
function drawSteamStuff(x, y) {
push();
fill(255, 255, 255, 150); // Semi-transparent steam
noStroke()
let steamSize = noise(t,x) * 26; // Varying size of steam
ellipse(x, y - steamSize / 2, steamSize, steamSize);
steamSize = noise(t,x*434+y) * 23
ellipse(x + 15, y - steamSize / 2 - 10, steamSize / 2, steamSize / 2);
steamSize = noise(t,x*434/y-x) * 23
ellipse(x - 15, y - steamSize / 2 - 5, steamSize / 1.5, steamSize / 1.5);
pop();
}
function drawPressureGauge(x, y, size, offset=76786.) {
push();
stroke('silver');
fill('darkslategray');
circle(x, y, size); // Gauge body
let needleAngle = noise(t*x/y,offset) * TWO_PI; // Oscillating needle angle
stroke('orange');
strokeWeight(2);
line(x, y, x + cos(needleAngle) * size*.3, y + sin(needleAngle) * size*.3);
pop();
}
function drawCog(x, y, radius, numTeeth, time, direction=1,offset=0) {
push();
translate(x, y);
rotate(time*direction + offset);
beginShape();
for (let i = 0; i < numTeeth; i++) {
let angle = map(i, 0, numTeeth, 0, TWO_PI);
let nextAngle = map(i + 1, 0, numTeeth, 0, TWO_PI);
let outerRadius = radius * 1.2;
let innerRadius = radius;
let x1 = cos(angle) * outerRadius;
let y1 = sin(angle) * outerRadius;
let x2 = cos(nextAngle) * outerRadius;
let y2 = sin(nextAngle) * outerRadius;
let x3 = cos(nextAngle) * innerRadius;
let y3 = sin(nextAngle) * innerRadius;
vertex(x1, y1);
vertex(x2, y2);
vertex(x3, y3);
}
endShape(CLOSE);
pop();
}
function performTick() {
armClickSound.setVolume(0.05);
armStartSound.setVolume(0.05);
let ticks = int(tickAmountInput.value());
armStartSound.play();
// Schedule the sound to play every 10ms
for (let i = 0; i < ticks; i++) {
setTimeout(function() {
arm.reach(objectA);
armClickSound.play();
}, i * 35);
}
}
function playSoundLoop(ticks, currentTick) {
if (currentTick < ticks) {
armClickSound.play();
arm.reach(objectA);
setTimeout(function() {
playSoundLoop(ticks, currentTick + 1);
}, 122);
}
}
function work_on_task_till_finished(){
// when global finishingTask is true, move a bit every frame
{
arm.reach(objectA);
if (p5.Vector.dist(arm.segments[arm.segments.length - 1].end, objectA) < 10) {
objectA = targetB;
armHasItem = true;
playRandomSound()
if (p5.Vector.dist(arm.segments[arm.segments.length - 1].end, targetB) < 10) {
armHasItem = false;
finishingTask = keep_going.checked() ? true : false;
spawnObjects();
}
}
}
}
function playRandomSound() {
let randomIndex = int(random(armSounds.length)); // Select a random index
armSounds[randomIndex].play(); // Play the sound at the selected index
}
function finishTask() {
playRandomSound()
finishingTask=true;
}
function spawnObjects() {
objectA = createVector(random(width), random(height));
targetB = createVector(random(width), random(height));
}
function moveObject() {
if (p5.Vector.dist(arm.segments[arm.segments.length - 1].end, objectA) < 10) {
objectA = targetB;
spawnObjects();
}
}
function drawObjects() {
strokeWeight(1);
fill(255, 0, 0);
ellipse(objectA.x, objectA.y, 10, 10);
fill(0, 255, 0);
ellipse(targetB.x, targetB.y, 10, 10);
}