甜甜圈图:计算外圆坐标得到切片之间的平行间隙

Dougnut chart: calculate outer circle's coordinates to get parallel gaps between slices

我有一个自定义的 Dougnut 图表抽屉,可以像这样绘制甜甜圈:

我对此非常满意,但是我想对其进行微调,但我只是不知道正确的解决方案。

我希望切片之间有 平行 间隙(填充)。

我提高了这些填充以更好地了解我的目标。

我目前是这样画的:

double cx, cy; //center points of circle
double r1, r2; //radius of outer and inner circle
double pad = M_PI / 360 * 12; //12 degree pad
double alpha = -1 * M_PI / 2; //starting from up (noon)
double da = 0.0; // delta of current slice

for (ALL_SLICES) {
  calculate_da(&da); //calculate slice's delta

  // drawing
  move_pen(cx + r2 * cos(alpha + pad/2), cy + r2 * sin(alpha + pad/2)); //STEP1
  draw_arc(cx, cy, r1, alpha + pad/2, alpha + da - pad/2); //STEP2
  draw_negative_arc(cx, cy, r2, alpha + da - pad/2, alpha + pad/2); //STEP3
  fill();
  
  //update next alpha
  alpha += da;
}

绘图步骤:

所以这很简单,但是要在切片之间有平行间隙,我必须为外端点和起点计算另一个角度:

我已经画了很多三角形,但我没能轻易解决这个问题。

同样:目标是在切片之间有平行的填充,即将这两个红点在圆周上靠得更近,以获得两个切片之间的精确平行线。

更新:

这是使用@chux 回答的缩放填充后的样子:

不幸的是它并不完美,这些线不平行:

我们将针对一个任意间隙的情况来解决这个问题。然后,您可以将我们的解决方案应用于任意数量的间隙。

首先,我们可以计算出两条边界从中心向外辐射的夹角的平均值。这将是我们的两个新线段从内圈辐射到外圈的角度。如果原始线段以角度 a 和 b 从内圆的中心辐射,则令 c 为通过它们之间圆的较小线段的中间的角度。 a = 60 deg, b = 90 deg, 以c = 75 deg为例

现在,对于与您已经找到的内圆边缘的交点,放置斜率为 c 角的线(w.r.t。正常情况下为正 x 轴)。然后,找到与外圆相应的交点。这些新的外部点和旧的内部点定义了您正在寻找的平行线段。

例子:内圆半径r = 10;外圆半径20;角度 a = 30 度和 b = 60 度。你当前的内外点是p1 = (5sqrt(3), 5), p2 = (5, 5sqrt(3)), q1 = 2p1, q2 = 2p2(这里假设内圆的圆心在原点) .计算 c = 45 度。可以使用切线找到具有该角度的线的斜率; m = 1. 定义两条直线穿过 p1 和 p2,斜率为 1;我们得到 y = x + 5(1 - sqrt(3)) 和 y = x + 5(sqrt(3) - 1)。现在我们找到这些线与外圆的交点;我将把它留作练习,但基本上只采用外圆的方程 x^2 + y^2 = 400,将其中的 y 替换为每个方程的右侧,然后求解 x。你会得到两个解决方案(一条线穿过外圈内的一个点必须在两个地方与圆相交)......选择一个在你原来的外点定义的较小线段中的那个。

这似乎是手工完成的相当乏味的事情,但实际上一旦你编写了代码,计算机就会整天为你做这些没有问题。

(如果您需要我在我的 phone 上尝试计算示例中的交点,请发表评论)。

"Pad should be 12degree" --> 如果你想要平行线,内填充角和外填充角是不同的——不是都是12°。

使用缩放填充。

给定外填充角度为double pad = ...,
像以前一样绘制第一条弧线。

draw_arc(cx, cy, r1, alpha + pad/2, alpha + da - pad/2); //STEP2

内填充角度按比例变大

double inner_pad = pad*r1/r2;
draw_negative_arc(cx, cy, r2, alpha + da - inner_pad/2, alpha + inner_pad/2); //STEP3

提示:使用 r_inner, r_outer 比使用 r2, r1 更容易混淆。


轻微:填充计算看起来相差 2 倍。

// double pad = M_PI / 360 * 12; //12 degree pad.
double pad = 2 * M_PI / 360 * 12; //12 degree pad.

对于@chux - 恢复 Monica 的请求,我将在此处发布我的最终解决方案,该解决方案在很大程度上依赖于他的 link 评论:Radius and central angle on a circular segment.

1.) 设置初始常量:

double pad = PADDING_VALUE;
double r_inner = INNER_RADIUS;
double r_outer = OUTER_RADIUS;

2.) 计算切片起点坐标(内圆):

double alpha = CURRENT_SLICE_ANGLE;
double inner_angle = alpha + pad/2; // slice will begin with pad/2 offset

double x1 = r_inner * cos(inner_angle);
double y1 = r_inner * sin(inner_angle);

3.) 现在计算上一个切片的终点坐标(也是在内圆上):

double x2 = r_inner * cos(inner_angle - pad); //easy, exactly angle-pad
double y2 = r_inner * sin(inner_angle - pad); //easy, exactly angle-pad

4.) 现在我有了当前切片开始和前一个切片结束的坐标。我想在 2 个切片之间保持恒定长度,这个长度应该正好是 (x1,y1)(x2,y2) 之间的片段长度。这是一个 right-angled 三角形 ,我正在寻找的线段长度很容易用 Pitagoras 公式:[=24= 表示]

double hyp = sqrt(pow(x1-x2,2) + pow(y1-y2,2));

5.) 这个炒作 正好是 这里的弦长 (c):

6.) 在同一维基百科页面中,Theta 可以用弦长和半径表示:

double theta = 2 * asin(hyp / 2 / r_outer);

7.) 我必须借助 thetapad:

绘制外弧
double outer_angle_correction = (pad - theta) / 2;

应用此计算结果:

这有点奇怪,因为差距有点太大了。但无论如何,这些巨大的差距只是为了演示,我把它们改回我最初的预期值后,结果是这样的:

在不使用任何近似值的情况下,所有切片之间的间隙完全平行 - 只是纯数学。甜