如何在 Asymptote 中将标签置于封闭路径的中心?

如何在 Asymptote 中将标签置于封闭路径的中心?

我是 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)); 
}

相关内容