Selecting points on an Ellipse

Similar to the code in the attached notebook and also in the picture I want to be able to select points on a defined ellipse. However, I also ONLY want them to be allowed on the ellipse. Is there an easy way to do this that doesn’t involve using equations to verify the point location? How would this be done in an interactive way?

One idea could be to react to points changing, calculate the nearest point on the ellipse and move the point to there?

I don’t know if it will work. But its an idea.

But it would involve equations.

Hi Marc,
Thanks for your response and that was my initial inclination, I just wondered if there was a simpler approach using the existing ellipse but it sounds like perhaps there is not. I also was not entirely clear how to capture and recalculate that point.
Cheers

@Marc I presume creating a parameter class is the starting point as below:

import panel as pn
import param
import numpy as np
import holoviews as hv
from holoviews import opts

hv.extension("bokeh")


class CirclePoints(param.Parameterized):

    circle_points = param.DataFrame(
        pd.DataFrame(
            {"x": [3, 2.6, -3.85, 0], "y": [2.6, -3, 1, 4], "z": [0.2, 0.4, 0.6, 0.8]}
        )
    )
    radius = param.Number(4.0)
    circle_centre_x = param.Number(0.0)
    circle_centre_y = param.Number(0.0)
    num_points = param.Integer(4, bounds=(0, 20))

    @param.depends("circle_points", "radius", "circle_centre_x", "circle_centre_y")
    def view(self):
        buffer = 0.2
        hv_points = hv.Points(self.circle_points, vdims="z").redim.range(
            x=(
                min(np.min(self.circle_points.x),-self.radius) - buffer,
                max(np.max(self.circle_points.x),self.radius) + buffer,
            ),
            y=(
                min(np.min(self.circle_points.y),-self.radius) - buffer,
                max(np.max(self.circle_points.y),self.radius) + buffer,
            ),
        )
        point_stream = streams.PointDraw(
            data=hv_points.columns(),
            num_objects=self.num_points,
            source=hv_points,
            empty_value=0,
        )
        table = hv.Table(hv_points, ["x", "y"], "z")
        DataLink(hv_points, table)

        circle_and_points = (
            hv_points
            * hv.Ellipse(self.circle_centre_x, self.circle_centre_y, 2 * self.radius)
            + table
        ).opts(
            opts.Layout(merge_tools=False),
            opts.Points(
                active_tools=["point_draw"],
                color="z",
                height=400,
                size=10,
                tools=["hover"],
                width=400,
                cmap="RdYlGn",
            ),
            opts.Table(editable=True),
        )

        return circle_and_points


circle_points = CirclePoints(name="Test")
circle_points.view()