如何反转使用 C# 中的数组构建的 SVG 曲线?

How do I reverse an SVG curve built using an array in C#?

我是 c#/svg 的新手,正在尝试转换其点存在于数组中的相对 <90 曲线:float arcArray[2,4] 并尝试将所有内容保留在我的 ArcPlot class using System 并将实际的 svg 函数放在单独的 class.

这将在视觉上生成正确的曲线,但我需要它朝相反的方向移动以附加到现有的 svg 字符串:

float [,] arcPoint = ArcPlot.arcPointsArray(StartAngle, SweepAngle, Radius, -RadiusOffset, Clockwise);
svgOut += " m " + arcPoint[0, 0] + " " + arcPoint[1, 0] + " c " + arcPoint[0, 1] + " " + arcPoint[1, 1] + " " + arcPoint[0, 2] + " " + arcPoint[1, 2] + " " + arcPoint[0, 3] + " " + arcPoint[1, 3];

这个:

float [,] arcPoint = ArcPlot.reverseArcArray(ArcPlot.arcPointsArray(StartAngle, SweepAngle, Radius, -RadiusOffset, Clockwise));
svgOut += " m " + arcPoint[0, 0] + " " + arcPoint[1, 0] + " c " + arcPoint[0, 1] + " " + arcPoint[1, 1] + " " + arcPoint[0, 2] + " " + arcPoint[1, 2] + " " + arcPoint[0, 3] + " " + arcPoint[1, 3];

使用这个函数:

public static float[,] reverseArcArray(float[,] ArcArray)
{
    float [,] arcArray = ArcArray;
    float [,] swapArray = new float [2,4];
    swapArray[0, 0] = arcArray[0, 3] - arcArray[0, 3];
    swapArray[1, 0] = arcArray[1, 3] - arcArray[1, 3];
    swapArray[0, 1] = arcArray[0, 2] - arcArray[0, 3];
    swapArray[1, 1] = arcArray[1, 2] - arcArray[1, 3];
    swapArray[0, 2] = arcArray[0, 1] - arcArray[0, 3];
    swapArray[1, 2] = arcArray[1, 1] - arcArray[1, 3];
    swapArray[0, 3] = arcArray[0, 0] - arcArray[0, 3];
    swapArray[1, 3] = arcArray[1, 0] - arcArray[1, 3];
    return swapArray;
}

在正确的位置开始曲线 (0,0),其余三个控制点很接近,但被我忽略的东西抵消了。我假设这是绝对弧和相对弧之间的区别,我遗漏了一些简单的东西,比如对实际曲线坐标的推导。

粗暴 forcing/trial 错误对我不起作用。

我第一次尝试使用 ArcPlot.arcPointsArray(StartAngle + SweepAngle, SweepAngle, Radius, -RadiusOffset, !Clockwise) 时也没有运气,这将是避免完全逆转的首选方法,但是,我再次明显遗漏了一些东西。我仍然想弄清楚反向函数,以更好地理解相对 svg。

如果有帮助,这是我用来创建弧线的实际函数:

public static float[,] arcPointsArray(double StartAngle, double SweepAngle, double Radius, double RadiusOffset = 0d,
                                      bool Clockwise = false, float XCenter = 0f, float YCenter = 0f)
{
    double radius = Radius, startAngle = StartAngle, sweepAngle = SweepAngle, radiusOffset = RadiusOffset;
    bool arcClockwise = Clockwise;
    float xCenter = XCenter, yCenter = YCenter;

    double startRadiusAngle = arcClockwise ? startAngle - (pi / 2) : startAngle + (pi / 2);
    startRadiusAngle -= Convert.ToInt32(startRadiusAngle / (pi * 2)) * (pi * 2); // mathematical overcircle check
    sweepAngle -= Convert.ToInt32(sweepAngle / (pi * 2)) * (pi * 2);

    double toCenterAngle = arcClockwise ? startAngle + (pi / 2) : startAngle - (pi / 2);
    if (toCenterAngle > (pi * 2)) toCenterAngle -= pi * 2; // functional overcircle check
    if (toCenterAngle < 0) toCenterAngle += pi * 2;
    if (XCenter == 0f) xCenter = Convert.ToSingle(Math.Cos(toCenterAngle) * radius);
    if (YCenter == 0f) yCenter = Convert.ToSingle(Math.Sin(toCenterAngle) * radius);

    radius += radiusOffset;

    float[,] arcArray = new float[2, 4];
    arcArray[0, 0] = Convert.ToSingle(xCenter + (Math.Cos(startRadiusAngle) * radius)); // relocate start point
    arcArray[1, 0] = Convert.ToSingle(yCenter + (Math.Sin(startRadiusAngle) * radius));

    double circleFraction = pi * 2 / sweepAngle;
    double bezierLength = radius * 4 / 3 * Math.Tan(pi / (2 * circleFraction));

    arcArray[0, 1] = Convert.ToSingle(arcArray[0, 0] + (Math.Cos(startAngle) * bezierLength)) - arcArray[0, 0];
    arcArray[1, 1] = Convert.ToSingle(arcArray[1, 0] + (Math.Sin(startAngle) * bezierLength)) - arcArray[1, 0];
            
    double endRadiusAngle = arcClockwise ? startRadiusAngle + sweepAngle : startRadiusAngle - sweepAngle;
    if (endRadiusAngle > (pi * 2)) endRadiusAngle -= pi * 2;
    if (endRadiusAngle < 0) endRadiusAngle += pi * 2;

    arcArray[0, 3] = Convert.ToSingle(xCenter + (Math.Cos(endRadiusAngle) * radius)) - arcArray[0, 0];
    arcArray[1, 3] = Convert.ToSingle(yCenter + (Math.Sin(endRadiusAngle) * radius)) - arcArray[1, 0];

    double endAngle = arcClockwise ? endRadiusAngle - (pi / 2) : endRadiusAngle + (pi / 2);
    if (endAngle > (pi * 2d)) endAngle -= pi * 2;
    if (endAngle < 0d) endAngle += pi * 2;
    arcArray[0, 2] = Convert.ToSingle(arcArray[0, 3] + (Math.Cos(endAngle) * bezierLength));
    arcArray[1, 2] = Convert.ToSingle(arcArray[1, 3] + (Math.Sin(endAngle) * bezierLength));

    return arcArray;
}

我在 python 和 javascript 中看到过类似的问题,但对语法或结构的理解不够,无法翻译。

我假设答案只是换位、不正确的假设或数学错误,但如果不是,伪代码将是首选,这样我可以获得概念而不是 cut/paste 解决方案。

下面的 gif 显示了我遇到的旋转问题,因为内部相对弧没有被正确翻译。我将单独处理这个问题,作为之前的尝试(它不再存在,因为我直到之后才开始使用 git )在渲染具有绝对定位的所有内容时不会出现这个问题。我遇到的实际问题是可以正确渲染内部弧线,但只能在错误的方向上渲染。当使用上面显示的反转方法或使用 arcPointsArray 将其向后绘制时,这些部分需要分别识别和连接,而不是使用循环,因为它们需要稍微不同的方法。想法是最终将绿线以均匀的距离包裹在红线中,而不考虑起始角度、方向和比例。

https://imgur.com/a/6SiItuv

为什么不直接修改对 arcPointsArray() 的调用?这样的东西有用吗?

float [,] arcPoint = ArcPlot.arcPointsArray(StartAngle + SweepAngle,
                                            -SweepAngle,
                                            Radius,
                                            -RadiusOffset,
                                            !Clockwise);

这是我最终用于反转相对三次 svg 曲线的代码:

public static float[,] reverseArcArray(float[,] ArcArray)
{
    float [,] arcArray = ArcArray;
    float [,] swapArray = new float [2,4];
    swapArray[0, 0] = 0f;
    swapArray[1, 0] = 0f;
    swapArray[0, 1] = arcArray[0, 2] - arcArray[0, 3];
    swapArray[1, 1] = arcArray[1, 2] - arcArray[1, 3];
    swapArray[0, 2] = arcArray[0, 1] - arcArray[0, 3];
    swapArray[1, 2] = arcArray[1, 1] - arcArray[1, 3];
    swapArray[0, 3] = -arcArray[0, 3];
    swapArray[1, 3] = -arcArray[1, 3];
    return swapArray;
}

我的问题是误解了第一个坐标和最后一个坐标之间的关系。如问题中所述,该功能将正确完成工作。它既会反转相对曲线,又会在反转时将绝对值转换为相对值。

因为我只处理相对曲线,所以我可以丢弃第一个坐标,因为它们始终为 0,0,并且可以根据需要用起始位置覆盖它。

Paul 的回答中得出的解决方案表明这是一个 xy 问题。重新评估我如何使用 arcPointsArray 方法消除了对 reverseArcArray 方法的需要。

我留下这个答案是为了让任何实际搜索 y 问题的人都不会被 x 解决方案所困。