如何以任意角度绘制文本并平行于一条线?
How can I draw text at any angle above and parallel to a line?
我想在行的正上方放置一个文本(由变量 distance
控制)。有一个方法 drawLineDescription()
可以做到这一点。此方法获取直线的起点和终点,然后计算中心点 x, y
。我已经使用 angle
来确保文本放置正确。不幸的是,我无法弄清楚如何将文本垂直放置在每个角度的线上,即根据旋转,变量 x, y
必须能够移动。我该如何补充?
def drawLineDescription(canvas, startX, startY, endX, endY, distance):
lengthX = endX - startX
lengthY = endY - startY
x = int(startX+((lengthX)/2))
y = int(startY+((lengthY)/2))
angle = math.degrees(math.atan2(lengthY, lengthX)*-1)
angle = round(angle)
if angle < -90 or angle > 90:
angle += 180
canvas.create_text(x, y, angle=angle, font=("Arial", 12), text="exampleText")
最后它应该看起来像这样(多行文本示例 - 这些行永远不会交叉它们的文本):
Example result
lengthX = endX - startX
lengthY = endY - startY
fullLength = math.sqrt(lengthX**2 + lengthY**2)
#unit direction vector
ux = lengthX / fullLength
uy = lengthY / fullLength
#unit normal
if ux < 0:
nx, ny = -uy, ux
else:
nx, ny = uy, -ux
#text center point (D at the picture)
cx = x + nx * distance
cy = y + ny * distance
#if you need start of text (S at the picture)
sx = x + nx * distance - ux * halfwidth
sy = y + ny * distance - uy * halfwidth
如果您使用 tcl > 8.6
,您可以在 tkinter 上绘制旋转文本,并遵循以下说明:canvas_item = tk.create_text
和 canvas.itemconfig(canvas_item, angle=rotation_angle)
为了实现您想要的效果,您需要一些几何知识,尤其是线段的坐标、中点、垂直于线段的偏移向量以及线段的角度。
我将计算适当几何元素所需的算法封装在 class point
和 c class Vector
中。这些 class 不是防弹的,但它们为您提供了基本几何的起点。
我添加了一个由两点定义的线的示例,文本根据需要放置并旋转以匹配线段的方向。
import math
import tkinter as tk
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other: 'Vector'):
return Vector(other.x + self.x, other.y + self.y)
def __sub__(self, other):
return Vector(self.x - other.x, self.y - other.y)
def __iter__(self):
yield self.x
yield self.y
def __str__(self):
return f'{self.__class__.__name__}({self.x}, {self.y})'
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
return Point(other.x + self.x, other.y + self.y)
def __sub__(self, other):
return Vector(other.x - self.x, other.y - self.y)
def scale(self, scalar):
return Vector(self.x * scalar, self.y * scalar)
def normal(self):
norm = self.norm()
return Vector(self.x / norm, self.y / norm)
def norm(self):
return math.hypot(self.x, self.y)
def perp(self):
x, y = self.normal()
return Vector(y, -x)
def angle(self):
return math.atan2(-self.y, self.x) * (180 / math.pi)
def __iter__(self):
yield self.x
yield self.y
def __str__(self):
return f'{self.__class__.__name__}({self.x}, {self.y})'
if __name__ == '__main__':
root = tk.Tk()
canvas = tk.Canvas(root, width=500, height=500)
p0, p1 = Point(100, 40), Point(200, 300)
segment = p1 - p0
mid_point = segment.scale(0.5) + p0
# canvas.create_oval(*(mid_point - Vector(2, 2)), *(Vector(2, 2) + mid_point))
line = canvas.create_line(*p0, *p1)
offset = segment.perp().scale(20)
# canvas.create_line(*mid_point, *(mid_point+offset))
txt = canvas.create_text(*(offset + mid_point), text='example')
canvas.itemconfig(txt, angle=segment.angle())
canvas.pack()
root.mainloop()
我想在行的正上方放置一个文本(由变量 distance
控制)。有一个方法 drawLineDescription()
可以做到这一点。此方法获取直线的起点和终点,然后计算中心点 x, y
。我已经使用 angle
来确保文本放置正确。不幸的是,我无法弄清楚如何将文本垂直放置在每个角度的线上,即根据旋转,变量 x, y
必须能够移动。我该如何补充?
def drawLineDescription(canvas, startX, startY, endX, endY, distance):
lengthX = endX - startX
lengthY = endY - startY
x = int(startX+((lengthX)/2))
y = int(startY+((lengthY)/2))
angle = math.degrees(math.atan2(lengthY, lengthX)*-1)
angle = round(angle)
if angle < -90 or angle > 90:
angle += 180
canvas.create_text(x, y, angle=angle, font=("Arial", 12), text="exampleText")
最后它应该看起来像这样(多行文本示例 - 这些行永远不会交叉它们的文本): Example result
lengthX = endX - startX
lengthY = endY - startY
fullLength = math.sqrt(lengthX**2 + lengthY**2)
#unit direction vector
ux = lengthX / fullLength
uy = lengthY / fullLength
#unit normal
if ux < 0:
nx, ny = -uy, ux
else:
nx, ny = uy, -ux
#text center point (D at the picture)
cx = x + nx * distance
cy = y + ny * distance
#if you need start of text (S at the picture)
sx = x + nx * distance - ux * halfwidth
sy = y + ny * distance - uy * halfwidth
如果您使用 tcl > 8.6
,您可以在 tkinter 上绘制旋转文本,并遵循以下说明:canvas_item = tk.create_text
和 canvas.itemconfig(canvas_item, angle=rotation_angle)
为了实现您想要的效果,您需要一些几何知识,尤其是线段的坐标、中点、垂直于线段的偏移向量以及线段的角度。
我将计算适当几何元素所需的算法封装在 class point
和 c class Vector
中。这些 class 不是防弹的,但它们为您提供了基本几何的起点。
我添加了一个由两点定义的线的示例,文本根据需要放置并旋转以匹配线段的方向。
import math
import tkinter as tk
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other: 'Vector'):
return Vector(other.x + self.x, other.y + self.y)
def __sub__(self, other):
return Vector(self.x - other.x, self.y - other.y)
def __iter__(self):
yield self.x
yield self.y
def __str__(self):
return f'{self.__class__.__name__}({self.x}, {self.y})'
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
return Point(other.x + self.x, other.y + self.y)
def __sub__(self, other):
return Vector(other.x - self.x, other.y - self.y)
def scale(self, scalar):
return Vector(self.x * scalar, self.y * scalar)
def normal(self):
norm = self.norm()
return Vector(self.x / norm, self.y / norm)
def norm(self):
return math.hypot(self.x, self.y)
def perp(self):
x, y = self.normal()
return Vector(y, -x)
def angle(self):
return math.atan2(-self.y, self.x) * (180 / math.pi)
def __iter__(self):
yield self.x
yield self.y
def __str__(self):
return f'{self.__class__.__name__}({self.x}, {self.y})'
if __name__ == '__main__':
root = tk.Tk()
canvas = tk.Canvas(root, width=500, height=500)
p0, p1 = Point(100, 40), Point(200, 300)
segment = p1 - p0
mid_point = segment.scale(0.5) + p0
# canvas.create_oval(*(mid_point - Vector(2, 2)), *(Vector(2, 2) + mid_point))
line = canvas.create_line(*p0, *p1)
offset = segment.perp().scale(20)
# canvas.create_line(*mid_point, *(mid_point+offset))
txt = canvas.create_text(*(offset + mid_point), text='example')
canvas.itemconfig(txt, angle=segment.angle())
canvas.pack()
root.mainloop()