xxxxxxxxxx
187
// p5 variables
let heartWaveS;
let heartWaveC;
let song;
// arduino variables
let heartData1 = 350;
let heartData2 = 350;
let sensors = [];
// adding music - Arriving by Jon Hopkins, supplemented with heartbeat sound from https://freesound.org/people/sqeeeek/sounds/237104/
function preload() {
song = loadSound("Arriving.mp3");
}
function setup() {
createCanvas(windowWidth, windowHeight);
strokeWeight(0.1);
noFill();
// radians instead of degrees, a radian is the angle that creates an arc length equal to the radius of the circle, easier to work with PI
angleMode(RADIANS);
// slow it down from 60
frameRate(20);
song.play();
// initialize class with noise scale and noise speed - smaller numbers mean less noise + slower movement
heartWaveS = new NoiseSine(0.005, 0.001);
heartWaveC = new NoiseCos(0.005, 0.001);
// set up serial communication with Arduino
setupSerial();
}
function draw() {
background("black");
// run the two functions
heartWaveS.run();
heartWaveC.run();
}
// HEARTSINE
// class containing multiple sinewaves from HeartSine class, constructor takes two parameters
function NoiseSine(xscale, yspeed) {
// empty array of waves
this.wavesS = [];
// complexity of the wave, how many loops - higher number means denser wave
this.NS = 60;
// for loop that runs 60 times to create a new heartsine object during each iteration
for (let i = 0; i < this.NS; i++) {
// y attenuation: scales y amplitude between 10-20% of window height
let yAtten = map(i, 0, this.NS, height * 0.1, height * 0.2);
// heartsine created with xscale horizontal scaling, yspeed, yatten, and small offset to loop index - slight phase shift between waves
this.wavesS.push(new HeartSine(xscale, yspeed, yAtten, i * 0.01));
}
}
// all objects using NoiseSine will inherit this run method - i read that .prototype.run is good for saving memory + attaching all function data to the 'NoiseSine' or whatever instance called
NoiseSine.prototype.run = function () {
// for loop running NS = 60 times, drawing each heartsine wave
for (let i = 0; i < this.NS; i++) {
this.wavesS[i].display();
}
};
function HeartSine(xScale_, ySpeed_, yAtten_, offset_) {
// segments used to draw the wave
this.NS = 155;
// horizontal scaling
this.xScale = xScale_;
// vertical speed
this.ySpeed = ySpeed_;
// vertical scaling/amplitude
this.yAtten = yAtten_;
// offset shift to form a unique position for each wave in the animation
this.offset = offset_;
}
// draw those waves
HeartSine.prototype.display = function () {
stroke("white");
// sacling the size
let r = windowWidth / 4;
push();
// translate the origin to canvas center
translate(width / 2, height / 2);
//define vertices in shape
beginShape();
// loop through the number of points/segments that make up the shape - set to 155 earlier
for (let i = 0; i < this.NS; i++) {
// x position scaled to mangitude of 1, spans the width of the window
let x = norm(i, 0, this.NS) * width;
// creates motion, lerp returns smooth variation for vertical displacement, y_off, smoothly varies between +/- max/min vertical attenuation, perlin noise controls further smoothing out the points being drawn
let y_off = lerp(
-this.yAtten,
this.yAtten,
//frameCount speed-ish
noise(x * this.xScale, frameCount * 0.3 * this.ySpeed + this.offset)
);
// vertical position of sine wave + randomness from y_off
let y = (-height / 6) * sin(heartData1) + y_off;
// shifts the horizontal phase/orientation of the angle
let angle1 = radians(x) + PI;
// shifts vertical phase/orientation
let angle2 = radians(y) + PI;
// new x coordinate calculation, horizontal
x = r * sin(heartData1) * cos(angle2);
// new y coordinate, vertical
y = r * sin(angle1) * sin(angle2);
// adds a vertex to the shape at the position (x,y) to create smooth curves
curveVertex(x, y);
}
endShape();
pop();
};
// HEARTCOS
// class that was supposed to contain multiple coswaves from HeartCos class, but I actually ended up cheating myself and just made another sine wave class in the opposite direction
function NoiseCos(xScale, ySpeed) {
this.wavesC = [];
this.NC = 60;
for (let i = 0; i < this.NC; i++) {
let xAtten = map(i, this.NC, 0, width * 0.01, width * 0.2);
this.wavesC.push(new HeartCos(xScale, ySpeed, xAtten, i * 0.01));
}
}
NoiseCos.prototype.run = function () {
for (let i = 0; i < this.NC; i++) {
this.wavesC[i].display();
}
};
function HeartCos(xScaleC_, ySpeedC_, xAttenC_, offsetC_) {
this.NC = 155;
this.xScaleC = -xScaleC_;
this.ySpeedC = -ySpeedC_;
this.xAttenC = xAttenC_;
this.offsetC = offsetC_;
}
// draw those waves
HeartCos.prototype.display = function () {
stroke("rgb(228,192,188)");
let r = windowWidth / 4;
push();
translate(width / 2, height / 2);
beginShape();
for (let i = 0; i < this.NC; i++) {
let x2 = norm(i, 0, this.NC) * width;
let x_off = lerp(
this.xAttenC,
-this.xAttenC,
//frameCount speed, or use frameRate, come back to this
noise(x2 * this.xScaleC, frameCount * 0.3 * this.ySpeedC + this.offsetC)
);
// complementary cosine wave, which is actually a sine wave now, so guess it's not cos
let y2 = (-height / 3) * cos(heartData2) + x_off;
let angle1 = radians(x2) + PI;
let angle2 = radians(y2) + PI;
y2 = r * sin(heartData2) * cos(angle2);
x2 = r * sin(angle1) * sin(angle2);
curveVertex(x2, y2);
}
endShape();
pop();
};
// ARDUINO SERIAL COMMUNICATION
function serialEvent() {
// read a string from the serial port
// until you get carriage return and newline:
let inString = serial.readStringUntil("\r\n");
//check to see that there's actually a string there:
if (inString) {
let sensors = split(inString, ",");
// console.log(sensorsAvg);
heartData1 =
map(int(sensors[0]), 350, 1000, 0, TWO_PI) * 0.1 + heartData1 * 0.9;
heartData2 =
map(int(sensors[1]), 350, 1000, 0, TWO_PI) * 0.1 + heartData2 * 0.9;
// console.log(sensors);
}
serial.write("x");
}