This was inspired by an app I used to use on my mom’s iPod as a kid. I’m not very good at drawing, so I figured I would give the user a chance to draw. I’ve created a program based on degrees of symmetry that lets you alter the size of the brush, number of brushes, color of the brush, color of the background, and movement of the drawn elements. You can also save your creation by pressing p.
// variable definitions
var degOfSymmetry = 36;
var curAngle = 0;
var diameter = 10;
let drawnCircs = [];
var radiate = false;
var radiateVelocity = 1;
var showRef = true;
let currentColor = ["red", "green", "blue"];
let colorVals = [255, 255, 255];
let backgroundCols = [0, 0, 0];
var colorIndex = 0;
var currentSelect = "Currently selected: " + currentColor[colorIndex];
var whatKey = "";
var rainbowMode = false;
var rainbowCtr = 0;
var freq = 0.01;
function setup() {
createCanvas(600, 600);
background(220);
text("p5.js vers 0.9.0 test.", 10, 15);
}
function draw() {
// define background based on color arrays
background(backgroundCols[0], backgroundCols[1], backgroundCols[2]);
noStroke();
// set circle color based on user choice
let c = color(colorVals[0], colorVals[1], colorVals[2]);
if (rainbowMode) {
var rr = sin(rainbowCtr) * 127 + 128;
var rg = sin(rainbowCtr + (2*PI/3)) * 127 + 128;
var rb = sin(rainbowCtr + (4*PI/3)) * 127 + 128;
c = color(rr, rg, rb);
}
fill(c);
// self explanatory
getAngle();
// define a center, get relative mouse radius
var centerX = width / 2;
var centerY = height / 2;
var radX = mouseX - centerX;
var radY = mouseY - centerY;
var mouseRad = sqrt(pow(radX, 2) + pow(radY, 2));
// draw a circle for each degree of symmetry
for (i = 0; i < degOfSymmetry; i++) {
var loopAngle = curAngle - (i * 2*PI / degOfSymmetry)
var tX = centerX + mouseRad * cos(loopAngle);
var tY = centerY + mouseRad * sin(loopAngle);
circle(tX, tY, diameter);
// add to circle history if clicked
if (mouseIsPressed) {
drawnCircs.push([tX, tY, diameter, c, loopAngle]);
}
}
// draw each circle from history
for (i = 0; i < drawnCircs.length; i++) {
fill(drawnCircs[i][3]);
circle(drawnCircs[i][0], drawnCircs[i][1], drawnCircs[i][2]);
// if radiating, begin changing circle positions
if (radiate) {
drawnCircs[i][0] += radiateVelocity * cos(drawnCircs[i][4]);
drawnCircs[i][1] += radiateVelocity * sin(drawnCircs[i][4]);
if (drawnCircs[i][0] > width || drawnCircs[i][0] < 0) {
drawnCircs.splice(i, 1); // remove circles for speed
} else if (drawnCircs[i][1] > height || drawnCircs[i][1] < 0) {
drawnCircs.splice(i, 1); // remove circles for speed
}
}
}
// increment counter for rainbow mode
rainbowCtr = (rainbowCtr + freq) % (2*PI);
// display onscreen reference
if (showRef) {
fill(255);
text("ref: c=change color, d=change degrees of symmetry " +
"s=change circle size, b=background color \n" +
"o=clear screen, r=radiate outwards, p=save screen as png " +
"v=set radiate velocity\n q=rainbow color cycle, " +
"up/down arrow=color/velocity val+-1, right/left arrow=+-10\n" +
"i=change selected color, enter=hide ref, y=rainbow speed",
10, height - 50);
text(currentSelect, 10, 15);
text("RGB: " + (colorVals.toString(10)), 10, 30);
text("Radiate Velocity " + radiateVelocity.toString(10), 10, 45);
text("Background RGB: " + (backgroundCols.toString(10)), 10, 60);
text("Circle Size: " + (diameter.toString(10)), 10, 75);
text("Degrees of Symmetry: " + (degOfSymmetry.toString(10)), 10, 90);
text("Rainbow Frequency: " + (freq.toString(10)), 10, 105);
}
}
// gets angle from mouse pos
function getAngle() {
curAngle = atan2(mouseY - height / 2, mouseX - width / 2);
if (mouseX - width / 2 == 0) {
if (mouseY - height / 2 > 0) {
curAngle = HALF_PI;
} else {
curAngle = 3 * HALF_PI;
}
}
}
function keyPressed() {
// checks what key is pressed, performs an action, or sets a flag
if (key == "c") {
whatKey = "c";
currentSelect = "Currently selected: " + currentColor[colorIndex];
}
if (key == "o") {
drawnCircs = [];
}
if (key == "s") {
whatKey = "s";
currentSelect = "Currently selected: circle size";
}
if (key == "b") {
whatKey = "b";
currentSelect = "Currently selected: background color";
}
if (key == "d") {
whatKey = "d";
currentSelect = "Currently selected: degrees of symmetry";
}
if (key == "r") {
radiate = !radiate;
}
if (key == "q") {
rainbowMode = !rainbowMode;
}
if (key == "v") {
whatKey = "v";
currentSelect = "Currently selected: velocity"
}
if (key == "p") {
save("canvas_drawing.png");
}
if (key == "i") {
colorIndex = (colorIndex + 1) % 3;
currentSelect = "Currently selected: " + currentColor[colorIndex];
}
if (key == "Enter") {
showRef = !showRef;
}
if (key == "y") {
whatKey = "y";
currentSelect = "Currently selected: Rainbow Cycle Speed";
}
// changing parameters that affect drawing and motion
if (keyCode == UP_ARROW) {
if (whatKey == "d") {
degOfSymmetry += 1;
}
if (whatKey == "c") {
colorVals[colorIndex] += 1;
}
if (whatKey == "v") {
radiateVelocity += 1;
}
if (whatKey == "b") {
backgroundCols[colorIndex] += 1;
}
if (whatKey == "s") {
diameter += 1;
}
if (whatKey == "y") {
freq += 0.01;
}
}
if (keyCode == DOWN_ARROW) {
if (whatKey == "d") {
degOfSymmetry -= 1;
}
if (whatKey == "c") {
colorVals[colorIndex] -= 1;
}
if (whatKey == "v") {
radiateVelocity -= 1;
}
if (whatKey == "b") {
backgroundCols[colorIndex] -= 1;
}
if (whatKey == "s") {
diameter -= 1;
}
if (whatKey == "y") {
freq -= 0.01;
}
}
if (keyCode == RIGHT_ARROW) {
if (whatKey == "d") {
degOfSymmetry += 10;
}
if (whatKey == "c") {
colorVals[colorIndex] += 10;
}
if (whatKey == "v") {
radiateVelocity += 10;
}
if (whatKey == "b") {
backgroundCols[colorIndex] += 10;
}
if (whatKey == "s") {
diameter += 10;
}
if (whatKey == "y") {
freq += 0.10;
}
}
if (keyCode == LEFT_ARROW) {
if (whatKey == "d") {
degOfSymmetry -= 10;
}
if (whatKey == "c") {
colorVals[colorIndex] -= 10;
}
if (whatKey == "v") {
radiateVelocity -= 10;
}
if (whatKey == "b") {
backgroundCols[colorIndex] -= 10;
}
if (whatKey == "s") {
diameter -= 10;
}
if (whatKey == "y") {
freq -= 0.10;
}
}
return false;
}
Getting the symmetry correct was a little difficult, as well as figuring out how to deal with user input.