我是 Asymptote 的新手,需要一点帮助。我想将标签精确地放置在闭合路径的中心,例如三角形中:
unitsize(1cm);
size(4cm);
draw((0, 0) -- (0, 3) -- (3, 0) -- cycle);
label("L", (1, 1));
我在文档中搜索了计算中心点的方法,但一无所获。所以我的问题是:是否有可能找到路径凸包的中心,还是我必须手动计算?
谢谢你,
阿德里安
答案1
我碰巧有之前写过的函数可能会对你有帮助。第一个函数返回一个点数组的凸包。你可以使用@CharlesStaats 提供的函数来获取原始路径中的点数组。按原样,该convexHull
函数仅适用于分段线性路径。
path convexHull(pair[] in_pset)
{
pair[] pset = copy(in_pset);
if (pset.length == 0) { path hull; return hull; }
{ // remove duplicate points
int indexDelete = 1;
while (indexDelete > 0)
{
indexDelete = -1;
for (int i = 1; i < pset.length; ++i)
{
for (int j = 0; j < i; ++j)
{
if (pset[i] == pset[j])
{
indexDelete = i;
break;
}
}
if (indexDelete > 0) { break; }
}
if (indexDelete > 0) { pset.delete(indexDelete); }
}
}
path hull;
{ // add point at min y (and min x if tie) to hull, delete point from pset
int minIndex = 0;
for (int i = 1; i < pset.length; ++i)
{
if (pset[i].y < pset[minIndex].y ||
(pset[i].y == pset[minIndex].y && pset[i].x < pset[minIndex].x))
{
minIndex = i;
}
}
hull = pset[minIndex];
pset.delete(minIndex);
}
while (pset.length > 0)
{
{ // add next point to hull
real minAngle = 361.0;
int minAngleIndex = 0;
for (int i = 0; i < pset.length; ++i)
{
real angle = degrees(pset[i] - relpoint(hull, 1.0), false);
if (angle < minAngle)
{
minAngle = angle;
minAngleIndex = i;
}
}
hull = hull--pset[minAngleIndex];
pset.delete(minAngleIndex);
}
{ // remove points interior to current hull from pset
path tempHull = hull--cycle;
int[] deleteIndeces;
for (int i = pset.length - 1; i > -1; --i)
{
if (inside(tempHull, pset[i])) { deleteIndeces.push(i); }
}
for (int i = 0; i < deleteIndeces.length; ++i)
{
pset.delete(deleteIndeces[i]);
}
}
}
return hull--cycle;
}
现在您可以使用蒙特卡罗技术来找到凸包路径的重心 (CG),如下所示。
pair pathCG(path p, int numTestPoints)
{
int numInside = 0;
pair bl = min(p);
pair tr = max(p);
pair sumPair = (0,0);
for (int i = 0; i < numTestPoints; ++i)
{
pair testPoint = (
unitrand() * (tr.x - bl.x) + bl.x,
unitrand() * (tr.y - bl.y) + bl.y
);
if (inside(p, testPoint))
{
sumPair = sumPair + testPoint;
++numInside;
}
}
pair CG = (0,0);
if (numInside > 0) { CG = sumPair / numInside; }
return CG;
}
以下代码将使用上述函数以黑色绘制您的路径,以红色虚线绘制该路径的凸包,并以蓝点绘制凸包 CG。如上所述,convexHull 函数不支持原始路径的曲率。
unitsize(1inch);
// vertices function here
// convexHull function here
// pathCG function here
path origPath = (0,0)--(3,0){NNE}..(3,2)--(1.5,3)--(1,1)--(0,1.7)--(0.3,1)--cycle;
draw(origPath);
dot(origPath);
pair[] pset = vertices(origPath);
path hull = convexHull(pset);
draw(hull, dashed+red);
dot(Label("CG of hull"), pathCG(hull, 500), S, 6+blue);
对于您在评论中提到的标记角度,您可以使用类似以下示例中的函数angleLabel
。它将标签放置在由三个点指定的角度内,距离中点有指定的距离,并绘制一个可选箭头。
unitsize(1inch);
void angleLabel(string s, pair p1, pair p2, pair p3,
real offset = 0.5, bool drawArrow = true)
{
real angle1 = degrees(p1-p2);
real angle2 = degrees(p3-p2);
real midAngle = (angle1 + angle2) / 2.0;
pair labelPos = shift(p2)*rotate(midAngle)*(offset,0);
if (drawArrow)
{
draw(arc(p2, offset, angle1, angle2), Arrows);
}
label(s, labelPos, UnFill);
}
pair a = (2,0);
pair b = (0,0);
pair c = (-1,1);
label("$a$", a, S);
label("$b$", b, S);
label("$c$", c, N);
path p = a--b--c;
draw(p);
dot(p);
angleLabel("$B$", a, b, c, false);
angleLabel("$B$", a, b, c, 0.75);
答案2
Asymptote 甚至(目前)不提供计算凸包的函数,更不用说它的质心(我假设这就是你所说的“中心”)。如果你想编写自己的函数,你可能想假设循环路径实际上是一个封闭的、非自相交的多边形。在这种情况下,我们可以使用这个公式代表质心,你会发现以下函数很有用:
// Extract the vertices of a path (cyclic or otherwise)
pair[] vertices(path g) {
return sequence(new pair(int i) { return point(g,i); },
size(g));
}