derikvanschaik / MIMI

A mindmapping application using PySimpleGUI
4 stars 2 forks source link

How would you go about making spline curves for the connecting lines? #2

Open PySimpleGUI opened 2 years ago

PySimpleGUI commented 2 years ago

I'm curious what your approach would be to get curved lines between the boxes. I've not looked at the problem, but I've looked enviously at screenshots of network diagrams that have smooth, curved lines between the nodes. Do you now how to go about doing something like that?

derikvanschaik commented 2 years ago

I am not really sure...I have thought about this before but couldn't come up with a method that makes the lines very crisp. Here is a solution I just drummed up although I am pretty sure you have thought of some variation of this method. Basically this attempts to make as close to infinitely many points as possible and connect them with lines. I do think it is possible to improve on this greatly, all I understand about the problem is you need very small points and a specific y to x scaling to make it look crisper.

import PySimpleGUI as sg
import numpy as np 

def draw_spline_curve(canvas, points):
    lastPoint = points[0]
    for point in points[1:]:
        # make the points smaller by using linspace
        xVals = np.linspace(lastPoint[0], point[0], num = 50 )
        yVals = np.linspace(lastPoint[1], point[1], num = 50 )   
        values = []
        # build up values array 
        for i in range(len(xVals)):
            values.append( (xVals[i], yVals[i]))

        lastSmallerPoint = values[0]
        for smallPoint in values:
            canvas.draw_line(lastSmallerPoint, smallPoint)
            lastSmallerPoint = smallPoint 
        # reassign last point to curpoint for next iteration 
        lastPoint = point

def main():
    canvas = sg.Graph((500, 500), (0,0), (500, 500), key='canvas',background_color='white')
    layout =[[canvas]] 
    window = sg.Window('tester', layout).finalize()
    draw_spline_curve(canvas, [(x + 250, (1/50)*(-x**(2)) + 500 ) for x in range(-500, 500)]) 

    while True: 
        event, values = window.read()
        if event == sg.WIN_CLOSED:
            break

    window.close() 
if __name__ == "__main__":
    main() 
derikvanschaik commented 2 years ago

@PySimpleGUI I realize when looking at the code I can make a modification to make it a lot simpler and probably more efficient but it still does not solve the crispness issue

import PySimpleGUI as sg
import numpy as np 

def draw_spline_curve(canvas, points):
    lastPoint = points[0] 
    for point in points[1:]:
        canvas.draw_line(lastPoint, point) 
        lastPoint = point

def main():
    canvas = sg.Graph((500, 500), (0,0), (500, 500), key='canvas',background_color='white')
    layout =[[canvas]] 
    window = sg.Window('tester', layout).finalize()
    draw_spline_curve(canvas, [(x + 250, (1/50)*(-x**(2)) + 500 ) for x in np.linspace(-500, 500, num = 50000)]) 

    while True: 
        event, values = window.read()
        if event == sg.WIN_CLOSED:
            break

    window.close() 
if __name__ == "__main__":
    main() 
PySimpleGUI commented 2 years ago

Oh wow! That looks great!

image

I've not looked at this at all, so you're ahead of me on this one. That was quick work! I'm impressed.

PySimpleGUI commented 2 years ago

I don't know if it would be of help, but there's a multi-point draw: image

You can pass it a list of points and it'll draw lines between each point.

PySimpleGUI commented 2 years ago

I get a similar look using this:

import PySimpleGUI as sg
import numpy as np

def main():
    canvas = sg.Graph((500, 500), (0,0), (500, 500), key='canvas',background_color='white')
    layout =[[canvas]]
    window = sg.Window('tester', layout).finalize()

    points = [(x + 250, (1/50)*(-x**(2)) + 500 ) for x in np.linspace(-500, 500, num = 50000)]

    canvas.draw_polygon(points, fill_color='white', line_color='black' )

    while True:
        event, values = window.read()
        if event == sg.WIN_CLOSED:
            break

    window.close()
if __name__ == "__main__":
    main()

You know better than I do about these curves. I just know the methods that are available 😀

image

derikvanschaik commented 2 years ago

@PySimpleGUI even better, never really looked into that method ever. I guess I gotta read the docs! Tinkering with the line_width property has given me a nicer result in this very specific example

import PySimpleGUI as sg
import numpy as np

def main():
    canvas = sg.Graph((500, 500), (0,0), (500, 500), key='canvas',background_color='white')
    layout =[[canvas]]
    window = sg.Window('tester', layout).finalize()
    # reduced step var 'num' 
    points = [(x + 250, (1/50)*(-x**(2)) + 500 ) for x in np.linspace(-500, 500, num = 100)] 
    # increase line_width argument 
    canvas.draw_polygon(points, fill_color='white', line_color='black', line_width=4)

    while True:
        event, values = window.read()
        if event == sg.WIN_CLOSED:
            break

    window.close()
if __name__ == "__main__":
    main()
PySimpleGUI commented 2 years ago

Oooo.....

image

Yeah, that's nice!

I've no clue how to go about using something like this to connect 2 rectangles like in a network diagram.

I think your line drawing version is likely superior though. I'm not sure how to indicate (I've forgotten if it's even possible) that a "fill" should be transparent. The draw_polygon will fill automatically. I think your version is likely better in this situation. I'll look at the tkinter method create_polygon to see what's possible.

Having a demo that's got diagrams like yours would be really great. There so SO many applications for something that has boxes connected by lines. These curved lines are the difference between a very very simple diagram and a professional-looking one.

It's through projects like yours and capabilities the Jason write and my own experimentation that builds the base of knowledge and makes it possible for more and more people to make bigger applications.

One of the demos is a simple drawing application that shows how to click and move things around. You too have shown others how to do a similar operation. Now if someone wants to make a diagramming program, they'll have a nice jump-start.


Speaking of Graph Elements....

While "poking around" yesterday to see a few things people built, I ran across this remarkable program that I wouldn't believe is a PySimpleGUI program.

GrabCut-Annotation-Tool

https://user-images.githubusercontent.com/37477845/131681382-020df52c-dbc7-4750-80d1-42ff141ba829.mp4

Annotation tool using GrabCut() of OpenCV.
It can be used to create datasets for semantic segmentation.
* Due to GrabCut's algorithm, it is suitable for annotation of data with clear boundaries.

Requirement

Directory

│  app.py
│  config.json
│  
├─core
│  │  gui.py
│  └─util.py
│          
├─input
│      
└─output
    ├─image
    └─annotation

app.py, core/gui.py, core/util.py

Source code.

input

Image files are stored in this directory.

output

Directory to save annotation results.

Usage

Start it with the following command.

python app.py

The following options can be specified.

Using GrabCut-Annotation-Tool

File select

You can switch the annotation target by clicking the file list.
keyboard shortcut  ↑、p:preview file ↓、n:next file

Initial ROI designation

You can specify the initial ROI by right-drag the mouse when "Select ROI" is displayed.


After the drag is finished, GrabCut processing is performed.


The area is selected.


Background designation

You can specify the background by dragging the right mouse button.




前景指定

You can switch to foreground specification by unchecking "Manually label background".
keyboard shortcut Ctrl


You can specify the foreground by dragging the right mouse button.




Class ID switching

You can switch the class ID by pressing the check box.
The single digit ID can be switched with a shortcut key.
keyboard shortcut 0-9


After switching the class ID, it is necessary to specify the ROI.




Auto save

Resized images and annotation images are automatically saved for each GrabCut process.


If you do not want to save automatically, uncheck "Auto save".
If you want to save other than auto save, press "s" on the keyboard.


Other settings


ToDo

Author

Kazuhito Takahashi(https://twitter.com/KzhtTkhs)

License

GrabCut-Annotation-Tool is under Apache-2.0 License.

The sample image uses the photograph of フリー素材 ぱくたそ.

derikvanschaik commented 2 years ago

@PySimpleGUI The craziest part about this being implemented using the graph element is that I have literally no idea how they drew that image on the right using the available methods.....not even the slightest idea. I would never have even guessed this used the graph element to be honest. Also feel free to make a demo program that incorporates the textbox widgets I made, no problem! I would love to see that. I am pretty curious as to how this spline drawing could be used so I will probably start coding something up and see what works. If I come up with anything worth mentioning I will post it here!

PySimpleGUI commented 2 years ago

Please do lemme know if you end up adding something. No pressure to do so, however. I'm impressed you whipped up a little piece of sample code within a few hours of me asking about splines in general.

image

It's been great chatting with you. Thanks for taking a moment and working with me on this spline question. Keep on making stuff! You've got great instincts and can clearly work on problems quickly.