UPDATE: Nevermind, I found the bug that forces the double click. It was coming from inside the populateOptions function, because the button axis was already set to "fill" I would need to click twice on the button to change back to "nofill" so what I did was I am emptying the self.axis of the button now, every time I change the color so it works flawlessly. It might not be the best approach but it works. If you have any ideas for a better approach, let me know.
My approach, based on this global variable that turns true when I click another color inside the colorPalette, I've put this inside populateOptions so when it gets called from colorPalette, this runs first and clears the state of the button:
if (changedColor) {
self.axis = "";
changedColor = false;
}
I'm working on a drawing app project for University, I am only allowed to use vanilla Javascript with p5.js. I have different constructors in separate files, for example the colourPalette where the user selects a color (and the fill of that color is declared inside this constructor), another circleTool constructor where I'm creating an ellipse and when this tool is selected, a button appears that can toggle between filled/unfilled.
Now my issue is that the toggle button works at first with no issues but when I'm selecting a new color while the button is "not filled", the fill inside colourPalette is enabled again but my button does not update so I came up with the idea of calling the populateOptions() function from the circleTool every time I change the color because this will recreate the button in the "filled" state. But the issue with this is that when I change color, I now need to click on the button twice until unfilled takes effect, see the below code:
colourPalette constructor: This is where I'm calling the populateOptions() function every time I select a new color so that my button resets every time with the fill state
//Displays and handles the colour palette.
function ColourPalette() {
//a list of web colour strings
this.colours = ["black", "silver", "gray", "white", "maroon", "red", "purple",
"orange", "pink", "fuchsia", "green", "lime", "olive", "yellow", "navy",
"blue", "teal", "aqua"
];
//make the start colour be black
this.selectedColour = "black";
var self = this;
var colourClick = function() {
//remove the old border
var current = select("#" + self.selectedColour + "Swatch");
current.style("border", "0");
//get the new colour from the id of the clicked element
var c = this.id().split("Swatch")[0];
//set the selected colour and fill and stroke
self.selectedColour = c;
fill(c);
stroke(c);
circleTool.unselectTool();
circleTool.populateOptions();
//add a new border to the selected colour
this.style("border", "2px solid blue");
}
//load in the colours
this.loadColours = function() {
//set the fill and stroke properties to be black at the start of the programme
//running
fill(this.colours[0]);
stroke(this.colours[0]);
//for each colour create a new div in the html for the colourSwatches
for (var i = 0; i < this.colours.length; i++) {
var colourID = this.colours[i] + "Swatch";
//using JQuery add the swatch to the palette and set its background colour
//to be the colour value.
var colourSwatch = createDiv()
colourSwatch.class('colourSwatches');
colourSwatch.id(colourID);
select(".colourPalette").child(colourSwatch);
select("#" + colourID).style("background-color", this.colours[i]);
colourSwatch.mouseClicked(colourClick)
}
select(".colourSwatches").style("border", "2px solid blue");
};
//call the loadColours function now it is declared
this.loadColours();
}
circleTool constructor: This is where the populateOptions function is, where the button is controlled.
//Tool to draw circles
function CircleTool() {
this.name = "circleTool";
this.icon = "/assets/circle.jpg";
var startMouseX = -1;
var startMouseY = -1;
var drawing = false;
this.draw = function() {
//Function to draw the circle
if(mouseIsPressed) {
if(startMouseX == -1) {
drawing = true;
startMouseX = mouseX;
startMouseY = mouseY;
loadPixels();
}
else {
updatePixels();
ellipse(startMouseX,startMouseY,dist(startMouseX,startMouseY,mouseX,mouseY));
}
}
else if(drawing) {
drawing = false;
startMouseX = -1;
startMouseY = -1;
}
}
//This will clear the button from the canvas when circleTool is unselected
this.unselectTool = function() {
updatePixels();
//clear options
select(".options").html("");
};
//adds a button and click handler to the options area. When clicked
//toggle the fill of the circle
this.populateOptions = function() {
select(".options").html(
"<button id='circleButton'>Filled Circle</button>");
// //click handler
select("#circleButton").mouseClicked(function() {
var button = select("#" + this.elt.id);
if (self.axis == "fill") {
self.axis = "notFill";
button.html('Filled Circle');
fill(colourP.selectedColour);
}
else {
self.axis = "fill";
self.lineOfSymmetry = width / 2;
noFill();
button.html('Not Filled');
}
});
};
}
In order to eliminate the problem of the need to click the button twice so unfilled takes effect, I tried to control all this filled/unfilled with a global variable but it did not work because even if I controled the state of the button with a boolean, the button will not update its state unless I would call the populateOptions function again.
I just want this button to control and toggle the fill state of my circle and I'm not sure how to approach this, seems an easy problem but can't figure it out.
Perhaps you can get some ideas for completing your project by looking at the following example. The demo will create an array of circles base on a Circle class and then allow the user to change the color of each circle by using a colorPicker. Drawing the circles is performed by dragging the mouse with the 'shift' key down to create a bounding square with the circle centered inside. Subsequent hit testing is by checking mouse coordinates to see if they fall within the bounding square (displayed for demonstration purposes). Other techniques could be used to see if the mouse point falls within the circle, eg Pythagorean theorem. To use the demo use the following procedure:
Second example shows how to construct a toggle button.
let crc = [];
let shiftDown = false;
let x, y, x1, y1;
let w, h;
let selectedCrc;
let myPicker;
class Circle {
constructor(xpos, ypos, diam, fillColor) {
this.x = xpos;
this.y = ypos;
this.d = diam;
this.bkgrnd = fillColor;
this.selected = false;
}
display() {
for (let i = 0; i < crc.length; i++) {
fill(crc[i].bkgrnd);
circle(crc[i].x, crc[i].y, crc[i].d);
}
}
}
function setup() {
createCanvas(800, 800);
background(209);
myPicker = createColorPicker("deeppink");
myPicker.position(width - 100, 10);
let button = createButton("change color of selected circle");
button.position(width - 320, 15);
button.mousePressed(() => {
if (crc[selectedCrc].selected) {
crc[selectedCrc].bkgrnd = myPicker.color();
fill(crc[selectedCrc].bkgrnd);
circle(crc[selectedCrc].x, crc[selectedCrc].y, crc[selectedCrc].d);
}
});
}
function draw() {
for (let i = 0; i < crc.length; i++) {
crc[i].display();
}
}
function mousePressed() {
if (shiftDown) {
x = mouseX;
y = mouseY;
}
print("x:" + mouseX + ",y:" + mouseY);
for (let i = 0; i < crc.length; i++) {
// Reconstruct bounding rectangle from circle coordinates and diameter
if (
mouseX >= crc[i].x - crc[i].d / 2 &&
mouseX <= crc[i].x + crc[i].d / 2 &&
mouseY >= crc[i].y - crc[i].d / 2 &&
mouseY <= crc[i].y + crc[i].d / 2
) {
print("isInCircleRect =", i);
crc[i].selected = true;
selectedCrc = i;
print("selectedCircle id =", selectedCrc);
} else {
crc[i].selected = false;
print("unselectedCircle =", i);
}
}
}
function mouseDragged() {
if (shiftDown) {
x1 = mouseX;
y1 = mouseY;
w = x1 - x;
h = y1 - y;
}
}
function mouseReleased() {
if (shiftDown) {
x1 = mouseX;
y1 = mouseY;
diam = x1 - x;
w = x1 - x;
h = y1 - y;
// center circle in bounding rectangle
crc.push(new Circle(x + w / 2, y + w / 2, diam, color(255)));
// display bounding rectangle
noFill();
rect(x, y, w, w);
}
diam = 0; // reset to zero
print(crc);
}
function keyPressed() {
if (keyCode == SHIFT) {
shiftDown = true;
}
print("shiftDown = ", shiftDown);
}
function keyReleased() {
if (keyCode == SHIFT) {
shiftDown = false;
}
print("shiftDown = ", shiftDown);
}
Toggle Button Demo
var btnState = false;
let colorPicker;
function setup() {
createCanvas(400, 400);
background(209);
fill(0);
circle(200, 200, 100);
myPicker = createColorPicker('deeppink');
myPicker.position(110, 10);
let button = createButton("fill/unfill");
button.position(20, 10);
// Use button to change circle's color.
button.mousePressed(() => {
if (!btnState) {
btnState = true;
fill(255);
circle(200, 200, 100);
} else {
btnState = false;
fill(myPicker.value());
circle(200, 200, 100);
}
});
}
function draw() {
}