Jul-27-2023, 04:21 PM
Hello, not good at math haha. So I have a little app and I want to know how to create a good musical slur like in sheet music using tkinter canvas. In this example I used
My code:
create_polygon() to draw a beautiful cubic bezier curve. Then I did the same curve in reversed() mode with slightly different middle two control points. The problem is that I need some clever math solution to get a good shape in all directions. I feel like I am almost there :)My code:
import tkinter as tk
import math
class BezierCurveApp:
def __init__(self, master):
self.master = master
self.master.title("Cubic Bezier Curve Editor")
self.canvas = tk.Canvas(self.master, width=400, height=400)
self.canvas.pack(expand=True, fill='both')
self.control_points = [[200, 100], [250, 200], [350, 200], [400, 100]]
self.control_points2 = [[0, 100], [50, 210], [150, 210], [200, 100]]
self.control_point_color = "black"
self.selected_point_index = None
self.width = 10
self.draw_curve()
self.draw_control_points()
self.canvas.bind("<Button-1>", self.on_mouse_click)
self.canvas.bind("<B1-Motion>", self.on_mouse_drag)
def on_mouse_click(self, event):
x, y = event.x, event.y
for i, (px, py) in enumerate(self.control_points):
if abs(x - px) < 10 and abs(y - py) < 10:
self.selected_point_index = i
break
def on_mouse_drag(self, event):
if self.selected_point_index is not None:
self.control_points[self.selected_point_index] = [event.x, event.y]
self.canvas.delete("all")
self.draw_curve()
self.draw_control_points()
def draw_curve(self):
# here is my question: I try to create a music slur using polygon from tkinter.
# my approach is to draw two cubic bezier curves with slightly different middle control points.
# how to draw the outline of a music slur?????
curve_points = []
ctl1 = self.control_points[0]
ctl2 = self.control_points[1]
ctl3 = self.control_points[2]
ctl4 = self.control_points[3]
# creating the basic curve
for t in range(100):
x, y = self.evaluate_cubic_bezier(t / 100, [ctl1,ctl2,ctl3,ctl4])
curve_points.append([x, y])
# doing the same curve in reverse with slightly changed ctl2 and ctl3 points. How to calculate the right xy offsets for ctl2 and 3?????
for t in reversed(range(100)):
x, y = self.evaluate_cubic_bezier(t / 100, [ctl1,[ctl2[0]+15,ctl2[1]+10],[ctl3[0],ctl3[1]+10],ctl4])
curve_points.append([x, y])
# draw slur
self.canvas.create_polygon(curve_points, fill='black', tag='slur')
self.canvas.create_line(self.control_points, dash=(6,6), width=1, fill='red')
def draw_control_points(self):
for i, (x, y) in enumerate(self.control_points):
fill_color = self.control_point_color
if self.selected_point_index == i:
fill_color = "green"
r = 5
self.canvas.create_oval(x - r, y - r, x + r, y + r, fill=fill_color)
def evaluate_cubic_bezier(self, t, control_points):
p0, p1, p2, p3 = control_points
x = (1 - t) ** 3 * p0[0] + 3 * t * (1 - t) ** 2 * p1[0] + 3 * t ** 2 * (1 - t) * p2[0] + t ** 3 * p3[0]
y = (1 - t) ** 3 * p0[1] + 3 * t * (1 - t) ** 2 * p1[1] + 3 * t ** 2 * (1 - t) * p2[1] + t ** 3 * p3[1]
return x, y
root = tk.Tk()
app = BezierCurveApp(root)
root.mainloop()You can drag the control points to see how the shape reacts to it. How to create a musical slur that always has a good shape no matter what direction it's drawn?
