Hide all bezier curves except the one on mouse hover

Thanks for the post!

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.

What would be the purpose to hide all but one curve?? User would have to move the mouse randomly to find other curves!

Consider all curves are pale and one curve is highlighted?

Also, you didn’t understand mcve.
Your mcve should be runnable so people can improve it instead of giving advice without running code…

1 Like

Thanks for the message, Chrisir!

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);
  }
}
1 Like

We talk about an animation here because view changes depending on mouse position: which curve is highlighted.

Hence you should let your mcve have setup and draw()/loop

Yup, setup and draw now included.

1 Like

Here I worked with beziers

I hope this helps.

2 Likes

If you have four curves and they aren’t updating every frame, then the picking algorithm is probably faster and more flexible.

  1. create an index of colors, 1 for each stroke.
  2. create a Graphics
  3. 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.
  4. 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.

2 Likes

Thanks for the your response, Jeremy!

I have SO much to learn. Could you help with resources on where to start?

  • Kindly advice what is a picking algorithm?
  1. By index of colors, did you mean an array (just being sure here)?
  2. Also, to clarify, I should try drawing all curve to the one Graphics created right?
  3. 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.

Thanks a ton!

1 Like

Here is a simple demonstration. Hopefully it answers your questions.

https://editor.p5js.org/jeremydouglass/sketches/83XyPImMP

Notice that there are two kinds of drawing from the curve data:

  1. drawing the map, which is done in unique colors that are added to an index.
  2. 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:

http://n.clavaud.free.fr/processing/library/picking/

This enables you to generate your map as a byproduct of your drawing code, rather than the manual “drawing twice” approach.

4 Likes

Hi Jeremy!

Thank you so much for illustrating with a simple and yet very helpful example. :raised_hands:

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);

where the JSON file would be =

[{ 
"column1": 50,
"column2": 75,
},
{
"column1": 75,
"column2": 95,
}
{
"column1": 10,
"column2": 120,
}
]

I’m not sure how to replace ‘cs’ in your beautiful example, and customize it.
Also, this one went straight above my head :unamused:
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 :slight_smile:

Once again, thank you!

I would expect

pg.get(mouseX, mouseY)==ccols[i]

here.

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.

It’s a fantastic solution.

Shouldn’t you need 4 Positions (each with x and y and z) for each curve??

It 2 anchor points and 2 control points and each has x,y,z

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[].
2 Likes

Thanks for the explanation, Jeremy!

Thanks for clarifying, Jeremy! Understood how get() works :slight_smile:

The logic left to figure out is building an object like cs in your example where if I have two arrays like;

a = [ 1,2,3,4,5]
b = [1,2,3,4,5]

How do I create an object like this;

c =
0: Array [5]
0: 1
0: 2
0: 3
0: 4
0: 5

1: Array [5]
1: 1
1: 2
1: 3
1: 4
1: 5

Any clues anyone?

What are a and b in your example?

Thanks for asking,
They are two arrays I had to create from the JSON data where the values from the respective column (example above) had to be mapped along y axis to the height of the canvas.
Hope that made sense? Happy to elaborate.

When you look at lines 33,34,35 in Jeremys code you see he is filling the array cs with 8 values. Here you want to use your 2 arrays as source for the values

1 Like

Right. You can cs.push(anything). Your anything could be:

  • a line, [x1,y2,x2,y2]
  • a centered circle, represented only by its radius [100]
  • a bezier curve, [0,1,2,3,4,5,6,7]
  • your json data [[0,1,2,3,4],[5,6,7,8,9]]
  • an class object, like a paper doll
  • …whatever

The only thing that matters is that the data can be used

  1. by makePicker to mark the thing on the map
  2. by draw to render the thing on the canvas

…but it can be anything. No matter what it is, any ccols[3] colored pixels on the pg map refer to the cs[3] object. When you are drawing cs[3] – whatever it contains – you check the mouse location on the map (pg) for color ccols[3]. If that appears under the mouse, bam. Your mouse is over cs[3].

2 Likes

Hi Chrisir, the 8 values are arguments for each curve, while there are 6 curves in total.
Since I have 2 Arrays a & b with values already in them, I’m just pushing it twice.
c.push(a);
c.push(b);
I was wondering whether there was a loop that I could code but I don’t think so.

That makes total sense. Thank you for clarifying! Appreciated Jeremy.
Been testing and trying it out, the logic is pretty sharp, nice one.
Will ping back when I run into other Qs :stuck_out_tongue:
Cheers!

1 Like