[svg] 원호의 호에 대한 SVG 경로를 계산하는 방법

반지름 25 인 (200,200)을 중심으로 한 원이 주어지면 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



답변

@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" />

라이브 데모


답변

elliptical 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)을 산출 할 수 있습니다. 지금까지 이러한 제약 조건이 주어지면 실제로 그릴 수있는 4 개의 호가 있으므로 두 플래그가 그 중 하나를 선택합니다. large-arc-flag각도가 감소하는 방향 ( = 0) 으로 작은 호 ( = 0) 를 그리려고 할 것입니다 sweep-flag. 모두 함께 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

다시, 나는 이것을 테스트하지 않았습니다.

@clocksmith와 같이 왜 그들이이 API를 선택했는지 궁금하다면 구현 노트를 살펴보십시오 . 여기에는 두 가지 가능한 아크 매개 변수화, “종료점 매개 변수화”(선택한 것) 및 “중심 매개 변수화”(질문이 사용하는 것과 유사 함)가 설명되어 있습니다. “엔드 포인트 매개 변수화”에 대한 설명에서 다음과 같이 말합니다.

엔드 포인트 매개 변수화의 장점 중 하나는 모든 경로 명령이 새로운 “현재 점”의 좌표로 끝나는 일관된 경로 구문을 허용한다는 것입니다.

따라서 기본적으로 아크는 별도의 객체가 아닌 더 큰 경로의 일부로 간주되는 아크의 부작용입니다. SVG 렌더러가 불완전하면 경로 구성 요소를 건너 뛸 수 있다고 가정합니다. 어떤 인수가 필요한지 알고있는 한 렌더링 방법을 모릅니다. 또는 많은 구성 요소가있는 경로의 다른 청크를 병렬 렌더링 할 수 있습니다. 또는 복잡한 경로의 길이에 따라 반올림 오류가 발생하지 않도록하기 위해 수행했을 수도 있습니다.

구현 노트는 두 개의 매개 변수화 사이에서 변환하기 위해 더 수학적 의사 코드가 있기 때문에 원래 질문에 유용합니다 (처음 에이 답변을 작성할 때 알지 못했습니다).


답변

opsb의 답변을 약간 수정 하고 서클 섹터에 대한 지원을 작성했습니다.
http://codepen.io/anon/pen/AkoGx

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>


답변

이것은 오래된 질문이지만 코드가 유용하다는 것을 알았고 3 분의 생각을 절약했습니다. 🙂 @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>

그리고 당신은 간다!


답변

@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>


답변

@Ahtenus의 답변, 특히 Ray Hulha의 의견에 대해 언급하고 싶었습니다.

이 코드 펜이 작동하지 않는 이유는 HTML에 획 너비가 0이므로 결함이 있기 때문입니다.

나는 그것을 고치고 여기에 두 번째 예를 추가했다 : 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;
}

자바 스크립트 :

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


답변

이미지와 파이썬

더 명확하게하고 다른 솔루션을 제공하십시오. Arc[ A] 명령은 현재 위치를 시작점으로 사용하므로 먼저 Moveto[M] 명령 을 사용해야 합니다.

그런 다음의 매개 변수 Arc는 다음과 같습니다.

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

예를 들어 다음 svg 파일을 정의하면 :

<svg viewBox="0 0 500 500">
    <path fill="red" d="
    M 250 250
    A 100 100 0 0 0 450 250
    Z"/>
</svg>

여기에 이미지 설명을 입력하십시오

M매개 변수 xfyf의 시작점으로 시작점을 설정합니다 A.

우리는 원을 찾고 있으므로 기본적으로 그렇게하는 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))

내가 쓴 이 게시물 에서 더 자세한 설명을 할 수 있습니다 .