Forum Archive

I need help Using ui.Path.add_arc 👀

Phuket2

I am trying to draw a arc using add_arc to a ui.Path (). With the sample below I can draw a wedge, but I only want the arc drawn. I have tried many ways, I can't figure it out 😱
I would also like to be able to draw the arc in degrees instead of rads, some how I can't also get that working properly, I have tried math.degrees but that seems not to work for me.
Hi-fully its something simple I am doing wrong

Any help appreciated

def draw_arc(rect):
    r = ui.Rect(*rect)
    s = ui.Path()
    s.move_to(r.center()[0], r.center()[1])
    s.add_arc(r.center()[0], r.center()[1], r.width / 2,math.radians(0), math.radians(45))

    s.eo_fill_rule = False
    s.close()
    ui.set_color('black')
    s.line_width = .3
    s.stroke()

Edit

JonB

You need to move_to the initial point of the arc, not the center. That would be r*sin(start), r*cos(end)

also, skip the close().

Phuket2

@JonB , thanks. I did try moving the start point, but not as you describe, now you mention not closing the path , that sort of makes sense, but not so intuitive. At least for me.
I guess people are educated up the wazoo these days. But It does seem to me that a thin API layer on top of the geometric functions could help us less educated 😁
But really thanks again.

cvp

To @JonB
I think that for any point on the circle, x=r * cos(angle) and y=r * sin(angle) must refer to the same angle, this not cos(start) and sin(end) like you wrote.

Phuket2

Guys, I am embarrassed to say, but I really can't work it out. I am so confused now, I just had to stop and ask. I thought I could figure it out after the advice. But my head is about to implode in on itself.

This is the latest crap, I have done. But I am so confused now about degrees, radiants, angles.....basically I am screwed.

If you could help me to get this function to work as expected, I would really appreciate it. Believe me, I have tried many things my self.
Thanks in advance 😳

def draw_arc(rect):
    r = ui.Rect(*rect)
    s = ui.Path()

    radius = r.width / 2

    start = 0
    finish = 90
    start = math.radians(start)
    finish = math.radians(finish)

    x = radius * math.cos(start) 
    y = radius * math.sin(finish)

    s.move_to(x, y)
    s.add_arc(r.center()[0], r.center()[1], radius, start, finish)

    s.eo_fill_rule = False
    #s.close()
    ui.set_color('black')
    s.line_width = 1
    s.stroke()
    #s.fill()
cvp

Almost correct, believe me, use the same angle for x,y:

x = r.center()[0]+radius * math.cos(start) 
y = r.center()[1]+radius * math.sin(start)

Try it and say if it's what you expect.

Phuket2

@cvp , thanks for your help. It's not quite. The pic below. The only thing I changed was To add your 2 lines and comment out the 2 other lines that were setting x and y.
In the pic can see I am a vertical line, which I don't want. I just want the arc

cvp

Really, I don't know anything about ui.path but I really knows geometry...
Try without the stroke. What do you want to draw with this line of code?

cvp

My two lines only compute the coordinates of the start point of the arc.
Then you move to it.
Then you draw the arc.
Then you "stroke" but I don't know this instruction.

cvp

The code hereafter only draws an arc

import ui
import math

with ui.ImageContext(100, 100) as ctx:
    r = ui.Rect(0,0,100,100)
    s = ui.Path()
    radius = r.width / 2
    start = 0
    finish = 90
    start = math.radians(start)
    finish = math.radians(finish)
    x = r.center()[0]+radius * math.cos(start) 
    y = r.center()[1]+radius * math.sin(start)
    s.move_to(x, y)
    s.add_arc(r.center()[0], r.center()[1], radius, start, finish)
    s.eo_fill_rule = False
    #s.close()
    ui.set_color('black')
    s.line_width = 1
    s.stroke()
    #s.fill()
    img = ctx.get_image()
    img.show()
Phuket2

@cvp , ok thanks. I am just trying to draw an arc around a circle as so many apps do in their interfaces. Also to animate that later. Once I can make the basic arc, I can do a lot from there.

If you are interested the below is a test bed to test the arc in. I probably should have done this first.

import ui, editor
import math

def draw_arc(rect):
    r = ui.Rect(*rect)
    s = ui.Path()

    radius = r.width / 2

    start = 0
    finish = 90
    start = math.radians(start)
    finish = math.radians(finish)

    #x = radius * math.cos(start) 
    #y = radius * math.sin(finish)
    x = r.center()[0]+radius * math.cos(start) 
    y = r.center()[1]+radius

    s.move_to(x, y)
    s.add_arc(r.center()[0], r.center()[1], radius, start, finish)

    s.eo_fill_rule = False
    #s.close()
    ui.set_color('black')
    s.line_width = 1
    s.stroke()
    #s.fill()

class MyClass(ui.View):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def draw(self):

        r = ui.Rect(10, 10, 200, 200)
        s = ui.Path.oval(*r)
        ui.set_color('deeppink')
        s.fill()
        draw_arc(r.inset(10, 10))

if __name__ == '__main__':
    w, h = 600, 800
    f = (0, 0, w, h)
    mc = MyClass(frame=f, bg_color='white')
    mc.present('sheet', animated=False)             
cvp

Your y = line is truncated, you forgot +radius * math.sin(start)

y = r.center()[1]+radius * math.sin(start)
Phuket2

@cvp , yes thanks I just seen that. It works perfectly now. Sorry about that. I have just tried so many things, I almost get vertigo when looking at that small function.

Lol, now, I have to modify it to work with params start and finish using degrees. I will be able to get that now, I hope 😬
But again thanks for your help. I will post something for the degrees once I work it out in case someone else reading has a hard time like me

cvp

Good luck

Phuket2

@cvp , thanks. I am sure you were smiling. For a few minutes I did go off on a stupid hunt then realised I just had to retard the start and finish by -90 to have degrees. I have just did it with a extra param as an offset. I guess not so clear. I think I will change that. But below, is the type of effect I was going after. A very std thing in apps. I have not put numbers etc there yet. But I have that already.
I realise this would normally run off a thread or of ui.delay etc... This was just a test

import ui, editor
import math, time

def draw_arc(rect, start, finish, offset = -90):
    r = ui.Rect(*rect)
    s = ui.Path()

    radius = r.width / 2

    start = math.radians(start + offset)
    finish = math.radians(finish + offset )

    x = r.center()[0]+radius * math.cos(start) 
    y = r.center()[1]+radius * math.sin(start)

    s.move_to(x, y)
    s.add_arc(r.center()[0], r.center()[1], radius, start, finish)

    ui.set_color('white')
    s.line_width = 15
    s.line_cap_style = ui.LINE_CAP_ROUND
    s.stroke()

class MyClass(ui.View):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.deg = 0

    def draw(self):
        r = ui.Rect(10, 10, 200, 200)
        s = ui.Path.oval(*r)
        ui.set_color('deeppink')
        s.fill()
        draw_arc(r.inset(20, 20), 0 , self.deg)

if __name__ == '__main__':
    w, h = 600, 800
    f = (0, 0, w, h)
    mc = MyClass(frame=f, bg_color='white')
    mc.present('sheet', animated=False)
    for i in range(0, 360):
        mc.deg = i
        mc.set_needs_display()
        time.sleep(.015)            
cvp

Sincerely, I didn't smile. I know by experience that sometimes I can search during hours for an obvious solution, obvious only when found 😬 And I dare not ask for help (sorry for my poor English).
I like the effect of your code!

Phuket2

@cvp , no problems. I meant it a nice way. I wanted to solve it myself. Like it was something major to solve 😬 I didn't realise it was that simple. Btw, your English is fine. I have no problems understanding you.

cvp

a (very) small improvement:

    tot_time = 2 # total time in seconds
    for i in range(0, 360):
        mc.deg = i
        mc.set_needs_display()
        time.sleep(tot_time/360) 
Phuket2

@cvp , thanks, I will not use that loop anyway. In a real world context it does nothing for you.
I am writing a timer view at the moment, so I am changing the function as I go along. Just some extra params and calculating the Rect Center once into vars cx, cy.

The timer won't be mind blowing, but will be a decent example to share.

def draw_arc(rect, start, finish, offset = -90,
                color = 'white', line_width = 30):
    r = ui.Rect(*rect)
    cy, cx = r.center()

    s = ui.Path()

    radius = r.width / 2

    start = math.radians(start + offset)
    finish = math.radians(finish + offset )

    x = cy+radius * math.cos(start) 
    y = cx+radius * math.sin(start)

    s.move_to(x, y)
    s.add_arc(cy, cx, radius, start, finish)

    ui.set_color(color)
    s.line_width = line_width
    s.line_cap_style = ui.LINE_CAP_ROUND

    s.stroke()