python_user_defined_function_Ellipse

2019年04月19日

首頁

 

 

首頁

 

User defined Ellipse Function in Python Canvas

使用者自定橢圓函數

                               By daych169

(1)            Introduction:

After using Python canvas to draw widget shapes , I find out

(1)  pure python canvas cannot use’ floodfill ‘ method to fill a irregular region surrounding by primitive shapes.(2)canvas can only save the shapes to printer postscript file, which could not redraw again,(3)The methods such as find_closest ,  find_enclosed , find_withtag, etc. hard to edit or modify a primitive shape in canvas directly. These issues mentioned above can implement easily in vb6. So I plan to solve it follow the concepts similar to the programs I designed before. First, we need to classify the canvas primitive to four basic types (a)line(lines), (b)polygons,(c)texts, (4) images. Here we will focus on line and polygon only. Classify circle arc, open ellipse , polyline, spline and other non_closed shapes as line, and  closed shapes, such as ellipse ,circle ,rectangle ,triangle ,closed polygon ,closed spline as polygon. Because Python Canvas polygon method can use optional attitudes ‘stipple ‘ to fill irregular region with predefined stipple or pattern(xbm or XBM file), to fill colors on any closed region ,using ’activefill’to show a color when mouse move over the shape. Following are the shapes plotted by author’s   progrm(vb 6).

 

學習Python tkinter canvas s畫圖後,發現(1)使用canvas在由兩

個圖元(primitive)以上所組成之不規則區域,無法直接在圖上使用洪溢填充方法(Floodfill,fill)鋪貼預設圖案、自定圖案或顏色,(2) canvas畫完圖後想再行編輯及改變性質(edit primitive & modify properties),所能使用之工具相當有限及困難,(3)canvas只能將圖元儲存成印表機認識之postscript 文字檔,似乎無法直接儲存成canvas相容之圖檔,需要時,能轉譯再繪成為原圖(redraw)。上述問題在vb6中實現並不困難,上圖就是我用vb6所設計之cad程式所繪製之圖案。因為有vb6之設計觀念,在研習多日後,終於研究出解決方法。利用tkinter canvas所有提供之資源,實現解決方案。

Before introduce the user defined ellipse widget. I want to introduce line draw method offer by Python tkinter(Tkinter) briefly. We are going to implement the ellipse curve by canvas. create_line or canvas. create_polygon method. The widget of python canvas supplies graphics facilities for Tkinter(tkinter) .Among these graphical objects are texts, lines, circles,polygons,ovals,arc,rectangle,windows, images, and other widgets. With this widget it's possible to draw graphs and plots, create primitive graphics editors, and implement various kinds of user_defined widgets. 

id = canvas.create_line(x0, y0, x1, y1, ..., xn, yn, option, ...)

The line goes through a series of points (x0,y0), (x1y1), … (xnyn).The method create_line is used to draw a straight line or polyline. The coordinates "coords" are given as four integer numbers: (x0, y0, x1, y1 )for a line ,and (x0,y0,x1,y1,x2,y2,x3,y3,….. ) for a polyline or smooth curve_fitting polyline(set smooth=True) under the same name ‘line’. This means that the line goes from the point (x0, y0) to the point (x1,y1, ) ,and from (x0, y0) to (x1, y1) and then to (x2, y2)…….. for a polyline. After these coordinates follows a comma separated these list of additional optional parameters, which may be empty or ‘None’. For example ,We can draw a line with penwidth=3, color=’red’.This can be implemented something like id=canvas.create_line(50,50,200,200,width=3,fill=’red’,arrow=None,activefill=’green’). Here create_line is the method of drawing by Python Tkiner, (50,50,200,200) is the coords ,width is the keyword for pen size, fill is the keyword of pen color, arrow is the keyword of arrow of line(can be ‘tk.FIRST’,’tk.LAST’,’tk.BOTH’), and actiivefill is the color of line when cursor moving over the line, id is the return handler of line.

The options for polygon are quite similar to line, some useful options,such as ‘stipple’,’outline’,’fill’are different with line. ‘fill’is used to fill the enclosed region,’outline’ is the color of polygon boundary line,’stipple’ is the stipple or predefined pattern to tile a closed region. The following diagrams are plotted by Arthur designed python program. The irregular closed regional area are detected first, and then it can be tiled with predefined stipple pattern or user_ defined pattern. Since the coordinates are found out, so the boundary line length and area can be calculated as well. The algorithm of detective method and xbp file making method and procedures are  another interesting issues, I would like to share friends in the future.

 

  Python tkinter自帶畫圖方法雖然可以繪製橢圓圖形,但只能畫垂直或水平,如要畫傾斜橢圓圖形,必須借用第三方提供之函數,如numpymatplotlib等, 如要編輯或修改圖形相當不方便。本文所要討論的是,如何使用自訂函數繪製編輯橢圓曲線。

(2)Governing equation of Ellipse and Ellipse arc:

The arc or oval methods offered by Python canvas can only draw the arc or oval curves enclosed by a box. If you intend to draw an inclined ellipse curve , You need to import numpy , matplotlib , PIL. ,

wxpython. Hereafetr,  I like to discuss a user defined function to draw ellipse by mouse or given three points to get an ellipse, simple and easily. And you also can draw an ellipse rubber_band ellipse or ellipse arc. The governing equation of ellipse curve can be defined like:

橢圓形曲線之方程式為

(x-xc)**2/a**2 +(y-yc)/b**2=1

or

x=xc+a*cos(theta)  y=yc+b*sin(theta)

通過平面上三個座標點可以定義一個傾斜之橢圓曲線。

Here (xc,yc) is the center of ellipse

a ,b is the semi diameter of long and short axis respectively.

If we give a three points(x0,y0),(x1,y1),(x2,y2) on a ellipse curve then

We can get :

xcen(xc)=(x0+x1)/2,

ycen(yc)=(y0+y1)/2

a= sqrt((x0 - xcen)**2 + (y0 - yeny) **2)

theta =atan2((y1-y0), (x1-x0))

b can be obtained after a, theta be solved.

 

 

 

def ellipse_Prop(x0,y0,x1,y1,x2,y2):

    ptcenx=(x0+x1) / 2.  # center of ellipse

    ptceny=(y0+y1) / 2.

    #print("ptcen.x,ptcen.y",ptcenx,ptceny)

    angslop = math.degrees(math.atan2((y1-y0), (x1-x0)))

    #print( 'angslop=',angslop)

    aa = math.sqrt((x0 - ptcenx)**2 + (y0 - ptceny) **2)

    #print('aa',aa)

    ptanyx = x2

    ptanyy = y2

    #print('ptany=',ptany.x,ptany.y)

    ta1 = ptanyx - ptcenx

    ta2 = aa * math.cos(math.radians(angslop))

    ta3 = -math.sin(math.radians(angslop))

    #print('ta1,ta2,ta3',ta1,ta2,ta3)

    tb1 = ptanyy - ptceny

    tb2 = aa * math.sin(math.radians(angslop))

    tb3 = math.cos(math.radians(angslop))

    #print('tb1,tb2,tb3',tb1,tb2,tb3)

    t1 = (ta1 * tb3 - tb1 * ta3) / (ta2 * tb3 - ta3 * tb2)

    t2 = (ta2 * tb1 - tb2 * ta1) / (ta2 * tb3 - ta3 * tb2)

    #print("t1,t2",t1,t2)

    #print(1-t1**2)

    bb=0

    if (1.0-t1**2) >= 0.00001 :

        bb = math.sqrt(t2**2 / (1.0 - t1 ** 2))

        #print("bb",bb)

    else:

        print("error in function ellprop")

    #print('aa,bb',aa,bb)

   

    #xys=[ptcenx,ptceny,aa,bb,angslop]

    #return xys #ptcenx,ptceny,aa,bb,angslop

    return ptcenx,ptceny,aa,bb,angslop

   

#-----------------------------------------------------------------

def ellipsemaker_XY(x0,y0,x1,y1,x2,y2,angbeg,angend,npt,cenReturn=0):

# angbeg,angend are angles of ellipse beg. & end of curve

# npt are no. of point on ellipse

# cenReturn=1, return (ptcenx,ptceny)

    #print('x0,y0,x1,y1,x2,y2',x0,y0,x1,y1,x2,y2)

    ptcenx=(x0+x1) / 2.

    ptceny=(y0+y1) / 2.

    #print("ptcen.x,ptcen.y",ptcen.x,ptcen.y)

    angslop = math.degrees(math.atan2((y1-y0), (x1-x0)))

    #print( 'angslop=',angslop)

    aa = math.sqrt((x0 - ptcenx)**2 + (y0 - ptceny) **2)

    #print('aa',aa)

    ptanyx = x2

    ptanyy = y2

    #print('ptany=',ptany.x,ptany.y)

    ta1 = ptanyx - ptcenx

    ta2 = aa * math.cos(math.radians(angslop))

    ta3 = -math.sin(math.radians(angslop))

    tb1 = ptanyy - ptceny

    tb2 = aa * math.sin(math.radians(angslop))

    tb3 = math.cos(math.radians(angslop))

    t1 = (ta1 * tb3 - tb1 * ta3) / (ta2 * tb3 - ta3 * tb2)

    t2 = (ta2 * tb1 - tb2 * ta1) / (ta2 * tb3 - ta3 * tb2)

      bb=aa

    try:

        if (1.0-t1**2) >= 0.0000001 :

            bb = math.sqrt(t2**2 / (1.0 - t1 ** 2))

        else:

            #print("error in function ellprop")

            pass

    except:

        pass

    ndo=npt

    ang0=angbeg

    ang1=angend

   if ang1 == 0.0 :

        ang1= 359.999999

    if ang1 < ang0 :

        ang1 =ang1+360.0

    angbeg=ang0

    angend=ang1

    delang = (angend-angbeg) / npt

   xout=[]

    yout=[]

    pts=[]

    

    for i in range(0,ndo):

        angi = angbeg + delang * i

        tpt1=aa* math.cos(math.radians(angi))* math.cos(math.radians(angslop))

        tpt2=- bb * math.sin(math.radians(angi)) * math.sin(math.radians(angslop))   # 'xcoord(x座標)

        ptsix=tpt1+tpt2

        tpt3=aa* math.cos(math.radians(angi))* math.sin(math.radians(angslop))

        tpt4= bb * math.sin(math.radians(angi)) * math.cos(math.radians(angslop))   # 'ycoord(y座標)

        ptsiy=tpt3 +tpt4

        ptsix=ptcenx+ptsix

        ptsiy=ptceny+ptsiy

        xout.append(ptsix)

        yout.append(ptsiy)

        pts += (ptsix,ptsiy)

        #print("ptellp",i,ptellp[i].x,ptellp[i].y)

    if cenReturn==0:

        if abs(360-abs(angend-angbeg))<=0.01 :

            xout.append(xout[0])

            yout.append(yout[0])

            pts +=(xout[0],yout[0])

            return npt+1,xout,yout,pts

        else:

            return npt,xout,yout,pts

    else:

        if abs(360-abs(angend-angbeg))<=0.01 :

            xout.append(xout[0])

            yout.append(yout[0])

            pts +=(xout[0],yout[0])

            return npt+1,xout,yout,pts,ptcenx,ptceny

        else:

            return npt,xout,yout,pts,ptcenx,ptceny

#----------------------------------------------------

The ellipse drawing by python canvas like:

Canvas.create_oval(xmin,ymin,xmax,ymax)

is same as

x0,y0=xmin,(ymin+ymax)/2

x1,y1=xmax,(ymin+ymax)/2

x2,y2=(xmin+xmax)/2,ymax

xout,yout,pts=[],[],[]

ndo,xout,yout,pts=ellipsemaker_XY(x0,y0,x1,y1,x2,y2,0,360,36,0)

canvas.create_polygon(pts,width=2,fill='red')

Function ellipsemaker_XY() can draw inclined ellipse also.

利用python canvas_create_oval(xmin,ymin,xmax,ymax)和自訂函數

ndo,xout,yout,pts=ellipsemaker_XY(x0,y0,x1,y1,x2,y2,0,360,36,0)

者相當,此處

xout,yout,pts=[],[],[]

x0,y0=xmin,(ymin+ymax)/2

x1,y1=xmax,(ymin+ymax)/2

x2,y2=(xmin+xmax)/2,ymax

ellipsemaker_XY()可以繪製傾斜橢圓及橢圓弧。

---------------------------------------

 (3)Edit of ellipse :

The edit of ellipse curve such as rotation, reflection, offset , tangential line, copy(ring copy), segmental slice of ellipse by means of python tkinter are difficult and complex ,but using user define function  are quite simple and easy. To fill or flood fill an irregular region on PictureBox of vb 6 or vb net are simple and fast. But in Python canvas shapes are difficult and tedious even on imageI try to build a region passing through the boundary key points of the interesting zone (surrounded by ellipse, or circle and lines) ,then fill a predefined pattern or self defined pattern ) by python polygon “stipple” and “fill” method.(stipple=’hourglass’,stipple=’gray50,stipple=’@penginns.xbm’(user_definned xbm file))To implement this method, You need to find out the segments of the connecting points, firstly. Here, I pose some useful source code to get the ellipse segment to share with people who is interesting.

Vb 6net可以利用API函數直接在繪圖方塊(PictureBox)上不規則之封閉區域Floodfill及鋪貼圖案,但Python canvas似乎並不直接支援此方法?好像只能在image上操作,因此本人嘗試計算不規則區域之邊界交點及曲線線段,然後與其他線段。理論上應該可行?

 

def  EllipseReflct(x0,y0,x1,y1,x2,y2,angbeg,angend,npt,xbase0,ybase0

,xbase1,ybase1):

    xout=[]

    yout=[]

    pts=[]

    npt,xout,yout,pts,cenx,ceny=ellipsemaker_XY(x0,y0,x1,y1,x2,y2,angbeg,angend,npt,cenReturn=1)

    xreflcts=[]

    yreflcts=[]

   

    for i in range(npt):

        bol,xvert,yvert,xreftpt,yreftpt=NearPt_ReflctPt(xout[i],yout[i],xbase0,ybase0,xbase1,ybase1)

        xreflcts.append(xreftpt)

        yreflcts.append(yreftpt)

   

    return npt,xreflcts,yreflcts

def EllipseRot(x0,y0,x1,y1,x2,y2,angbeg,angend,npt,xRotAt,yRotAt

,angRotIn):

    xout=[]

    yout=[]

    pts=[]

    npt,xout,yout,pts,cenx,ceny=ellipsemaker_XY(x0,y0,x1,y1,x2,y2,angbeg,angend,npt,cenReturn=1)

    xrots=[]

    yrots=[]

    pts=[]

    for i in range(npt):

        xrtpt,yrtpt=RotatePoint(xout[i],yout[i],xRotAt,yRotAt,angRotIn)

        xrots.append(xrtpt)

        yrots.append(yrtpt)

    xcenrot,ycenrot=RotatePoint(cenx,ceny,xRotAt,yRotAt,angRotIn)

 

    return npt,xrots,yrots,xcenrot,ycenrot

 

 

  

def Offset_In_Out(x0,y0,x1,y1,x2,y2,angbeg,angend,npt,Offset,

isInsideOff=True ):

    #print('in offset')

    #x0=self.x0

    #y0=self.y0

    #x1=self.x1

    #y1=self.y1

    xcen,ycen,aa,bb,angslop=ellipse_Prop(x0,y0,x1,y1,x2,y2)

    print('aa,bb',aa,bb)

    xvert,yvert=ellPoint_byGivenAngle(x0,y0,x1,y1,x2,y2,90)

    print('xvert,yvert',xvert,yvert)

    ll=math.sqrt((xvert-xcen)**2+(yvert-ycen)**2)

    raa=aa

    rbb=bb

    if isInsideOff==True :

        ratpt=(aa-Offset)

        rbtpt=(bb-Offset)

        x0new=xcen+(x0-xcen)*ratpt/raa

        y0new=ycen+(y0-ycen)*ratpt/raa

        x1new=xcen+(x1-xcen)*ratpt/raa

        y1new=ycen+(y1-ycen)*ratpt/raa

        x2new=xcen+(xvert-xcen) *rbtpt/rbb

        y2new=ycen+(yvert-ycen) *rbtpt/rbb

    else:

        ratpt=(aa+Offset)

        rbtpt=(bb+Offset)

        x0new=xcen+(x0-xcen)*ratpt/raa

        y0new=ycen+(y0-ycen)*ratpt/raa

        x1new=xcen+(x1-xcen)*ratpt/raa

        y1new=ycen+(y1-ycen)*ratpt/raa

x2new=xcen+(xvert-xcen) *rbtpt/rbb

        y2new=ycen+(yvert-ycen) *rbtpt/rbb

    xoffsets=[]

    yoffsets=[]

    pts=[]

    npt,xoffsets,yoffsets,pts=ellipsemaker_XY(x0new,y0new,x1new,y1new,x2new,y2new,angbeg,angend,npt,0)

    #xoffset,yoffset=Ellip.Ellipse()

    #print('xoffsets',xoffsets)

    #print('yffsets',yoffsets)

    return npt,xoffsets,yoffsets

def Ellipse2Tans(x0,y0,x1,y1,x2,y2,PtGivenX,PtGivenY):

    EllpCenX,EllpCenY,AA,BB,angslop=ellipse_Prop(x0,y0,x1,y1,x2,y2)

    ptAns1X = 99999.0

    ptAns1Y = 99999.0

    ptAns2X = 99999.0

    ptAns2Y = 99999.0

    Angslop=math.radians(angslop)

    Ta = AA ** 2 * math.sin(Angslop ) **2 + BB ** 2 * math.cos(Angslop ) ** 2

    Tb = AA ** 2 * math.cos(Angslop ) ** 2 + BB ** 2 * math.sin(Angslop) ** 2

    Tc = 2 * math.sin(Angslop ) * math.cos(Angslop) * (BB** 2 - AA ** 2)

    Td = AA **2 * BB **2

    Te = EllpCenX - PtGivenX

    Tf = EllpCenY - PtGivenY

    T1 = 2.0 * Ta * Te + Tc * Tf

    T2 = Tc * Te + 2.0 * Tb * Tf

 

    T3 = -(Tb * T1 - Tc * T2) / Ta / T1

    T4 = 2 * Tc * Td / Ta / T1

    T5 = Td / Ta

    T6 = -T2 / T1

    T7 = -2.0 * Td / T1

    Fa = T6 ** 2 - T3

    Fb = 2 * T6 * T7 - T4

    Fc = T7 ** 2 - T5

    tpt = Fb ** 2 - 4.0 * Fa * Fc

    print('tpt=',tpt)

    if tpt <= 0.0 :

        print('no solution for Ellipse Tangent line')

       

        return 'no solution'

    else:

        ptAns1Y = (-Fb - math.sqrt(tpt)) / 2.0 / Fa

        ptAns2Y = (-Fb + math.sqrt(tpt)) / 2.0 / Fa

        ptAns1X = T6 * ptAns1Y + T7

        ptAns2X = T6 * ptAns2Y + T7

        ptAns1X = ptAns1X + EllpCenX  #   平移

        ptAns1Y = ptAns1Y + EllpCenY  #  平移

        ptAns2X = ptAns2X + EllpCenX

        ptAns2Y = ptAns2Y + EllpCenY

        pttans0X = ptAns1X

def ellSlicesbyMile(xxin,yyin,x0,y0,x1,y1,x2,y2,angbeg,angend,npt):

    xout=[]

    yout=[]

    pts=[]

    npt,xout,yout,pts=ellipsemaker_XY(x0,y0,x1,y1,x2,y2,angbeg,angend,npt)

    miles=[]

    xcen,ycen,miles=Milelinexy_cen(xout,yout,1)

    print('in ellSlicesbyMile ^^miles',miles)

    xxonEll=[]

    yyonEll=[]

    xxonEll.append(xout[0]) # add the first point of ellipse

    yyonEll.append(yout[0])

    for i in range(len(xxin)):

        nint,ptonEllipx,ptonEllipy,tAng=point_onEllipse_Angle(xxin[i],yyin[i],x0,y0,x1,y1,x2,y2,angbeg,angend,npt)

        if nint==1:

            xxonEll.append(ptonEllipx)

            yyonEll.append(ptonEllipy)

    xxonEll.append(xout[-1])# add the last point of ellipse

    yyonEll.append(yout[-1])

    milesAns=[]

    milesAns.append(miles[0]) # add the first mile of ellipse

    for i in range(len(xxonEll)):

        yesorno,miletpt=MilesEllipse_givenXy(xxonEll[i],yyonEll[i],xout,yout)

        if yesorno==1:

            milesAns.append(miletpt)

    milesAns.append(miles[-1]) # add the last mile of ellipse

    print('in ellSlicesbyMile milesAns',milesAns)

    xysegAll=[]

    nbeg=0

    for i in range(len(xxonEll)-1):

        xyseg=[]

        xyseg.append([xxonEll[i],yyonEll[i]])  #first point of segment

        for j in range(len(xout)-1):

            if miles[j]>=milesAns[i] and miles[j]>=milesAns[i+1]:

                xyseg.append([xout[j],yout[j]])

        xyseg.append([xxonEll[i+1],yyonEll[i+1]]) #last point of segment

           

        xysegAll.append(xyseg)

    return xxonEll,yyonEll,xysegAll

   

        pttans0Y = ptAns1Y

        pttans1X = ptAns2X

        pttans1Y = ptAns2Y

        Ellipse2Tans = 1

        Ltans0 = math.sqrt((pttans0X - PtGivenX) ** 2 + (pttans0Y - PtGivenY) ** 2)

        Ltans1 = math.sqrt((pttans1X - PtGivenX) ** 2 + (pttans1Y - PtGivenY) ** 2)

        return  pttans0X,pttans0Y,pttans1X,pttans1Y,Ltans0,Ltans1

                       <to be continued.>

 

 

 

 

 

 

 

Name(您的大名)
E_MAIL(您的電子信箱)
Comment or Suggestion(您想反應的狀況,建議,或諮詢事項)
首頁


 

 

 

 

首頁 | python_user_defined_stipple_pattern | python_user_defined_ellipse_calss(2) | python_point_line_calss | python_user_defined_splines | python_user_defined_function_Ellipse | lon_lattoxy

上次修改此網站的日期: 2019年03月23日