甜甜圈图:计算外圆坐标得到切片之间的平行间隙
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.) 我必须借助 theta
和 pad
:
绘制外弧
double outer_angle_correction = (pad - theta) / 2;
应用此计算结果:
这有点奇怪,因为差距有点太大了。但无论如何,这些巨大的差距只是为了演示,我把它们改回我最初的预期值后,结果是这样的:
在不使用任何近似值的情况下,所有切片之间的间隙完全平行 - 只是纯数学。甜
我有一个自定义的 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.) 我必须借助 theta
和 pad
:
double outer_angle_correction = (pad - theta) / 2;
应用此计算结果:
这有点奇怪,因为差距有点太大了。但无论如何,这些巨大的差距只是为了演示,我把它们改回我最初的预期值后,结果是这样的:
在不使用任何近似值的情况下,所有切片之间的间隙完全平行 - 只是纯数学。甜