Greetings!
I have four bezier curves (pasted code for two below as MCVE), and would like to be able to show only one if the mouse is hovered over the respective one.
I tried finding how to assign a class to the curve, use a distance condition, but all were failed attempts. I could use some guidance, so if anyone could help me out here, pretty please? thank you!
var activitesCurve = function(bzCurve) {
bzCurve.setup = function() {
var run = [25, 50, 60, 70];
var walk = [15, 75, 80, 40];
bzCurve = new BzCurve();
}
bzCurve.draw = function() {
bzCurve.show();
class BzCurve {
constructor() {
this.stroke = (239, 31, 193);
this.margin = 50;
this.height = bzCurve.windowHeight;
this.arr = run;
this.maxLength = run.length;
this.pointGap = position;
}
show() {
bezierCurve.stroke(this.stroke);
bezierCurve.beginShape();
bezierCurve.vertex(this.margin, this.height / 2 - this.arr[0]);
for (int i = 1; i < this.maxLength; i++) {
var posX = bezierCurve.float(i * this.pointGap + this.margin);
var posY = bezierCurve.float(this.height / 2 - this.arr[i]);
var c1 = bezierCurve.float((i - 1) * this.pointGap + this.margin);
var c2 = bezierCurve.float(this.height / 2 - this.arr[i - 1]);
var c3 = posX;
var c4 = posY;
bezierCurve.bezierVertex(c1, c2, c3, c4, posX, posY);
}
bezierCurve.endShape();
}
}
}
// Walk Line
bezierCurve.stroke(23, 225, 0);
bezierCurve.beginShape();
bezierCurve.vertex(margin, height / 2 - walk[0]);
for (int i = 1; i < walk.length; i++) {
var posWX = bezierCurve.float(i * pointGap + margin);
var posWY = bezierCurve.float(height / 2 - walk[i]);
var cW1 = bezierCurve.float((i - 1) * pointGap + margin);
var cW2 = bezierCurve.float(height / 2 - walk[i - 1]);
var cW3 = posWX;
var cW4 = posWY;
bezierCurve.bezierVertex(cW1, cW2, cW3, cW4, posWX, posWY);
}
}
First, make a class for your curves. Make a Boolean variable within the class for visibility.
Within your class, create a update function and a draw function. During the draw loop, run your update function for your curve(s) which you will have code to check the distance from the mouse; if it within a certain threshold, set the visibility variable to true (all within the update function still). Then call your draw function for that curve which you will have coded to only draw if the vis is true. This could be simplified but it is the default way I would approach it.
Btw - class for a curve would be something like this:
Class myCurve{
Boolean myVisibility;
//add variable you need to define the curve etc
//constructor below
Void myCurve(){
//build the base curve here
}
Void update(){
//check the mouse pos vs your curve pos here
}
Void drawCurve(){
//put your drawing stuff here
}
}
āāā
Apologies as I messed up, Iām working on P5js and not Processing :-/
Also made updates, Iām working with multiple Canvases, this one named bezierCurve. The updated code is;
bezierCurve.stroke(239, 31, 193);
// Run Line
bezierCurve.beginShape();
bezierCurve.vertex(margin, height/2-run[0]);
for (int i=1; i<run.length; i++) {
var posX = bezierCurve.float(i*pointGap+margin);
var posY = bezierCurve.float(height/2-run[i]);
var c1 = bezierCurve.float((i-1)*pointGap+margin);
float c2 = bezierCurve.float(height/2-run[i-1]);
float c3 = posX;
float c4 = posY;
bezierCurve.bezierVertex(c1, c2, c3, c4, posX, posY);
}
bezierCurve.endShape();
// Walk Line
bezierCurve.stroke(23, 225, 0);
bezierCurve.beginShape();
bezierCurve.vertex(margin, height/2-walk[0]);
for (int i=1; i<walk.length; i++) {
var posX = bezierCurve.float(i*pointGap+margin);
var posY = bezierCurve.float(height/2-walk[i]);
var c1 = bezierCurve.float((i-1)*pointGap+margin);
float c2 = bezierCurve.float(height/2-walk[i-1]);
float c3 = posX;
float c4 = posY;
bezierCurve.bezierVertex(c1, c2, c3, c4, posX, posY);
}
I tried adding a class but not able to define it for some reason.
bzCurve = new BzCurve();
followed by defining the class with a nested function to display (show)
Ah, on the road and have not played with p5.js too much. The only thing that I could see quickly is that there isnāt a constructor in your class, but this may be different in p5.js. Maybe one of the other lads will jump in with suggestions.
Thanks for letting know William, I do have a constructor included. Left it out to keep the MCVE down to M
Still trying to figure this out so Iām hoping a lad would chime in and help out here.
Cheers!
For each curve, enumerate the coordinates of the points that lie on it and check, using the mouse coordinate, for an overlap.
An O(n) time complexity and pixelCount space complexity solution to determine mouse hover is to create a 2D array with the dimensions of the screen and assign a number to each pixel indicating the ID of the bezier curve that overlays the given pixel (where a value of 0, or -1 could indicate that no bezier curve intersects the pixel). During runtime, lookup array[mouseX][mouseY] to determine intersection (if any) between the mouse coordinate and bezier curves.
How might you determine the coordinates of points that lie on a bezier curve?
Processing provides the following method: bezierPoint(a, b, c, d, t), whereā¦
a float: coordinate of first point on the curve
b float: coordinate of first control point
c float: coordinate of second control point
d float: coordinate of second point on the curve
t float: value between 0 and 1
ā¦to enumerate the coordinates of the points that lie on each bezier curve.
Iterate over t (increments of 0.005 would seem suitable) to find coordinates of points over the entirety of each curve.
Note that for each value t, the x and y coordinates of the point are calculated separately, by feeding in solely x values of the control points to bezierPoint(), followed by another call where solely y values of control points are input. See the reference for more information.
I will admit, I read and have to yet read your response multiple times to make sense of it. Seems a bit too complicated for where I am at the moment, which is stuck trying to figure out why Iām not even able to define a new class for one curve on one of the two canvases.
Seems like setting up a mouseOver/ mousePressed function on a class would be the simplest way to do achieve my goal here, if only I can know why I see the error that my class is not defined when it is.
Sure, all pale and one curve highlighted sounds good. I meant to hide all but one āonly on hoverā of the respective one. So curve A,B,C,D loads just fine but when user hovers over A, the others B,C and D could be hidden or even better as you suggested, pale. Thanks for clarifying.
Point noted on mcve. Iāve updated the code and also pasted below. Iām still debugging logic here, so kindly pardon the novice approach here. http://tiny.cc/fbisbz
If you could improve, that would be simply perfect!
var activitesCurve = function(bzCurve) {
bzCurve.setup = function() {
var run = [25, 50, 60, 70];
var walk = [15, 75, 80, 40];
bzCurve = new BzCurve();
}
bzCurve.draw = function() {
bzCurve.show();
class BzCurve {
constructor() {
this.stroke = (239, 31, 193);
this.margin = 50;
this.height = bzCurve.windowHeight;
this.arr = run;
this.maxLength = run.length;
this.pointGap = position;
}
show() {
bezierCurve.stroke(this.stroke);
bezierCurve.beginShape();
bezierCurve.vertex(this.margin, this.height / 2 - this.arr[0]);
for (int i = 1; i < this.maxLength; i++) {
var posX = bezierCurve.float(i * this.pointGap + this.margin);
var posY = bezierCurve.float(this.height / 2 - this.arr[i]);
var c1 = bezierCurve.float((i - 1) * this.pointGap + this.margin);
var c2 = bezierCurve.float(this.height / 2 - this.arr[i - 1]);
var c3 = posX;
var c4 = posY;
bezierCurve.bezierVertex(c1, c2, c3, c4, posX, posY);
}
bezierCurve.endShape();
}
}
}
// Walk Line
bezierCurve.stroke(23, 225, 0);
bezierCurve.beginShape();
bezierCurve.vertex(margin, height / 2 - walk[0]);
for (int i = 1; i < walk.length; i++) {
var posWX = bezierCurve.float(i * pointGap + margin);
var posWY = bezierCurve.float(height / 2 - walk[i]);
var cW1 = bezierCurve.float((i - 1) * pointGap + margin);
var cW2 = bezierCurve.float(height / 2 - walk[i - 1]);
var cW3 = posWX;
var cW4 = posWY;
bezierCurve.bezierVertex(cW1, cW2, cW3, cW4, posWX, posWY);
}
}
If you have four curves and they arenāt updating every frame, then the picking algorithm is probably faster and more flexible.
create an index of colors, 1 for each stroke.
create a Graphics
set a thick stroke (e.g. 3-4), and draw each curve to the Graphics in a different color. update only when the curve locations change.
to check what stroke you are over, get your mouse pointer pixel from the Graphics and look it up in the index.
That is going to be way, way faster than trying to compute distance. because the picking lookup is layered, it will also avoid a lot of weird corner cases with distance detection ā at each intersection, some lines will be implicitly āaboveā other lines because their pixels are drawn onto the lookup later.
Later, if you want to ovals or hexagons or rotated trapezoids or whatever, picking will handle that too ā it doesnāt need new collision math for new entities.
I have SO much to learn. Could you help with resources on where to start?
Kindly advice what is a picking algorithm?
By index of colors, did you mean an array (just being sure here)?
Also, to clarify, I should try drawing all curve to the one Graphics created right?
I didnāt understand this point. I know how to get x and y coordinates using mousex and mouseY but not sure what you meant by looking it up in the index.
Notice that there are two kinds of drawing from the curve data:
drawing the map, which is done in unique colors that are added to an index.
drawing on the sketch canvas ā the curves donāt need to be unique colors, they can all be black.
In this sketch, the picking map is displayed on the right-hand side, but normally it is just an invisible off-screen buffer ā it is only shown here to help you understand how the left-hand picker is working.
The mouse position is then checked against the map to identify which curve is under the mouse, and that curve can be styled at render time.
For a full library implementation in Processing (Java) ā not p5.js ā see the Picking library:
Thank you so much for illustrating with a simple and yet very helpful example.
If I may, could I use this logic for data thatās pulled from a JSON file? In other words, say if I would be drawing the bezier curves by first setting up a class in setup for eg,
curve1 = new BzCurve(column1);
curve2 = new BzCurve(column2);
Iām not sure how to replace ācsā in your beautiful example, and customize it.
Also, this one went straight above my head pg.get(mouseX, mouseY)[0]==ccols[i]
I see;
// compare the map color at mouse location
// with the index color for this object
What exactly are we getting here? Whatās the meaning of map color?
I hope you donāt mind my followup Qs, but Iām looking forward to your response
But theoretically the idea is that pg holds the image of the same curves as the screen and the Image has the same size.
pg has the curves in distinct unique colors ccols. These colors identify each curve. Use image(pg,0,0); to see pg.
Now get() gives us the color at mouse position in pg. When we find a color in that position that matches a curve we found that the mouse is over that curve. Thatās one way of picking a shape/curve.
Yes, this is because p5.js colors work slightly differently than Processing(Java). We are using simple grayscale index values rather than full colors:
save a grayscale value, like ā64ā, to ccols.
Use it in makePicker to draw the line, e.g. pg.stroke(64);
When we check with our mouse, we check the pg.get(mouseX, mouseY) pixel. In p5.js, get() āReturns an array of [R,G,B,A] values for any pixelā. So we get back [64, 64, 64, 255].
get()[0] gives usā¦ 64, which matches the index value in ccols.