xxxxxxxxxx
268
/* Draw on the canvas with left click. Each stroke creates a new shape (it is not
* continous). Press space to accelerate the simulation, backspace to
* decimate/erase the most recent shape and control to switch between display
* modes. The two display modes are either radial lines or individual shapes with
* random colours
* Squiggle speed sets the speed of the points, Simplify time sets the speed that
* erasing goes at.
*/
let canvasWidth = 1200; //400
let canvasHeight = 1200;
// Colours for the shape-mode gradient
let colour1;
let colour2;
function setup()
{
createCanvas(canvasWidth, canvasHeight);
// Generates random
colorMode(HSB);
colour1 = color(random(360), random(100), 50 + random(50));
colour2 = color(random(360), random(100), 50 + random(50));
}
// Alternative display method, press Crtl to change to it
let shapeMode = false;
let displayKeyHeld = false;
// The minimum distance between points before getting deleted. Only used in the
// shapeMode
let minimumPointDistance = 0.5;
let mouseHeld = false;
// Named squiggles because line is an already used name. And because its funny
let squiggles = [];
// Speed of the points in the lines. Beware going over 1, weird things start
// happening
let squiggleSpeed = 0.2;
let simplifyTimer = 0;
let simplifyTime = 0.5;
let start = true;
function draw()
{
// SETUP
background(0);
// Changes the measurement of time from milliseconds to seconds for easier math
deltaTime = deltaTime / 100;
// CONTROLS
// Accelerate time itself! with a click of the button!
if (keyIsDown(32))
deltaTime *= 10;
// Limit the intervals between decimations/simplifications of the line/shape
if (simplifyTimer < simplifyTime)
simplifyTimer += deltaTime;
if (keyIsDown(BACKSPACE))
{
// On every time interval, put the latest drawn line into the simplify
// function, to delete every second point
if (squiggles.length > 0 && simplifyTimer >= simplifyTime)
{
simplifyTimer = 0;
squiggles[squiggles.length - 1] = simplifyLine(squiggles[squiggles.length - 1])
}
}
// Toggles the display mode when controll is pressed
if (keyIsDown(CONTROL))
{
// Only happens once while the key is held down, rather than every frame
if (!displayKeyHeld)
{
displayKeyHeld = true;
shapeMode = !shapeMode;
}
}
else
displayKeyHeld = false;
// DRAW POINTS
if (mouseIsPressed)
{
// Start a new line every time you have release the mouse and then begin
// drawing again
if (!mouseHeld)
{
mouseHeld = true;
squiggles.push(new squiggle());
}
// Add another point at the location of the mouse to the current line
squiggles[squiggles.length - 1].points.push(new point(mouseX, mouseY));
}
else
{
mouseHeld = false;
}
// DISPLAY AND MOVE EACH POINT
// Iterate through each line that has been created
for (let i = 0; i < squiggles.length; i ++)
{
// Kill and ignore the line if it has no points left in it
if (squiggles[i].points.length == 0)
squiggles.splice(i, 1)
else
{
// Iterate through each point in the line except the last point. The last
// point is handled seperately because it has different behaviours
for (let j = 0; j < squiggles[i].points.length - 1; j ++)
{
// The index of the next point in the line
let next = j + 1;
// Get the gradient between this point and the next in the line
let rise = squiggles[i].points[next].y - squiggles[i].points[j].y;
let run = squiggles[i].points[next].x - squiggles[i].points[j].x;
// Apply the gradient modified by time and the speed to the point so that
// it moves towards the next point in the line. The farther apart the
// points are, the faster they move - which often means that lines that
// were drawn faster will move faster because of the limits of framerate
squiggles[i].points[j].x += run * deltaTime * squiggleSpeed;
squiggles[i].points[j].y += rise * deltaTime * squiggleSpeed;
// Used in the shape-mode display method to kill the points if they are too
// bunched up together
let distance;
// Calculates distance
if (shapeMode)
distance = sqrt(pow(rise, 2) + pow(run, 2));
// Ignores it if its not in the shape-mode
else
distance = minimumPointDistance;
// If the point is now outside of the bounds (or too close together) then
// it gets deleted
if (squiggles[i].points[j].x > canvasWidth ||
squiggles[i].points[j].x < 0 ||
squiggles[i].points[j].y > canvasHeight ||
squiggles[i].points[j].y < 0 ||
distance < 0.5)
{
squiggles[i].points.splice(j, 1)
}
// Otherwise it gets displayed
else
{
// Displays the data in 2 alternative methods
if (shapeMode)
{
// Gets the colour from a gradient stretching across the screen
let Inter = lerpColor(colour1, colour2,
squiggles[i].points[j].y / canvasHeight);
stroke(Inter);
// Draws a line from the current point to the next in the line
line(squiggles[i].points[j].x,
squiggles[i].points[j].y,
squiggles[i].points[next].x,
squiggles[i].points[next].y);
}
else
{
// Line colour to white
stroke(255);
// Draws a line from the current point to the centre of the screen
line(squiggles[i].points[j].x,
squiggles[i].points[j].y,
canvasWidth / 2, canvasHeight / 2);
}
}
}
// If there are at least 2 points in this line then handle the last point
// differently
if (squiggles[i].points.length > 1)
{
let last = squiggles[i].points.length - 1;
// Gets the gradient between the last and first point so that follows the
// tail of the line/shape
let rise = squiggles[i].points[last].y - squiggles[i].points[0].y;
let run = squiggles[i].points[last].x - squiggles[i].points[0].x;
// Distance between first and last point, used for normalising the vector
// of the movement so that it has a consistent speed. This can't be done on
// the other points because of how inconsistent the distances are, meaning
// that theres a chance this would make the points overshoot where they are
// supposed to be.
let distance = sqrt(pow(rise, 2) + pow(run, 2))
// If they aren't inside each other (To avoid diving by 0) then move the
// last point towards the first point by squiggleSpeed pixels per second.
if (distance != 0)
{
squiggles[i].points[last].x += -run * deltaTime * squiggleSpeed * 10 / distance;
squiggles[i].points[last].y += -rise * deltaTime * squiggleSpeed * 10 / distance;
}
// Delete the last point if it goes outside the bounds of the canvas
if (squiggles[i].points[last].x > canvasWidth ||
squiggles[i].points[last].x < 0 ||
squiggles[i].points[last].y > canvasHeight ||
squiggles[i].points[last].y < 0)
{
squiggles[i].points.pop();
}
}
}
}
}
/* A function that recieves a line, deletes every second point, and then returns
* the new version of that line. Used when decimating a shape to delete it, but
* could also be used once a line has finished being drawn to reduce the computing
* used to process and display the line
*/
function simplifyLine(simplifiedLine)
{
// Loops through each second point in a line and deletes it
for (let i = 0; i < simplifiedLine.points.length; i += 2)
{
simplifiedLine.points.splice(i, 1);
}
// Returns the new version of the line
return simplifiedLine;
}
/* The class defining a line. Each contains an array of points
*/
class squiggle
{
constructor()
{
this.points = [];
}
}
/* The class defining a line. Each contains an x and y coordinate
*/
class point
{
constructor(xInput, yInput)
{
this.x = xInput;
this.y = yInput;
}
}