圆内点之间的弧

Arc between points in circle

我正在尝试使用 Matplotlib 绘制弦图。我知道已有的库(例如 Plotly)为我提供了该功能,但我真的很想在 matplotlib 中进行。

我目前的代码如下所示:

import itertools
import matplotlib.patches as patches
import matplotlib.pyplot as plt
import numpy as np

%matplotlib inline

fig, ax = plt.subplots()

ax.axhline(0, color='black', linestyle='--')
ax.axvline(0, color='black', linestyle='--')

npoints = 3

# Calculate the xy coords for each point on the circle
s = 2 * np.pi / npoints
verts = np.zeros((npoints, 2))
for i in np.arange(npoints):
    angle = s * i
    x = npoints * np.cos(angle)
    y = npoints * np.sin(angle)
    verts[i] = [x, y]


# Plot the arcs
numbers = [i for i in xrange(npoints)]
for i, j in itertools.product(numbers, repeat=2):

    if i == j:
        continue

    x1y1 = x1, y1 = verts[i]
    x2y2 = x2, y2 = verts[j]

    # Calculate the centre of the Arc
    mxmy = mx, my = [(x1 + x2) / 2, (y1 + y2) / 2]

    r = np.sqrt((x1 - mx)**2 + (y1 - my)**2)
    xy = [mx - r, my - r]
    width = 2 * r
    height = 2 * r
    start_angle = np.arctan2(y1 - my, x1 - mx) * 180 / np.pi
    end_angle = np.arctan2(y2 - my, x2 - mx) * 180 / np.pi

    arc = patches.Arc(mxmy, width, height, start_angle, end_angle)
    ax.add_patch(arc)

# Plot the points
x, y = verts.T
ax.scatter(x, y, marker='o', s=50, c='r')
ax.annotate("1", (x[0], y[0]), xytext=(x[0] + .5, y[0] + .5))
ax.annotate("2", (x[1], y[1]), xytext=(x[1] - 1, y[1] + .5))
ax.annotate("3", (x[2], y[2]), xytext=(x[2] - 1, y[2] - 1))

ax.set_xlim(-npoints - 5, npoints + 6)
ax.set_ylim(-npoints - 5, npoints + 6)
ax.set(aspect=1)

谁能告诉我为什么我的剧情是这样的?

我期待更像下面的内容(图片来自 Plotly


编辑 1

我想在以下各点之间画弧线:

理想情况下,这些弧应该在内侧。


编辑 2

经过进一步调查,我发现 end_angle 似乎是问题的根源。

在@f5r5e5d 指出 plotly 中使用的 Bézier curve 之后,我决定试一试。看起来这也是我的方法。

import itertools
import matplotlib.patches as patches
import matplotlib.pyplot as plt
import numpy as np
import sys

%matplotlib inline

fig, ax = plt.subplots()

npoints = 5

# Calculate the xy coords for each point on the circle
s = 2 * np.pi / npoints
verts = np.zeros((npoints, 2))
for i in np.arange(npoints):
    angle = s * i
    x = npoints * np.cos(angle)
    y = npoints * np.sin(angle)
    verts[i] = [x, y]

# Plot the Bezier curves
numbers = [i for i in xrange(npoints)]
bezier_path = np.arange(0, 1.01, 0.01)
for a, b in itertools.product(numbers, repeat=2):
    if a == b:
        continue

    x1y1 = x1, y1 = verts[a]
    x2y2 = x2, y2 = verts[b]

    xbyb = xb, yb = [0, 0]

    # Compute and store the Bezier curve points
    x = (1 - bezier_path)** 2 * x1 + 2 * (1 - bezier_path) * bezier_path * xb + bezier_path** 2 * x2
    y = (1 - bezier_path)** 2 * y1 + 2 * (1 - bezier_path) * bezier_path * yb + bezier_path** 2 * y2

    ax.plot(x, y, 'k-')

x, y = verts.T
ax.scatter(x, y, marker='o', s=50, c='r')

ax.set_xlim(-npoints - 5, npoints + 6)
ax.set_ylim(-npoints - 5, npoints + 6)
ax.set(aspect=1)

上面的代码描绘了我想要它做什么。稍微修改一下样式,应该就可以了。

由于潜在的问题是“如何在 matplotlib 中绘制和弦图”,我只想让您知道现在有一个 python 库可以做到这一点:mpl-chord-diagram

你可以做到 pip install mpl-chord-diagram.

[免责声明]我是当前维护者[/免责声明]