如何计算圆弧的 SVG 路径

发布于 2024-11-02 17:33:48 字数 324 浏览 1 评论 0 原文

给定一个以 (200,200) 为圆心、半径为 25 的圆,如何绘制从 270 度到 135 度的圆弧以及从 270 度到 45 度的圆弧?

0 度表示位于 x 轴右侧(右侧)(表示位于 3 点钟位置) 270度表示它是12点钟位置,90度表示它是6点钟位置

更一般地说,圆的一部分的弧线的路径是什么

x, y, r, d1, d2, direction

意思

center (x,y), radius r, degree_start, degree_end, direction

Given a circle centered at (200,200), radius 25, how do I draw an arc from 270 degree to 135 degree and one that goes from 270 to 45 degree?

0 degree means it is right on the x-axis (the right side) (meaning it is 3 o' clock position)
270 degree means it is 12 o'clock position, and 90 means it is 6 o'clock position

More generally, what is a path for an arc for part of a circle with

x, y, r, d1, d2, direction

meaning

center (x,y), radius r, degree_start, degree_end, direction

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(16

番薯 2024-11-09 17:33:48

扩展 @wdebeaum 的出色答案,这里有一种生成弧形路径的方法:

function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
  var angleInRadians = (angleInDegrees-90) * Math.PI / 180.0;

  return {
    x: centerX + (radius * Math.cos(angleInRadians)),
    y: centerY + (radius * Math.sin(angleInRadians))
  };
}

function describeArc(x, y, radius, startAngle, endAngle){

    var start = polarToCartesian(x, y, radius, endAngle);
    var end = polarToCartesian(x, y, radius, startAngle);

    var largeArcFlag = endAngle - startAngle <= 180 ? "0" : "1";

    var d = [
        "M", start.x, start.y, 
        "A", radius, radius, 0, largeArcFlag, 0, end.x, end.y
    ].join(" ");

    return d;       
}

中使用

document.getElementById("arc1").setAttribute("d", describeArc(200, 400, 100, 0, 180));

在 html

<path id="arc1" fill="none" stroke="#446688" stroke-width="20" />

Live demo 和

Expanding on @wdebeaum's great answer, here's a method for generating an arced path:

function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
  var angleInRadians = (angleInDegrees-90) * Math.PI / 180.0;

  return {
    x: centerX + (radius * Math.cos(angleInRadians)),
    y: centerY + (radius * Math.sin(angleInRadians))
  };
}

function describeArc(x, y, radius, startAngle, endAngle){

    var start = polarToCartesian(x, y, radius, endAngle);
    var end = polarToCartesian(x, y, radius, startAngle);

    var largeArcFlag = endAngle - startAngle <= 180 ? "0" : "1";

    var d = [
        "M", start.x, start.y, 
        "A", radius, radius, 0, largeArcFlag, 0, end.x, end.y
    ].join(" ");

    return d;       
}

to use

document.getElementById("arc1").setAttribute("d", describeArc(200, 400, 100, 0, 180));

and in your html

<path id="arc1" fill="none" stroke="#446688" stroke-width="20" />

Live demo

月亮邮递员 2024-11-09 17:33:48

您想要使用椭圆Arc命令。不幸的是,这需要您指定起点和终点的笛卡尔坐标(x,y),而不是您拥有的极坐标(半径,角度),因此您必须做一些数学运算。这是一个应该可以工作的 JavaScript 函数(尽管我还没有测试过它),我希望它是相当不言自明的:

function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
  var angleInRadians = angleInDegrees * Math.PI / 180.0;
  var x = centerX + radius * Math.cos(angleInRadians);
  var y = centerY + radius * Math.sin(angleInRadians);
  return [x,y];
}

哪些角度对应于哪些时钟位置将取决于坐标系;只需根据需要交换和/或否定 sin/cos 项即可。

arc 命令具有以下参数:

rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y

对于第一个示例:

rx=ry=25 和 x-axis-rotation=0,因为您想要一个圆而不是一个椭圆。您可以使用上面的函数计算起始坐标(您应该M)和结束坐标(x, y),分别产生 (200, 175) 和大约 (182.322, 217.678) 。到目前为止,考虑到这些限制,实际上可以绘制四个弧线,因此两个标志选择其中之一。我猜你可能想在角度减小的方向上画一个小弧(意味着large-arc-flag=0)(意味着sweep-flag=0) )。总而言之,SVG 路径是:

M 200 175 A 25 25 0 0 0 182.322 217.678

对于第二个示例(假设您的意思是朝同一方向移动,因此是一个大弧线),SVG 路径是:

M 200 175 A 25 25 0 1 0 217.678 217.678

再次强调,我还没有测试过这些。

(编辑于 2016 年 6 月 1 日)如果您像 @clocksmith 一样想知道他们为什么选择这个 API,请查看 实现说明。他们描述了两种可能的弧参数化,“端点参数化”(他们选择的一种)和“中心参数化”(就像问题所使用的那样)。在“端点参数化”的描述中,他们说:

端点参数化的优点之一是它允许一致的路径语法,其中所有路径命令都以新的“当前点”的坐标结束。

所以基本上这是弧被视为更大路径的一部分而不是它们自己的单独对象的副作用。我想如果你的 SVG 渲染器不完整,它可以跳过任何它不知道如何渲染的路径组件,只要它知道它们需要多少个参数。或者它可能允许使用许多组件并行渲染路径的不同块。或者也许他们这样做是为了确保舍入误差不会沿着复杂路径的长度累积。

实现注释对于原始问题也很有用,因为它们有更多的数学伪代码用于在两个参数化之间进行转换(当我第一次写这个答案时我没有意识到)。

You want to use the elliptical Arc command. Unfortunately for you, this requires you to specify the Cartesian coordinates (x, y) of the start and end points rather than the polar coordinates (radius, angle) that you have, so you have to do some math. Here's a JavaScript function which should work (though I haven't tested it), and which I hope is fairly self-explanatory:

function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
  var angleInRadians = angleInDegrees * Math.PI / 180.0;
  var x = centerX + radius * Math.cos(angleInRadians);
  var y = centerY + radius * Math.sin(angleInRadians);
  return [x,y];
}

Which angles correspond to which clock positions will depend on the coordinate system; just swap and/or negate the sin/cos terms as necessary.

The arc command has these parameters:

rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y

For your first example:

rx=ry=25 and x-axis-rotation=0, since you want a circle and not an ellipse. You can compute both the starting coordinates (which you should Move to) and ending coordinates (x, y) using the function above, yielding (200, 175) and about (182.322, 217.678), respectively. Given these constraints so far, there are actually four arcs that could be drawn, so the two flags select one of them. I'm guessing you probably want to draw a small arc (meaning large-arc-flag=0), in the direction of decreasing angle (meaning sweep-flag=0). All together, the SVG path is:

M 200 175 A 25 25 0 0 0 182.322 217.678

For the second example (assuming you mean going the same direction, and thus a large arc), the SVG path is:

M 200 175 A 25 25 0 1 0 217.678 217.678

Again, I haven't tested these.

(edit 2016-06-01) If, like @clocksmith, you're wondering why they chose this API, have a look at the implementation notes. They describe two possible arc parameterizations, "endpoint parameterization" (the one they chose), and "center parameterization" (which is like what the question uses). In the description of "endpoint parameterization" they say:

One of the advantages of endpoint parameterization is that it permits a consistent path syntax in which all path commands end in the coordinates of the new "current point".

So basically it's a side-effect of arcs being considered as part of a larger path rather than their own separate object. I suppose that if your SVG renderer is incomplete it could just skip over any path components it doesn't know how to render, as long as it knows how many arguments they take. Or maybe it enables parallel rendering of different chunks of a path with many components. Or maybe they did it to make sure rounding errors didn't build up along the length of a complex path.

The implementation notes are also useful for the original question, since they have more mathematical pseudocode for converting between the two parameterizations (which I didn't realize when I first wrote this answer).

小女人ら 2024-11-09 17:33:48

我稍微修改了opsb的答案并为圆扇区提供了支持填充。

JS

function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
  var angleInRadians = (angleInDegrees-90) * Math.PI / 180.0;

  return {
    x: centerX + (radius * Math.cos(angleInRadians)),
    y: centerY + (radius * Math.sin(angleInRadians))
  };
}

function describeArc(x, y, radius, startAngle, endAngle){

    var start = polarToCartesian(x, y, radius, endAngle);
    var end = polarToCartesian(x, y, radius, startAngle);

    var arcSweep = endAngle - startAngle <= 180 ? "0" : "1";

    var d = [
        "M", start.x, start.y, 
        "A", radius, radius, 0, arcSweep, 0, end.x, end.y,
        "L", x,y,
        "L", start.x, start.y
    ].join(" ");

    return d;       
}

document.getElementById("arc1").setAttribute("d", describeArc(200, 400, 100, 0, 220));

HTML

<svg>
  <path id="arc1" fill="orange" stroke="#446688" stroke-width="0" />
</svg>

I slightly modified the answer of opsb and made in support fill for the circle sector.

JS

function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
  var angleInRadians = (angleInDegrees-90) * Math.PI / 180.0;

  return {
    x: centerX + (radius * Math.cos(angleInRadians)),
    y: centerY + (radius * Math.sin(angleInRadians))
  };
}

function describeArc(x, y, radius, startAngle, endAngle){

    var start = polarToCartesian(x, y, radius, endAngle);
    var end = polarToCartesian(x, y, radius, startAngle);

    var arcSweep = endAngle - startAngle <= 180 ? "0" : "1";

    var d = [
        "M", start.x, start.y, 
        "A", radius, radius, 0, arcSweep, 0, end.x, end.y,
        "L", x,y,
        "L", start.x, start.y
    ].join(" ");

    return d;       
}

document.getElementById("arc1").setAttribute("d", describeArc(200, 400, 100, 0, 220));

HTML

<svg>
  <path id="arc1" fill="orange" stroke="#446688" stroke-width="0" />
</svg>
柠栀 2024-11-09 17:33:48

我正在对 @opsb 的答案添加一个小扩展。

如果您想将此弧转换为切片(以允许填充),我们可以稍微修改代码:

function describeArc(x, y, radius, spread, startAngle, endAngle){
    var innerStart = polarToCartesian(x, y, radius, endAngle);
    var innerEnd = polarToCartesian(x, y, radius, startAngle);
    var outerStart = polarToCartesian(x, y, radius + spread, endAngle);
    var outerEnd = polarToCartesian(x, y, radius + spread, startAngle);

    var largeArcFlag = endAngle - startAngle <= 180 ? "0" : "1";

    var d = [
        "M", outerStart.x, outerStart.y,
        "A", radius + spread, radius + spread, 0, largeArcFlag, 0, outerEnd.x, outerEnd.y,
        "L", innerEnd.x, innerEnd.y, 
        "A", radius, radius, 0, largeArcFlag, 1, innerStart.x, innerStart.y, 
        "L", outerStart.x, outerStart.y, "Z"
    ].join(" ");

    return d;
}

function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
  var angleInRadians = (angleInDegrees-90) * Math.PI / 180.0;

  return {
    x: centerX + (radius * Math.cos(angleInRadians)),
    y: centerY + (radius * Math.sin(angleInRadians))
  };
}

var path = describeArc(150, 150, 50, 30, 0, 50)
document.getElementById("p").innerHTML = path
document.getElementById("path").setAttribute('d',path)
<p id="p">
</p>
<svg width="300" height="300" style="border:1px gray solid">
  <path id="path" fill="blue" stroke="cyan"></path>
</svg>

就这样吧!

I am adding a small expansion to @opsb's answer.

If you wanted to convert this arc into a slice (to allow for fill) we can modify the code slightly:

function describeArc(x, y, radius, spread, startAngle, endAngle){
    var innerStart = polarToCartesian(x, y, radius, endAngle);
    var innerEnd = polarToCartesian(x, y, radius, startAngle);
    var outerStart = polarToCartesian(x, y, radius + spread, endAngle);
    var outerEnd = polarToCartesian(x, y, radius + spread, startAngle);

    var largeArcFlag = endAngle - startAngle <= 180 ? "0" : "1";

    var d = [
        "M", outerStart.x, outerStart.y,
        "A", radius + spread, radius + spread, 0, largeArcFlag, 0, outerEnd.x, outerEnd.y,
        "L", innerEnd.x, innerEnd.y, 
        "A", radius, radius, 0, largeArcFlag, 1, innerStart.x, innerStart.y, 
        "L", outerStart.x, outerStart.y, "Z"
    ].join(" ");

    return d;
}

function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
  var angleInRadians = (angleInDegrees-90) * Math.PI / 180.0;

  return {
    x: centerX + (radius * Math.cos(angleInRadians)),
    y: centerY + (radius * Math.sin(angleInRadians))
  };
}

var path = describeArc(150, 150, 50, 30, 0, 50)
document.getElementById("p").innerHTML = path
document.getElementById("path").setAttribute('d',path)
<p id="p">
</p>
<svg width="300" height="300" style="border:1px gray solid">
  <path id="path" fill="blue" stroke="cyan"></path>
</svg>

and there you go!

梦里兽 2024-11-09 17:33:48

如果不必使用 arc,绘制部分圆的一个更简单的解决方案是使用 SVG lines-dasharray

将破折号数组分为两个元素,并将它们的范围缩放到所需的角度。可以使用Stroke-dashoffset调整起始角度。

看不到任何余弦。

带解释的完整示例:
https://codepen.io/mjurczyk/pen/wvBKOvP

// Simpler alternative to using SVG arcs: http://xahlee.info/js/svg_circle_arc.html
// More about dash arrays: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-dasharray
// Setup DOM elements.
const input = document.querySelector('input');
const circle = document.querySelector('circle');

// All calculations are within 'run' function.
const run = () => {
  // 1. Get angle from input field.
  let angle = parseFloat(input.value) || 0;

  // 2. Radius of SVG circle.
  const radius = 50;
  const circumference = 2 * Math.PI * radius;

  // 3. First, 1/4 of circumfence of 90 degrees. To start from top of the view,
  //    we must rotate it by 90 degrees. By default circle will start on the right.
  //    Stroke offset effectively rotates the circle.
  // 4. Second, calculate dash array. We need dash array containing only two parts -
  //    visible dash, and invisible dash.
  //    Visible dash should have length of the chosen angle. Full circle is 360 degrees,
  //    and this 360 degrees does also equal the entire circumference. We want just a part of
  //    this entire circle to be visible - (angle / 360 degrees) returns a percentage value
  //    (between 0.0 and 1.0) of how much circumference should be visible.
  //    Hence, we then multiply (angle / 360) times the entire circumference.
  const strokeOffset = (1 / 4) * circumference;
  const strokeDasharray = (angle / 360) * circumference;

  // 5. Set circle radius
  circle.setAttribute('r', 50);
  // 6. Create dash array of two elements (combined they must equal the entire circumference).
  //    First has the length of visible portion. Second, the remaining part.
  circle.setAttribute('stroke-dasharray', [
    strokeDasharray,
    circumference - strokeDasharray
  ]);
  // 7. (Optional) Rotate circle to start from the top.
  circle.setAttribute('stroke-dashoffset', strokeOffset);
}

// Run and update DOM
input.addEventListener('keyup', run);
run();
/* You can ignore this part, too */

svg,
input {
  display: block;
  margin: 2px;
}

svg {
  width: 200px;
  height: 200px;
  stroke: #000;
  stroke-width: 2px;
  fill: transparent;
}
<!-- You can ignore this part -->
<input type="number" placeholder="angle (deg)" value="90" />
<svg>
  <circle cx="100" cy="100" r="0" />
</svg>

If using arc is not obligatory, a far simpler solution to draw a part-circle is to use stroke-dasharray of SVG <circle>.

Divide dash array into two elements, and scale their range to the desired angle. Starting angle can be adjusted using stroke-dashoffset.

Not a single cosine in sight.

Full example with explanations:
https://codepen.io/mjurczyk/pen/wvBKOvP

// Simpler alternative to using SVG arcs: http://xahlee.info/js/svg_circle_arc.html
// More about dash arrays: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-dasharray
// Setup DOM elements.
const input = document.querySelector('input');
const circle = document.querySelector('circle');

// All calculations are within 'run' function.
const run = () => {
  // 1. Get angle from input field.
  let angle = parseFloat(input.value) || 0;

  // 2. Radius of SVG circle.
  const radius = 50;
  const circumference = 2 * Math.PI * radius;

  // 3. First, 1/4 of circumfence of 90 degrees. To start from top of the view,
  //    we must rotate it by 90 degrees. By default circle will start on the right.
  //    Stroke offset effectively rotates the circle.
  // 4. Second, calculate dash array. We need dash array containing only two parts -
  //    visible dash, and invisible dash.
  //    Visible dash should have length of the chosen angle. Full circle is 360 degrees,
  //    and this 360 degrees does also equal the entire circumference. We want just a part of
  //    this entire circle to be visible - (angle / 360 degrees) returns a percentage value
  //    (between 0.0 and 1.0) of how much circumference should be visible.
  //    Hence, we then multiply (angle / 360) times the entire circumference.
  const strokeOffset = (1 / 4) * circumference;
  const strokeDasharray = (angle / 360) * circumference;

  // 5. Set circle radius
  circle.setAttribute('r', 50);
  // 6. Create dash array of two elements (combined they must equal the entire circumference).
  //    First has the length of visible portion. Second, the remaining part.
  circle.setAttribute('stroke-dasharray', [
    strokeDasharray,
    circumference - strokeDasharray
  ]);
  // 7. (Optional) Rotate circle to start from the top.
  circle.setAttribute('stroke-dashoffset', strokeOffset);
}

// Run and update DOM
input.addEventListener('keyup', run);
run();
/* You can ignore this part, too */

svg,
input {
  display: block;
  margin: 2px;
}

svg {
  width: 200px;
  height: 200px;
  stroke: #000;
  stroke-width: 2px;
  fill: transparent;
}
<!-- You can ignore this part -->
<input type="number" placeholder="angle (deg)" value="90" />
<svg>
  <circle cx="100" cy="100" r="0" />
</svg>

星軌x 2024-11-09 17:33:48

opsb的答案很简洁,但中心点不准确,而且,正如Jithin指出的,如果角度是360度,那么什么也画不出来。

Jithin 修复了 360 度问题,但如果您选择小于 360 度,那么您将得到一条闭合圆弧循环的线,这不是必需的。

我修复了这个问题,并在下面的代码中添加了一些动画:

function myArc(cx, cy, radius, max){       
       var circle = document.getElementById("arc");
        var e = circle.getAttribute("d");
        var d = " M "+ (cx + radius) + " " + cy;
        var angle=0;
        window.timer = window.setInterval(
        function() {
            var radians= angle * (Math.PI / 180);  // convert degree to radians
            var x = cx + Math.cos(radians) * radius;  
            var y = cy + Math.sin(radians) * radius;
           
            d += " L "+x + " " + y;
            circle.setAttribute("d", d)
            if(angle==max)window.clearInterval(window.timer);
            angle++;
        }
      ,5)
 }     

  myArc(110, 110, 100, 360);
    
<svg xmlns="http://www.w3.org/2000/svg" style="width:220; height:220;"> 
    <path d="" id="arc" fill="none" stroke="red" stroke-width="2" />
</svg>

opsb's answers is neat, but the center point is not accurate, moreover, as Jithin noted, if the angle is 360, then nothing is drawn at all.

Jithin fixed the 360 issue, but if you selected less than 360 degree, then you'll get a line closing the arc loop, which is not required.

I fixed that, and added some animation in the code below:

function myArc(cx, cy, radius, max){       
       var circle = document.getElementById("arc");
        var e = circle.getAttribute("d");
        var d = " M "+ (cx + radius) + " " + cy;
        var angle=0;
        window.timer = window.setInterval(
        function() {
            var radians= angle * (Math.PI / 180);  // convert degree to radians
            var x = cx + Math.cos(radians) * radius;  
            var y = cy + Math.sin(radians) * radius;
           
            d += " L "+x + " " + y;
            circle.setAttribute("d", d)
            if(angle==max)window.clearInterval(window.timer);
            angle++;
        }
      ,5)
 }     

  myArc(110, 110, 100, 360);
    
<svg xmlns="http://www.w3.org/2000/svg" style="width:220; height:220;"> 
    <path d="" id="arc" fill="none" stroke="red" stroke-width="2" />
</svg>

望她远 2024-11-09 17:33:48

ES6版本:

const angleInRadians = angleInDegrees => (angleInDegrees - 90) * (Math.PI / 180.0);

const polarToCartesian = (centerX, centerY, radius, angleInDegrees) => {
    const a = angleInRadians(angleInDegrees);
    return {
        x: centerX + (radius * Math.cos(a)),
        y: centerY + (radius * Math.sin(a)),
    };
};

const arc = (x, y, radius, startAngle, endAngle) => {
    const fullCircle = endAngle - startAngle === 360;
    const start = polarToCartesian(x, y, radius, endAngle - 0.01);
    const end = polarToCartesian(x, y, radius, startAngle);
    const arcSweep = endAngle - startAngle <= 180 ? '0' : '1';

    const d = [
        'M', start.x, start.y,
        'A', radius, radius, 0, arcSweep, 0, end.x, end.y,
        fullCircle ? 'Z':''
    ].join(' ');

    return d;
};

ES6 version:

const angleInRadians = angleInDegrees => (angleInDegrees - 90) * (Math.PI / 180.0);

const polarToCartesian = (centerX, centerY, radius, angleInDegrees) => {
    const a = angleInRadians(angleInDegrees);
    return {
        x: centerX + (radius * Math.cos(a)),
        y: centerY + (radius * Math.sin(a)),
    };
};

const arc = (x, y, radius, startAngle, endAngle) => {
    const fullCircle = endAngle - startAngle === 360;
    const start = polarToCartesian(x, y, radius, endAngle - 0.01);
    const end = polarToCartesian(x, y, radius, startAngle);
    const arcSweep = endAngle - startAngle <= 180 ? '0' : '1';

    const d = [
        'M', start.x, start.y,
        'A', radius, radius, 0, arcSweep, 0, end.x, end.y,
        fullCircle ? 'Z':''
    ].join(' ');

    return d;
};
诠释孤独 2024-11-09 17:33:48

我想对 @Ahtenus 的答案发表评论,特别是 Ray Hulha 的评论,他说 codepen 没有显示任何弧线,但我的声誉不够高。

此 codepen 无法工作的原因是其 html 有错误,笔画宽度为零。

我修复了它并在此处添加了第二个示例: http://codepen.io/AnotherLinuxUser/pen/QEJmkN

html:

<svg>
    <path id="theSvgArc"/>
    <path id="theSvgArc2"/>
</svg>

相关 CSS:

svg {
    width  : 500px;
    height : 500px;
}

path {
    stroke-width : 5;
    stroke       : lime;
    fill         : #151515;
}

javascript:

document.getElementById("theSvgArc").setAttribute("d", describeArc(150, 150, 100, 0, 180));
document.getElementById("theSvgArc2").setAttribute("d", describeArc(300, 150, 100, 45, 190));

I wanted to comment on @Ahtenus answer, specifically on Ray Hulha comment saying the codepen does not show any arc, but my reputation is not high enough.

The reason for this codepen not working is that its html is faulty with a stroke-width of zero.

I fixed it and added a second example here : http://codepen.io/AnotherLinuxUser/pen/QEJmkN.

The html :

<svg>
    <path id="theSvgArc"/>
    <path id="theSvgArc2"/>
</svg>

The relevant CSS :

svg {
    width  : 500px;
    height : 500px;
}

path {
    stroke-width : 5;
    stroke       : lime;
    fill         : #151515;
}

The javascript :

document.getElementById("theSvgArc").setAttribute("d", describeArc(150, 150, 100, 0, 180));
document.getElementById("theSvgArc2").setAttribute("d", describeArc(300, 150, 100, 45, 190));
掩于岁月 2024-11-09 17:33:48

一张图片和一些 Python

只是为了更好地澄清并提供另一种解决方案。 Arc [A< /code>] 命令使用当前位置作为起点,因此您必须使用 Moveto 首先是 [M] 命令。

那么 Arc 的参数如下:

rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, xf, yf

如果我们定义以下 svg 文件:

<svg viewBox="0 0 500px 500px">
    <path fill="red" d="
    M 100 100
    A 40 40 0 0 0 180 100
    Z"/> 
</svg>

输入图片这里的描述

您将使用M设置起点,使用的参数xfyf设置终点一个。

我们正在寻找圆,因此我们将 rx 设置为 ry 这样做基本上现在它会尝试找到所有相交的半径为 rx 的圆起点和终点。

import numpy as np

def write_svgarc(xcenter,ycenter,r,startangle,endangle,output='arc.svg'):
    if startangle > endangle: 
        raise ValueError("startangle must be smaller than endangle")
    
    if endangle - startangle < 360:
        large_arc_flag = 0
        radiansconversion = np.pi/180.
        xstartpoint = xcenter + r*np.cos(startangle*radiansconversion)
        ystartpoint = ycenter - r*np.sin(startangle*radiansconversion)
        xendpoint = xcenter + r*np.cos(endangle*radiansconversion)
        yendpoint = ycenter - r*np.sin(endangle*radiansconversion)
        #If we want to plot angles larger than 180 degrees we need this
        if endangle - startangle > 180: large_arc_flag = 1
        with open(output,'a') as f:
            f.write(r"""<path d=" """)
            f.write("M %s %s" %(xstartpoint,ystartpoint))
            f.write("A %s %s 0 %s 0 %s %s" 
                    %(r,r,large_arc_flag,xendpoint,yendpoint))
            f.write("L %s %s" %(xcenter,ycenter))
            f.write(r"""Z"/>""" )
    
    else:
        with open(output,'a') as f:
            f.write(r"""<circle cx="%s" cy="%s" r="%s"/>"""
                    %(xcenter,ycenter,r))

您可以在这篇文章中获得更详细的解释是我写的。

An image and some Python

Just to clarify better and offer another solution. Arc [A] command use the current position as a starting point so you have to use Moveto [M] command first.

Then the parameters of Arc are the following:

rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, xf, yf

If we define for example the following svg file:

<svg viewBox="0 0 500px 500px">
    <path fill="red" d="
    M 100 100
    A 40 40 0 0 0 180 100
    Z"/> 
</svg>

enter image description here

You will set the starting point with M the ending point with the parameters xf and yf of A.

We are looking for circles so we set rx equal to ry doing so basically now it will try to find all the circle of radius rx that intersect the starting and end point.

import numpy as np

def write_svgarc(xcenter,ycenter,r,startangle,endangle,output='arc.svg'):
    if startangle > endangle: 
        raise ValueError("startangle must be smaller than endangle")
    
    if endangle - startangle < 360:
        large_arc_flag = 0
        radiansconversion = np.pi/180.
        xstartpoint = xcenter + r*np.cos(startangle*radiansconversion)
        ystartpoint = ycenter - r*np.sin(startangle*radiansconversion)
        xendpoint = xcenter + r*np.cos(endangle*radiansconversion)
        yendpoint = ycenter - r*np.sin(endangle*radiansconversion)
        #If we want to plot angles larger than 180 degrees we need this
        if endangle - startangle > 180: large_arc_flag = 1
        with open(output,'a') as f:
            f.write(r"""<path d=" """)
            f.write("M %s %s" %(xstartpoint,ystartpoint))
            f.write("A %s %s 0 %s 0 %s %s" 
                    %(r,r,large_arc_flag,xendpoint,yendpoint))
            f.write("L %s %s" %(xcenter,ycenter))
            f.write(r"""Z"/>""" )
    
    else:
        with open(output,'a') as f:
            f.write(r"""<circle cx="%s" cy="%s" r="%s"/>"""
                    %(xcenter,ycenter,r))

You can have a more detailed explanation in this post that I wrote.

鲜血染红嫁衣 2024-11-09 17:33:48

wdebeaum 的原始 PolarToCartesian 函数是正确的:

var angleInRadians = angleInDegrees * Math.PI / 180.0;

使用以下方式反转起点和终点:

var start = polarToCartesian(x, y, radius, endAngle);
var end = polarToCartesian(x, y, radius, startAngle);

(对我而言)令人困惑,因为这会反转扫描标志。使用:

var start = polarToCartesian(x, y, radius, startAngle);
var end = polarToCartesian(x, y, radius, endAngle);

使用sweep-flag =“0”绘制“正常”逆时针弧线,
我认为这更直接。
请参阅 https://developer.mozilla.org/en-US/文档/Web/SVG/教程/路径

The orginal polarToCartesian function by wdebeaum is correct:

var angleInRadians = angleInDegrees * Math.PI / 180.0;

Reversing of start and end points by using:

var start = polarToCartesian(x, y, radius, endAngle);
var end = polarToCartesian(x, y, radius, startAngle);

Is confusing (to me) because this will reverse the sweep-flag. Using:

var start = polarToCartesian(x, y, radius, startAngle);
var end = polarToCartesian(x, y, radius, endAngle);

with the sweep-flag = "0" draws "normal" counter-clock-wise arcs,
which I think is more straight forward.
See https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths

梦一生花开无言 2024-11-09 17:33:48

基于所选答案的 ReactJS 组件:

import React from 'react';

const polarToCartesian = (centerX, centerY, radius, angleInDegrees) => {
    const angleInRadians = (angleInDegrees - 90) * Math.PI / 180.0;

    return {
        x: centerX + (radius * Math.cos(angleInRadians)),
        y: centerY + (radius * Math.sin(angleInRadians))
    };
};

const describeSlice = (x, y, radius, startAngle, endAngle) => {

    const start = polarToCartesian(x, y, radius, endAngle);
    const end = polarToCartesian(x, y, radius, startAngle);

    const largeArcFlag = endAngle - startAngle <= 180 ? "0" : "1";

    const d = [
        "M", 0, 0, start.x, start.y,
        "A", radius, radius, 0, largeArcFlag, 0, end.x, end.y
    ].join(" ");

    return d;
};

const path = (degrees = 90, radius = 10) => {
    return describeSlice(0, 0, radius, 0, degrees) + 'Z';
};

export const Arc = (props) => <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 300 300">
    <g transform="translate(150,150)" stroke="#000" strokeWidth="2">
        <path d={path(props.degrees, props.radius)} fill="#333"/>
    </g>

</svg>;

export default Arc;

ReactJS component based on the selected answer:

import React from 'react';

const polarToCartesian = (centerX, centerY, radius, angleInDegrees) => {
    const angleInRadians = (angleInDegrees - 90) * Math.PI / 180.0;

    return {
        x: centerX + (radius * Math.cos(angleInRadians)),
        y: centerY + (radius * Math.sin(angleInRadians))
    };
};

const describeSlice = (x, y, radius, startAngle, endAngle) => {

    const start = polarToCartesian(x, y, radius, endAngle);
    const end = polarToCartesian(x, y, radius, startAngle);

    const largeArcFlag = endAngle - startAngle <= 180 ? "0" : "1";

    const d = [
        "M", 0, 0, start.x, start.y,
        "A", radius, radius, 0, largeArcFlag, 0, end.x, end.y
    ].join(" ");

    return d;
};

const path = (degrees = 90, radius = 10) => {
    return describeSlice(0, 0, radius, 0, degrees) + 'Z';
};

export const Arc = (props) => <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 300 300">
    <g transform="translate(150,150)" stroke="#000" strokeWidth="2">
        <path d={path(props.degrees, props.radius)} fill="#333"/>
    </g>

</svg>;

export default Arc;
无名指的心愿 2024-11-09 17:33:48

对@opsb 的答案稍作修改。我们不能用这种方法画一个完整的圆。即如果我们给出 (0, 360) 它根本不会绘制任何东西。因此,做了一些小小的修改来解决这个问题。显示有时达到 100% 的分数可能很有用。

function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
  var angleInRadians = (angleInDegrees-90) * Math.PI / 180.0;

  return {
    x: centerX + (radius * Math.cos(angleInRadians)),
    y: centerY + (radius * Math.sin(angleInRadians))
  };
}

function describeArc(x, y, radius, startAngle, endAngle){

    var endAngleOriginal = endAngle;
    if(endAngleOriginal - startAngle === 360){
        endAngle = 359;
    }

    var start = polarToCartesian(x, y, radius, endAngle);
    var end = polarToCartesian(x, y, radius, startAngle);

    var arcSweep = endAngle - startAngle <= 180 ? "0" : "1";

    if(endAngleOriginal - startAngle === 360){
        var d = [
              "M", start.x, start.y, 
              "A", radius, radius, 0, arcSweep, 0, end.x, end.y, "z"
        ].join(" ");
    }
    else{
      var d = [
          "M", start.x, start.y, 
          "A", radius, radius, 0, arcSweep, 0, end.x, end.y
      ].join(" ");
    }

    return d;       
}

document.getElementById("arc1").setAttribute("d", describeArc(120, 120, 100, 0, 359));

A slight modification to @opsb's answer. We cant draw a full circle with this method. ie If we give (0, 360) it will not draw anything at all. So a slight modification made to fix this. It could be useful to display scores that sometimes reach 100%.

function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
  var angleInRadians = (angleInDegrees-90) * Math.PI / 180.0;

  return {
    x: centerX + (radius * Math.cos(angleInRadians)),
    y: centerY + (radius * Math.sin(angleInRadians))
  };
}

function describeArc(x, y, radius, startAngle, endAngle){

    var endAngleOriginal = endAngle;
    if(endAngleOriginal - startAngle === 360){
        endAngle = 359;
    }

    var start = polarToCartesian(x, y, radius, endAngle);
    var end = polarToCartesian(x, y, radius, startAngle);

    var arcSweep = endAngle - startAngle <= 180 ? "0" : "1";

    if(endAngleOriginal - startAngle === 360){
        var d = [
              "M", start.x, start.y, 
              "A", radius, radius, 0, arcSweep, 0, end.x, end.y, "z"
        ].join(" ");
    }
    else{
      var d = [
          "M", start.x, start.y, 
          "A", radius, radius, 0, arcSweep, 0, end.x, end.y
      ].join(" ");
    }

    return d;       
}

document.getElementById("arc1").setAttribute("d", describeArc(120, 120, 100, 0, 359));
千年*琉璃梦 2024-11-09 17:33:48

我会使用其他答案中的代码,它们似乎都是相互复制的,但我会将起点设为起始角度的函数,将终点设为结束角度的函数。

我将通过使用绝对值使大弧标志与顺序无关,并通过对 360 度进行模运算使角度与数值大小无关。

var start = polarToCartesian(x, y, radius, startAngle);
var end = polarToCartesian(x, y, radius,   endAngle);

largeArcFlag = Math.abs((endAngle - startAngle) % 360) <= 180 ? "0" : "1";
clockwiseFlag = (endAngle > startAngle) ? "1" : "0";

var d = [
    "M", start.x, start.y, 
    "A", radius, radius, 0, largeArcFlag, clockwiseFlag, end.x, end.y
  ].join(" ");

向威利道歉;我还没读到最后,发现他也发现了同样的事情。如果您喜欢我的帖子,请为他点赞!

I'd use the code from other answers, where they all appear to be copying from each other, but I would make the start point a function of the start angle and the end point a function of the end angle.

I would make the large arc flag independent of the order by using absolute value, and make the angles independent of numerical magnitude by working modulo 360 degrees.

var start = polarToCartesian(x, y, radius, startAngle);
var end = polarToCartesian(x, y, radius,   endAngle);

largeArcFlag = Math.abs((endAngle - startAngle) % 360) <= 180 ? "0" : "1";
clockwiseFlag = (endAngle > startAngle) ? "1" : "0";

var d = [
    "M", start.x, start.y, 
    "A", radius, radius, 0, largeArcFlag, clockwiseFlag, end.x, end.y
  ].join(" ");

Apologies to Wiley; I hadn't read to the end and see he has spotted the same thing. If you like my post, upvote his instead!

寒冷纷飞旳雪 2024-11-09 17:33:48

可以使用我为上面的答案所做的JSFiddle代码:

https://jsfiddle.net/tyw6nfee/

您 需要做的是更改最后一行 console.log 代码并为其提供您自己的参数:

  console.log(describeArc(255,255,220,30,180));
  console.log(describeArc(CenterX,CenterY,Radius,startAngle,EndAngle))

you can use JSFiddle code i made for answer above:

https://jsfiddle.net/tyw6nfee/

all you need to do is change last line console.log code and give it your own parameter:

  console.log(describeArc(255,255,220,30,180));
  console.log(describeArc(CenterX,CenterY,Radius,startAngle,EndAngle))
一笑百媚生 2024-11-09 17:33:48

PHP 有人吗?

将接受的答案转换为 PHP 代码。帮助在服务器上生成弧线。

function polarToCartesian($centerX, $centerY, $radius, $angleInDegrees) {
   $angleInRadians = ($angleInDegrees-90) * pi() / 180.0;

  return array(
    "x"=> $centerX + ($radius * cos($angleInRadians)),
    "y"=> $centerY + ($radius * sin($angleInRadians)),
  );
}

function describeArc($x, $y, $radius, $startAngle, $endAngle){

     $start = polarToCartesian($x, $y, $radius, $endAngle);
     $end = polarToCartesian($x, $y, $radius, $startAngle);

     $largeArcFlag = $endAngle - $startAngle <= 180 ? "0" : "1";

     $d = implode(" ", array(
        "M", $start["x"], $start["y"], 
        "A", $radius, $radius, 0, $largeArcFlag, 0, $end["x"], $end["y"]));

    return $d;       
}
<svg>
    <path fill="none" stroke="#446688" stroke-width="20" d="<?= describeArc(150, 150, 100, 0, 30) ?>" />
</svg>

PHP Anyone?

Converted the accepted answer to PHP code. Helps in generating the arc on the server.

function polarToCartesian($centerX, $centerY, $radius, $angleInDegrees) {
   $angleInRadians = ($angleInDegrees-90) * pi() / 180.0;

  return array(
    "x"=> $centerX + ($radius * cos($angleInRadians)),
    "y"=> $centerY + ($radius * sin($angleInRadians)),
  );
}

function describeArc($x, $y, $radius, $startAngle, $endAngle){

     $start = polarToCartesian($x, $y, $radius, $endAngle);
     $end = polarToCartesian($x, $y, $radius, $startAngle);

     $largeArcFlag = $endAngle - $startAngle <= 180 ? "0" : "1";

     $d = implode(" ", array(
        "M", $start["x"], $start["y"], 
        "A", $radius, $radius, 0, $largeArcFlag, 0, $end["x"], $end["y"]));

    return $d;       
}
<svg>
    <path fill="none" stroke="#446688" stroke-width="20" d="<?= describeArc(150, 150, 100, 0, 30) ?>" />
</svg>
鸢与 2024-11-09 17:33:48

以下是一个变体,0 位于北位置,并且在 SVG 坐标系中顺时针扫描角度(即左上角的 0,0)。它生成一个可分配给 img.src 的 SVG 数据 URI。它更新了largeArcFlag 和sweepFlag 的计算。

function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
    const angleInRadians = -Math.PI/2 + angleInDegrees * Math.PI / 180.0;
    return {
        x: centerX + radius * Math.cos(angleInRadians),
        y: centerY + radius * Math.sin(angleInRadians)
    };
}
function svgArc(radius, startAngle, endAngle) {
    const start = polarToCartesian(radius, radius, radius, startAngle);
    const end = polarToCartesian(radius, radius, radius, endAngle);
    const largeArcFlag = endAngle - startAngle > 180 ? 1 : 0
    const sweepFlag = endAngle >= startAngle ? 1 : 0;
    return `<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 ${radius*2} ${radius*2}">
<path stroke="black" stroke-width="1" fill="none" d="
M ${start.x} ${start.y}
A ${radius} ${radius} 0 ${largeArcFlag} ${sweepFlag} ${end.x} ${end.y}"/>
</svg>`;
}
function svgArcUri(radius, startAngle, endAngle) {
    return `data:image/svg+xml;utf8,${svgArc(radius, startAngle, endAngle)}`;
}
document.getElementById("myimg").src = svgArcUri(75, 0, 270);
<img id="myimg" width="150" height="150" />

The following is a variation that has 0 in the north position and the angle sweep clockwise in SVG coordinate system (i.e. 0,0 on the top-left corner). It generates an SVG data-URI which can be assigned to an img.src. It has updated calculations for largeArcFlag and sweepFlag.

function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
    const angleInRadians = -Math.PI/2 + angleInDegrees * Math.PI / 180.0;
    return {
        x: centerX + radius * Math.cos(angleInRadians),
        y: centerY + radius * Math.sin(angleInRadians)
    };
}
function svgArc(radius, startAngle, endAngle) {
    const start = polarToCartesian(radius, radius, radius, startAngle);
    const end = polarToCartesian(radius, radius, radius, endAngle);
    const largeArcFlag = endAngle - startAngle > 180 ? 1 : 0
    const sweepFlag = endAngle >= startAngle ? 1 : 0;
    return `<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 ${radius*2} ${radius*2}">
<path stroke="black" stroke-width="1" fill="none" d="
M ${start.x} ${start.y}
A ${radius} ${radius} 0 ${largeArcFlag} ${sweepFlag} ${end.x} ${end.y}"/>
</svg>`;
}
function svgArcUri(radius, startAngle, endAngle) {
    return `data:image/svg+xml;utf8,${svgArc(radius, startAngle, endAngle)}`;
}
document.getElementById("myimg").src = svgArcUri(75, 0, 270);
<img id="myimg" width="150" height="150" />

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文