omz:forum

    • Register
    • Login
    • Search
    • Recent
    • Popular

    Welcome!

    This is the community forum for my apps Pythonista and Editorial.

    For individual support questions, you can also send an email. If you have a very short question or just want to say hello — I'm @olemoritz on Twitter.


    draw ui.Path within the coordinate system of a node

    Pythonista
    8
    31
    13027
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • zipit
      zipit last edited by zipit

      Hi,

      I do not really understand how the frame of an ui.Path object works. If have cooked up a small scene that illustrates the problems I have. I am trying to draw an ui.Path that is matching points (coordinates) in the frame of a node. It seems that an ui.Path is always being centered on the center of the presenting ShapeNode.

      So my question is: How do I draw a path which is not centered on the center of its presenting ShapeNode - for example one that only lives in the x+y+ frame ? I would like to be able to actually draw within the coordinates system/frame of the presenting ShapeNode .

      Cheers,
      zipit

      from scene import *
      
      class MyScene (Scene):
          def setup(self):
              sx, sy = self.size.w * .5, self.size.h * .5
              # I would expect the white rect to have its lower left corner 
              # at the center of the screen. But it does not, it is sitting
              # on the origin of the node, x and y seem to have no effect.
              self.white = ShapeNode(ui.Path.rect(sx, sy, 200, 200), 
                                     parent=self,
                                     position=(0, 0))
              # a reference rect as our white rect is kinda off screen
              self.red = ShapeNode(ui.Path.rect(0, 0, 150, 150), 
                                   parent=self,
                                   fill_color = 'red',
                                   position=(sx, sy)) 
              # Here I would expect a line from the right top corner
              # of the red rect going to a point (25, 50) in the 
              # top right direction. But again the path is centered
              # on the node and also the y coordinate is being inverted.
              path = ui.Path()
              path.move_to(75,  75)
              path.line_to(sx + 100, sy + 125)
              path.line_width = 3
              self.cyan = ShapeNode(path, 
                                    parent=self.red,
                                    stroke_color='cyan',
                                    position=(0, 0))
               
      
      if __name__ == '__main__':
          run(MyScene(), show_fps=False)```
      1 Reply Last reply Reply Quote 0
      • JonB
        JonB last edited by

        @zipit said:

        from scene import *

        class MyScene (Scene):
        def setup(self):
        sx, sy = self.size.w * .5, self.size.h * .5
        # I would expect the white rect to have its lower left corner
        # at the center of the screen. But it does not, it is sitting
        # on the origin of the node, x and y seem to have no effect.
        self.white = ShapeNode(ui.Path.rect(sx, sy, 200, 200),
        parent=self,
        position=(0, 0))
        # a reference rect as our white rect is kinda off screen
        self.red = ShapeNode(ui.Path.rect(0, 0, 150, 150),
        parent=self,
        fill_color = 'red',
        position=(sx, sy))
        # Here I would expect a line from the right top corner
        # of the red rect going to a point (25, 50) in the
        # top right direction. But again the path is centered
        # on the node and also the y coordinate is being inverted.
        path = ui.Path()
        path.move_to(75, 75)
        path.line_to(sx + 100, sy + 125)
        path.line_width = 3
        self.cyan = ShapeNode(path,
        parent=self.red,
        stroke_color='cyan',
        position=(0, 0))

        if name == 'main':
        run(MyScene(), show_fps=False)```

        anchor_point can help in some cases.

        but, the problem seems to be that, no matter what you draw in a shape node, the coordinate system of the path is not honored, instead the bbox of the resulting path is computed, and then you can anchor it to a point relative to the bounding box. That makes it easier to do some things, but harder for others... for instance a diagonal line from (0,0) to (x,y), depending on the sign of x and y, the same anchor point (0,0) refers to either the start, end, or diagonal corner of the line.

        I seem to recall a forum post with a workaround that I posted, but my Google-fu is failing me this morning. I think you basically have to keep track of the math yourself on what the "center" and bounds of the path will be, and the use the anchor point to keep things aligned.

        1 Reply Last reply Reply Quote 0
        • omz
          omz last edited by

          A ShapeNode is basically just a SpriteNode that automatically creates its Texture using a path. The code is pretty simple, and you can actually look at it yourself by opening "Modules & Templates/Standard Library/site-packages/scene.py".

          If you want full control over the resulting texture's size, you could simply use a vanilla SpriteNode and draw the shape yourself.

          import ui, scene
          
          def texture_from_path(path, fill_color, width, height):
              with ui.ImageContext(width, height) as ctx:
                  ui.set_color(fill_color)
                  path.fill()
                  img = ctx.get_image()
              return scene.Texture(img)
          

          This is simplified a bit, but it shows the basic approach. You can look at ShapeNode's source code if you need its support of shadows, outlines and such.

          1 Reply Last reply Reply Quote 0
          • zipit
            zipit last edited by zipit

            Ah, okay, that does make sense now. I do have a follow up question though. Is it possible to access the points of a path after it has been created? edit: and with access I mean read them.

            Cheers,
            zipit

            1 Reply Last reply Reply Quote 0
            • omz
              omz last edited by

              @zipit No, that's not possible, you'd have to keep a reference to the points yourself.

              1 Reply Last reply Reply Quote 0
              • zipit
                zipit last edited by zipit

                Okay, thanks. I gave the thread a more meaningful title. I cannot provide a full example as my code relies on other stuff, but here is a snippet how I did solve the problem now (a bit clunky). Maybe it will help someone in the future. I wrote two comments so that the code does make some sense.

                def draw_line(self):
                        '''
                        '''
                        if self.line is not None:
                            self.line.remove_from_parent()
                        minx, miny = None, None
                        path = ui.Path()
                        path.line_width = 2
                        # self is a pythonista node. self.anchors are some phythonista node 
                        # objects that are children of self. We want to draw a line through all
                        # these anchors within the coord system of self.
                        for i, anchor in enumerate(self.anchors):
                            p = anchor.position
                            # get/update the lower left corner minimum
                            minx, miny = (p.x if minx is None else min(minx, p.x),
                                          p.y if miny is None else min(miny, p.y))
                            if i == 0: path.move_to(p.x, -p.y)
                            else: path.line_to(p.x, -p.y)
                        # the offset(position) of our node has to be the lower left corner
                        # point plus the center vector of our path
                        self.line = ShapeNode(path,
                                              stroke_color='green',
                                              fill_color='transparent',
                                              position = (minx + path.bounds.w * .5,
                                                          miny + path.bounds.h * .5),
                                              parent=self)
                
                1 Reply Last reply Reply Quote 1
                • mikeno
                  mikeno last edited by

                  Hi, looking back to this example, I tried to play a bit and discovered what seems to be an issue:

                  
                  from scene import *
                  import time
                  class MyScene (Scene):
                  	def setup(self):
                  		sx, sy = self.size.w, self.size.h
                  		self.red = ShapeNode(ui.Path.rect(0,0,sx,sy),parent=self,fill_color='red',position=(0,0),anchor_point=(0,0))
                  		path = ui.Path()
                  		path.line_width = 3
                  		path.move_to(0,0)
                  		path.line_to(100,0)
                  		#path.line_to(0,100) # Uncommenting this line modifies the drawing of both previous lines
                  		self.cyan = ShapeNode(path,parent=self.red,stroke_color='white',position=(0,0),anchor_point=(0,0))
                  if __name__ == '__main__':
                  	run(MyScene())
                  
                  

                  Removing the comment modifies how both previous lines are drawn!
                  I tried to understand but it is still unclear for me

                  mikael 1 Reply Last reply Reply Quote 0
                  • mikael
                    mikael @mikeno last edited by

                    @mikeno, without running this, I think what you are seeing is that the commented line changes the bounding box of the whole path and thus the node, and as the node position defines the center position, the path seems to move to left.

                    1 Reply Last reply Reply Quote 0
                    • mikeno
                      mikeno last edited by

                      Hi Mikael, thx for replying but is there a way to avoid this?

                      mikael 1 Reply Last reply Reply Quote 0
                      • mikael
                        mikael @mikeno last edited by

                        @mikeno, do you need to use scene, or in other words, what are you trying to do?

                        1 Reply Last reply Reply Quote 0
                        • mikeno
                          mikeno last edited by

                          I just want to draw a filled polygone following numerous coordinates, I already managed to do it with ui and canvas but only scene gives me the possibility to use full screen

                          cvp 1 Reply Last reply Reply Quote 0
                          • cvp
                            cvp @mikeno last edited by cvp

                            @mikeno try this, and to close, swipe down with two fingers

                            import ui
                            class my(ui.View):
                            	def draw(self):
                            		w,h = ui.get_screen_size()
                            		path = ui.Path()#.rect(0,0,w,h)
                            		path.line_width = 3
                            		ui.set_color('red')
                            		path.move_to(100,100)
                            		path.line_to(200,100)
                            		path.line_to(100,200)
                            		path.close()
                            		path.fill()
                            		#path.stroke()
                            v = my()
                            v.present('fullscreen',hide_title_bar=True)
                            
                            1 Reply Last reply Reply Quote 0
                            • mikeno
                              mikeno last edited by

                              Yes thx it works but it doesn’t with several polygons

                              cvp 1 Reply Last reply Reply Quote 0
                              • mikeno
                                mikeno last edited by

                                ... and then, I need to redraw in order to see the polygons in a different zoom factor. As I wrote, I already tried with ui but I stoped because I could not redraw, that’s why I tried with scene

                                cvp 1 Reply Last reply Reply Quote 0
                                • cvp
                                  cvp @mikeno last edited by cvp

                                  @mikeno said:

                                  it doesn’t with several polygons

                                  import ui
                                  class my(ui.View):
                                  	def draw(self):
                                  		w,h = ui.get_screen_size()
                                  		path1 = ui.Path()
                                  		path1.line_width = 3
                                  		ui.set_color('red')
                                  		path1.move_to(100,100)
                                  		path1.line_to(200,100)
                                  		path1.line_to(100,200)
                                  		path1.close()
                                  		path1.fill()
                                  		path2 = ui.Path()#.rect(0,0,w,h)
                                  		path2.line_width = 3
                                  		ui.set_color('blue')
                                  		path2.move_to(300,100)
                                  		path2.line_to(400,100)
                                  		path2.line_to(300,200)
                                  		path2.close()
                                  		path2.fill()
                                  v = my()
                                  v.present('fullscreen',hide_title_bar=True)
                                  

                                  1 Reply Last reply Reply Quote 1
                                  • cvp
                                    cvp @mikeno last edited by

                                    @mikeno said:

                                    zoom see @mikael Gestures

                                    1 Reply Last reply Reply Quote 0
                                    • mikeno
                                      mikeno last edited by

                                      Thx to both of you, concerning the gestures, it seems to be a little bit complicated but I will try

                                      1 Reply Last reply Reply Quote 0
                                      • mikeno
                                        mikeno last edited by

                                        My last issue is that the drawing doesn’t refresh even when I call explicitly drawTest() as you can see in the example below:

                                        
                                        import ui
                                        
                                        class my(ui.View):
                                        	
                                        	def __init__(self, *args, **kwargs):
                                        		self.factor = .5
                                        
                                        	def draw(self):
                                        		print('draw()')
                                        		self.drawTest()
                                        
                                        	def drawTest(self):
                                        		print('drawTest()')
                                        		print('%.1f' % (self.factor))
                                        		w,h = ui.get_screen_size()
                                        		path1 = ui.Path()
                                        		path1.line_width = 3
                                        		ui.set_color('red')
                                        		path1.move_to(100*self.factor,100*self.factor)
                                        		path1.line_to(200*self.factor,100*self.factor)
                                        		path1.line_to(100*self.factor,200*self.factor)
                                        		path1.close()
                                        		path1.fill()
                                        		path2 = ui.Path()#.rect(0,0,w,h)
                                        		path2.line_width = 3
                                        		ui.set_color('blue')
                                        		path2.move_to(300*self.factor,100*self.factor)
                                        		path2.line_to(400*self.factor,100*self.factor)
                                        		path2.line_to(300*self.factor,200*self.factor)
                                        		path2.close()
                                        		path2.fill()
                                        		
                                        	def touch_began(self, touch):
                                        		print('touch_began()')
                                        		self.factor += .5
                                        		self.drawTest()
                                        		
                                        	def will_close(self):
                                        		print('will_close()')
                                        		
                                        v = my()
                                        v.present('fullscreen',hide_title_bar=True)
                                        
                                        

                                        It redraws only when I turn the iPad, could you please tell me how I can force the refresh with the right zoom factor?

                                        cvp mikael 2 Replies Last reply Reply Quote 0
                                        • cvp
                                          cvp @mikeno last edited by

                                          @mikeno start from

                                          import ui
                                          
                                          class my(ui.View):
                                              
                                              def __init__(self, *args, **kwargs):
                                                  self.w,self.h = ui.get_screen_size()
                                                  iv = ui.ImageView(name='iv')
                                                  iv.frame = (0,0,self.w,self.h)
                                                  self.add_subview(iv)
                                                  self.factor = .5
                                                  self.update_interval = 1
                                          
                                              def update(self):
                                                  with ui.ImageContext(self.w,self.h) as ctx:
                                                    path1 = ui.Path()
                                                    path1.line_width = 3
                                                    ui.set_color('red')
                                                    path1.move_to(100*self.factor,100*self.factor)
                                                    path1.line_to(200*self.factor,100*self.factor)
                                                    path1.line_to(100*self.factor,200*self.factor)
                                                    path1.close()
                                                    path1.fill()
                                                    path2 = ui.Path()#.rect(0,0,w,h)
                                                    path2.line_width = 3
                                                    ui.set_color('blue')
                                                    path2.move_to(300*self.factor,100*self.factor)
                                                    path2.line_to(400*self.factor,100*self.factor)
                                                    path2.line_to(300*self.factor,200*self.factor)
                                                    path2.close()
                                                    path2.fill()
                                                    ui_image = ctx.get_image()
                                                  self['iv'].image = ui_image
                                                  
                                              def touch_began(self, touch):
                                                  self.factor += .5
                                                  
                                              def will_close(self):
                                                  print('will_close()')
                                                  
                                          v = my()
                                          v.present('fullscreen',hide_title_bar=True)
                                          
                                          
                                          1 Reply Last reply Reply Quote 0
                                          • mikeno
                                            mikeno last edited by

                                            Whao, I’m impressed, thank you!
                                            But I don’t understand why it doesn’t work in my last example

                                            cvp 2 Replies Last reply Reply Quote 0
                                            • First post
                                              Last post
                                            Powered by NodeBB Forums | Contributors