Incompatibility on drawing a line

jujuneosjujuneos Posts: 9Member

Hello guys, I'm having a trouble. I'm creating a simple drawing app for children. When I test it on godot, the line is drawn right after the cursor, but when I export the apk, the line is drawn with a huge space between it and the cursor. This happens both on the android emulator and on my phone. As you can see in the image, the cursor is very far from where the line is being drawn. Can anyone help me?


Tags :

Comments

  • TwistedTwiglegTwistedTwigleg Posts: 3,525Admin

    Welcome to the forums @jujuneos!

    How are you getting the mouse position? Are you using the get_mouse_position, get_global_mouse_position, or something else? My guess is that something is applying an offset to the mouse/cursor position and that is why the issue is occurring.

    Also, does the issue occur for the UI, or just drawing the lines?

  • jujuneosjujuneos Posts: 9Member

    Thanks, @TwistedTwigleg

    I've already tried both get_global_mouse_position and get_local_mouse_position. When I try to use get_mouse_position, I get an error: The method "get_mouse_position" isn't declared in the current class.

    The issue only occur when I draw the lines

  • TwistedTwiglegTwistedTwigleg Posts: 3,525Admin

    Well, if the issue only occurs for drawing the lines, I'm thinking it is probably a code issue rather than a core Godot bug (not to say it is not a Godot bug, its just unlikely to be a core one). How are you drawing the lines? Are you using a Line2D node, using the draw_line function, or something else?

  • jujuneosjujuneos Posts: 9Member

    @TwistedTwigleg I don't know how to draw freely using draw_line, when I use it, godot itself already shows a line drawn automatically, could you help me with that?

    I'm using an alternative way I found on YouTube, using a Timer and add_point.

        extends Line2D
    
        func _ready():
            pass 
    
        func _process(delta):
            if Input.is_action_just_released("draw"):
                $Timer.stop()
    
        func _on_Timer_timeout():
            add_point(get_global_mouse_position())
            $Timer.start()
    
  • TwistedTwiglegTwistedTwigleg Posts: 3,525Admin
    edited October 24

    Okay, looking at the code, I think I may know what is causing the issue. From what I remember, the Line2D node expects points passed to be in local space (position relative to its origin) rather than global space (relative to the scene origin). Since the mouse positions are in global space, there is a disconnect in the position spaces being used and that is leading to the offset.

    Thankfully, converting between spaces is relatively easy. I believe for this issue, all that needs to be done is convert the mouse position to local space relative to the Line2D:

    func on_Timer_timeout():
        add_point(global_transform.affine_inverse().xform(get_global_mouse_position()))
        $Timer.start()
    

    @jujuneos said:
    @TwistedTwigleg I don't know how to draw freely using draw_line, when I use it, godot itself already shows a line drawn automatically, could you help me with that?

    Using draw_line would be similar to how the Line2D node works internally: You would need to cache a series of points in an array, and then iterate through that array and draw the lines. Something like this (untested):

    var draw_points = []
    var point_timer = 0
    const POINT_WAIT_TIME = 0.1
    
    func _process(delta):
        if Input.is_action_pressed("draw"):
            if (point_timer <= 0):
                draw_points.append(get_global_mouse_position())
                point_timer += POINT_WAIT_TIME
                update()
            else:
                point_timer -= delta
    
    func _draw():
        for i in range(0, draw_points.size()-1):
            var next_position = global_transform.affine_inverse().xform(draw_points[i+1])
            var current_position = global_transform.affine_inverse().xform(draw_points[i])
            draw_line(current_position, next_position, Color.black, 1.0)
    
  • jujuneosjujuneos Posts: 9Member

    Thanks for helping me, @TwistedTwigleg

    I think the problem isn't on the code. The two solutions you gave worked, but when I export the apk, it still has the same spacing between the cursor and the line. Maybe it's a problem of resolution or compatibility, isn't it?

  • cyberealitycybereality Posts: 1,086Moderator

    Could be a portrait / landscape issue, or also not accounting for the aspect ratio / resolution scaling. You can also use events instead of mouse position. I did this on a test project and it works great on mobile. Just add a Node2D to the scene and attach this script.

    extends Node2D
    
    var points = []
    
    func _process(delta):
        update()
    
    func _input(event):
        if event.is_action_pressed("click") or \
                event is InputEventScreenTouch:
            points.push_back(event.position)
    
    func _draw():
        var view_rect = get_viewport().get_visible_rect()
        draw_rect(view_rect, Color(0.2, 0.2, 0.2))
        var total_points = points.size()
        if total_points > 1:
            for i in range(0, total_points - 1):
                draw_line(points[i], points[i + 1], Color(1.0, 1.0, 1.0), 3)
        for i in range(0, total_points):
                draw_circle(points[i], 6, Color(1.0, 1.0, 1.0))
    
  • jujuneosjujuneos Posts: 9Member

    Thanks, @cybereality

    This works on mobile without the spacing. However, this code draws a straight line between the last clicked point and the current one. I wanted it to work like microsoft paint: you click and drag and it creates a continuous line. Is it possible?

  • cyberealitycybereality Posts: 1,086Moderator

    Well a continuous line is just a series of straight lines to a computer. To do that you could get the position of the touch drag event and add points to the line then rather than just on the touch/click.

  • cyberealitycybereality Posts: 1,086Moderator

    Another option would be to draw a circle around the drag position, this would be more like using a marker (and what I assume MS Paint is doing) but I think the line method would be more optimized.

  • jujuneosjujuneos Posts: 9Member

    I get it! I just changed InputEventScreenTouch to InputEventScreenDrag and it worked! Thank you so much @cybereality

  • jujuneosjujuneos Posts: 9Member

    Hey @cybereality now I have another problem, if you could help me one more time: when I finish drawing a line, release the mouse button and change the position to draw another line, the new line always starts from where the previous one stopped. How can I make the lines independent of each other?

  • cyberealitycybereality Posts: 1,086Moderator

    Probably the easiest way would be to insert a fake value on InputEventScreenTouch, like (0, 0), and then when you are looping through the points to draw, skip (use a continue) if either the current point or the last point is (0, 0).

  • jujuneosjujuneos Posts: 9Member

    @cybereality sorry to bother you after almost a month, but i tried some ways to do what you said, and godot just says "you can't change the value of a constant". Can you help me one more time? This is what I did:

    func _input(event):
        if event.is_action_pressed("draw") or \
                event is InputEventScreenDrag:
            points.push_back(event.position)
    
    func _draw():
        var total_points = points.size()
        if total_points > 1:
            for i in range(0, total_points - 1):
                line = draw_line(points[i], points[i + 1], color, width)
                if (InputEventScreenTouch == (0,0)):
                    continue
    
  • cyberealitycybereality Posts: 1,086Moderator
    edited November 21

    InputEventScreenTouch is an event, not a vector value. You could use event.position, but I believe that only works in the _input function. In any case, that's not the method I was explaining, and the code you posted won't work. You can try this:

    func _draw():
        var total_points = points.size()
        if total_points > 1:
            for i in range(0, total_points - 1):
                if (points[i] == (0,0) or points[i+1] == (0,0)):
                    continue
                line = draw_line(points[i], points[i + 1], color, width)
    
    
  • cyberealitycybereality Posts: 1,086Moderator

    And don't forget to add those zero points when the user lifts their finger.

  • jujuneosjujuneos Posts: 9Member

    @cybereality I get it! But the more lines I draw, the slower the drawing gets. I believe this is happening because of the size of the array that is increasing. would there be a way to put each line in a different array or something to avoid that?

  • cyberealitycybereality Posts: 1,086Moderator

    If it's getting slow, you might want to try drawing to a texture, that way you don't have to loop through the points every frame. I haven't tried this, but I believe it should work (likely what apps like MS Paint are doing).

  • PeterPMPeterPM Posts: 8Member

    BROTHER. i have fixed your problem, because i am creating a drawing app to.
    I have fixed the increasing slowing of the program, when creating several lines
    and i have fixed the input bug, when you get piccoted lines

    Please, let me know the state of your problems.

  • PeterPMPeterPM Posts: 8Member
    edited November 21
    Because it is not a open-project, i can not share my complete implementation, and nether will.
    BUT
    I will share the line algo, that i get somewhere.
    AND
    i will tell you the fix for the problem, but you will need to think.
    
    This is the function that get 2 Vector2 and plot a line in betwen
        IF, you feed the coordenates of the previous input position, and the actual input position
            WHEN any of the X or Y values diference if greater then 1
                It will plot the correct straight line and it will fix the problem of FastInputDoNotDrawContinuously
    
    func draw_fill_gap(start : Vector2, end : Vector2) -> void:
        var dx := int(abs(end.x - start.x))
        var dy := int(-abs(end.y - start.y))
        var err := dx + dy
        var e2 := err << 1
        var sx = 1 if start.x < end.x else -1
        var sy = 1 if start.y < end.y else -1
        var x = start.x
        var y = start.y
        while !(x == end.x && y == end.y):
            e2 = err << 1
            if e2 >= dy:
                err += dy
                x += sx
    
            if e2 <= dx:
                err += dx
                y += sy
    
            filler(Vector2(x, y))
    
    
    .About the increasing performance usage when having several lines, i tell you a only a hint, but... ricercare, brother.
        You are getting a performance problem, because well, you are computing more and more things when you draw more and more lines
        How do you fix that? 
        You simple does not compute more and more things, you keep your computation static.
            You need Godot to work your empty space and filled space the same
                You can get that by using some bult-in node.
    
    What built-in node can fill the entire screen, with nothing and with completle noise, and keep the performance the same?
    Find that node, and you fix your performance problem. (and create another several problens, that are indeed very boring to fix, but its the work)
    

Leave a Comment

BoldItalicStrikethroughOrdered listUnordered list
Emoji
Image
Align leftAlign centerAlign rightToggle HTML viewToggle full pageToggle lights
Drop image/file