2

This class plots a curve. However, the inputs are currently set in main(). I'd like to set them as user-driven from mouse interaction. Some of this is possible and in the Matplotlib docs (see referenced sites below) but it's still not really setting it up to be a 'click and plot'. So, ideally the user would click a button to set the P and then whatever point (on the curve, has to be on the curve) they clicked next would be the new P. Same with Q. I'm sure this is a very simple question for anyone who's used Matplotlib but I'm teaching myself it right now, but it would probably take an entry-level dev just a few minutes to do something that I'm getting nowhere with.

Code:

import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.axes_grid.axislines import SubplotZero
from math import sqrt



class ECC123(object):

    def __init__(self,a,b,px,qx,qy):
        self.a = a
        self.b = b
        self.pxlam = px
        self.qxlam = qx
        self.invertQy = qy
        self.fig = plt.figure(1)
        self.ax = SubplotZero(self.fig, 111)

    def drawAxis(self):
        #fig = plt.figure(1)
        #ax = SubplotZero(fig, 111)
        self.fig.add_subplot(self.ax)
        for direction in ["xzero", "yzero"]:
            self.ax.axis[direction].set_axisline_style("->")
            self.ax.axis[direction].set_visible(True)

    def plotGraph(self):
        self.drawAxis()
        y, x = np.ogrid[-10:10:100j, -10:10:100j]  # range grid  [from : to : how_many_points]
        xlist = x.ravel(); ylist = y.ravel()
        plt.contour(xlist, ylist, self.elliptic_curve(x,y), [0])
        pylam = self.ecclambda(self.pxlam,self.a,self.b)  # calculate P from pxlam
        qylam = self.ecclambda(self.qxlam,self.a,self.b)  # calculate Q from qxlam
        if self.invertQy == 1:  qylam = -qylam # optional, inverts qy to negative on the plot
        plt.plot([self.pxlam,self.qxlam], [pylam,qylam], color = "c", linewidth=1)
        plt.plot([self.pxlam], [pylam], "mo"); plt.plot([self.qxlam], [qylam], "mo")
        plt.text(self.pxlam-0.25,pylam+0.5, '$P$'); plt.text(self.qxlam-0.25,self.qxlam+0.5, '$Q$')
        s = (pylam - qylam)/(self.pxlam - self.qxlam)  # calculate s slope
        xr = s**2 - self.pxlam - self.qxlam  # x-value of R
        yr = pylam + s*(xr - self.pxlam)  # y-value of -R; -y is R (inverted across x-axis)
        plt.plot([xr],[yr],"mo")
        plt.plot([xr],[-yr],"co")
        plt.plot([self.qxlam,xr], [qylam,yr], color = "c", linewidth=1)
        plt.plot([xr,xr], [yr,-yr], "x--")
        plt.text(xr+0.25,yr, '$-R$'); plt.text(xr+0.25,-yr, '$R$')

        plt.grid(True)
        plt.show()

I've been going over the docs in Matplotlib, the scipy cookbook, and related questions here on SO and still not seeing exactly how to do this:

http://matplotlib.org/users/event_handling.html

http://matplotlib.org/1.3.1/api/widgets_api.html#matplotlib.widgets.Button.on_clicked

Cursors for data selection in matplotlib

How can I create a frontend for matplotlib?

http://wiki.scipy.org/Cookbook/Matplotlib

So far, I'm getting little red x's all over when I click and they don't even fall within the curve.

1
  • I suggest removing all the domain specific stuff. I'm sure the elliptical filters are interesting, but they distract from the question being asked here. Try to get a minimal example. FWIW I'm interested in the answer :) Commented Nov 19, 2013 at 23:29

1 Answer 1

3

I modified your code a little, so that you can set location of P & Q by left & right click, I didn't accomplish all the graph data updates, the rest is left for you:

from mpl_toolkits.axes_grid.axislines import SubplotZero
import numpy as np
import matplotlib.pyplot as plt
from math import sqrt



class ECC(object):
    """
    class to implement elliptic curve and find P+Q=R on the plot
    """

    def __init__(self,a,b,px,qx,qy):
        """
        initialize input variables
        """
        self.a = a
        self.b = b
        self.pxlam = px
        self.qxlam = qx
        self.invertQy = qy
        self.fig = plt.figure(1)
        self.ax = SubplotZero(self.fig, 111)

    def drawAxis(self):
        """
        draw main x,y axis
        """
        #fig = plt.figure(1)
        #ax = SubplotZero(fig, 111)
        self.fig.add_subplot(self.ax)
        for direction in ["xzero", "yzero"]:
            self.ax.axis[direction].set_axisline_style("->")
            self.ax.axis[direction].set_visible(True)

    def ecclambda(self,xl,a,b):
        """
        returns points elliptic curve for P and Q
        y**2 = x**3 + a*x + b
        """
        return sqrt(xl**3 + a*xl + b)

    def elliptic_curve(self,x,y):
        """
        takes in x,y as set of points, returns the elliptic curve
        y**2 = x**3 + a*x + b
        """
        return pow(y, 2) - pow(x, 3) - x * self.a - self.b

    def onclick(self, event):
        x = event.xdata
        if event.button == 1:
            self.pxlam = x
        if event.button == 3:
            self.qxlam = x

        self.update()

    def update(self):
        pylam = self.ecclambda(self.pxlam,self.a,self.b)  # calculate P from pxlam
        qylam = self.ecclambda(self.qxlam,self.a,self.b)  # calculate Q from qxlam
        self.p.set_data([self.pxlam], [pylam])
        self.q.set_data([self.qxlam], [qylam])
        self.pt.set_x(self.pxlam-0.25)
        self.pt.set_y(pylam+0.5)
        self.qt.set_x(self.qxlam-0.25)
        self.qt.set_y(qylam+0.5)
        plt.gcf().canvas.draw()

    def plotGraph(self):
        """
        main plotting of elliptic curve and points/line for P+Q=R
        P+Q=R --->>>  -R is plotted (xr,yr), R is plotted (xr, -yr)
        conditional with invertQy allows inversion of Q across x-axis; set option in main()
        """
        self.drawAxis()
        y, x = np.ogrid[-10:10:100j, -10:10:100j]  # range grid  [from : to : how_many_points]
        xlist = x.ravel(); ylist = y.ravel()
        plt.contour(xlist, ylist, self.elliptic_curve(x,y), [0])
        pylam = self.ecclambda(self.pxlam,self.a,self.b)  # calculate P from pxlam
        qylam = self.ecclambda(self.qxlam,self.a,self.b)  # calculate Q from qxlam
        if self.invertQy == 1:  qylam = -qylam # optional, inverts qy to negative on the plot
        plt.plot([self.pxlam,self.qxlam], [pylam,qylam], color = "c", linewidth=1)
        self.p = plt.plot([self.pxlam], [pylam], "mo")[0]
        self.q = plt.plot([self.qxlam], [qylam], "mo")[0]
        self.pt = plt.text(self.pxlam-0.25,pylam+0.5, '$P$')
        self.qt = plt.text(self.qxlam-0.25,self.qxlam+0.5, '$Q$')
        s = (pylam - qylam)/(self.pxlam - self.qxlam)  # calculate s slope
        xr = s**2 - self.pxlam - self.qxlam  # x-value of R
        yr = pylam + s*(xr - self.pxlam)  # y-value of -R; -y is R (inverted across x-axis)
        plt.plot([xr],[yr],"mo")
        plt.plot([xr],[-yr],"co")
        plt.plot([self.qxlam,xr], [qylam,yr], color = "c", linewidth=1)
        plt.plot([xr,xr], [yr,-yr], "x--")
        plt.text(xr+0.25,yr, '$-R$'); plt.text(xr+0.25,-yr, '$R$')
        plt.text(-9,6,' P: (%s ,%s) \n Q: (%s ,%s) \n R: (%s ,%s) \n a: %s \n b: %s '
                %(self.pxlam,pylam,self.qxlam,qylam,xr,-yr,self.a,self.b),
                fontsize=10, color = 'blue',bbox=dict(facecolor='tan', alpha=0.5))
        plt.title(r"Elliptic Curve Implementation $y^{2} = x^{3} + a*x + b$", fontsize = 16, color = 'b')
        self.fig.canvas.mpl_connect('button_press_event', self.onclick)
        #[xi,yi] = plt.ginput(0)
        ##print "ginput ",xi,yi
        plt.grid(True)
        plt.show()




def main():

    a = -2; b = 1; px = -1.55; qx = -0.1
    invertQy = 0 # set to 1 if q should be inverted to negative along its y axis
    ec = ECC(a,b,px,qx,invertQy)
    ec.plotGraph()


if __name__ == '__main__':
    main()
Sign up to request clarification or add additional context in comments.

1 Comment

Wonderful! I was just not seeing exactly where everything would go inside the methods. Thanks +1 and accepted.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.