SVG "Path" 에 대한 정리

조경석·2023년 1월 13일
1
post-thumbnail

썸네일 출처 : https://www.xchangetraining.co.uk/blog/illustration-with-bezier-curves/

지난 글에 이어 MDN의 SVG 튜토리얼을 읽던 중,
계속 의문점이었던 <path> 요소에 대한 정리가 꽤 중요한 부분인것 같아서 정리해보았다.

Polyline/Polygon vs Path

SVG의 도형 요소 중에는 다각선과 다각형을 그리기 위한 <polyline>과 <polygon>요소가 있다.
하지만, UI 디자인 툴에서 추출한 SVG에서는 해당 요소를 사용한 경우를 찾을 수 없는데 그 이유는 무엇일까?

아래 SVG 마크업의 두 요소는 같은 형태의 다각형이다.

<polygon points="60 110, 65 120, 70 115, 75 115, 75 125, 85 140, 90 135"/>
<path d="m60 110 5 10 5-5h5v10l10 15 5-5z"/>

딱 봐도 특성값이 다른데, 그 차이를 알아보자.

points
<polyline>과 <polygon>은 도형의 좌표값으로 points를 사용한다.
points는 x, y의 절대 좌표 한 쌍의 배열을 값으로 가진다.
익숙한 2차원 좌표계를 따르므로, 사람이 보기에도 도형의 형태를 추측 가능한 직관적인 형태이다.
(일반적으로 좌표쌍 배열은 쉼표로 구분되어 있지만, 사실 EOL, 줄바꿈이나 공백으로만 구분되어 있어도 해석에 문제가 생기지는 않는다.)

d
path에서 사용하는 d는 딱 봐도 좌표쌍의 배열은 아니고, m,h,v,z와 같은 문자나 -가 섞인 숫자로 구성되고, 공백으로 구분된 배열이다.
SVG를 압축해보면 d 특성값 배열에서 소수점이 줄어들거나, 배열 자체가 줄어드는 것을 본 적은 있겠지만 그 패턴이나 원리를 직관적으로 파악할 수 없었을 것이다.

결론부터 말하자면, d는 도형을 그리기 위한 여러개의 대소문자를 구분하는 명령어좌표값과 파라미터로 이루어진 명령의 나열이다.

Path의 d 명령어

명령어의 대소문자

d의 모든 명령은 한 글자의 알파벳 형태다.
위에 쓴대로 명령어는 대소문자를 구분하는데,
대/소문자에 따라 이번 명령의 좌표값 파라미터와 이후 새로 명령어가 명시되기 전까지의 좌표값 파라미터에 대한 절대/상대 좌표 여부를 설정한다.
예제에서는 "m", 즉 소문자로 시작하는데, 이 명령어 뒤에 오는 좌표값은 모두 상대 좌표라는 의미이다.

명령어와 좌표값, 파라미터 구분

이제 알파벳과 숫자의 구분은 알겠는데, 예제를 보면 5-5h5v10l10 형식으로 명령의 구분 없이 알파벳, 특수문자, 숫자가 난잡하게 뒤섞여있다.
이는 사실 압축을 위해 최대한 축약된 형태로, 명령어 앞뒤의 공백과 음수 좌표값 표시를 위한 - 앞의 공백을 생략한 것이다.
즉 명령어와 좌표값, 파라미터 배열은 반드시 쉼표나 공백으로 구분되어야 할 필요가 없으며, 명령어의 특징에 따라 알아서 해석한다.

즉 예제의 path를 알아보기 쉽게 작성하면 아래와 같다.

<path d="
	m 60 110
      5 10
      5 -5
    h 5 
    v 10
    l 10 15
      5 -5
    z
"/>

직선(Line) 명령

M (Move to)

위 예제 마크업의 d는 "m60 110"으로 시작한다.
이는 예제 <polygon>의 첫번째 좌표쌍과 같은데,
말 그대로 커서를 뒤의 좌표쌍으로 옮겨 도형을 그리기 시작하라는 명령어다.
시작 명령어이므로, 다시 한번 M 명령어를 사용한다고 해서 이전 점에서 해당 좌표까지의 선을 그리지 않고 잘못 작성된 도형으로 무시한다.
즉 일반적으로 d 특성값의 첫번째 명령어로 사용되며, 이후 좌표값의 절대/상대 여부를 설정하기 위해 대/소문자를 구분하여 사용한다.

L (Line to)

시작 명령어인 m과 달리 이전 점에서 새로운 좌표쌍까지의 직선을 그리는 명령어다.
일반적으로 L x좌표 y좌표 형태로 사용하는데, 위 예제의 두번째, 세번째 명령은 L 없이 좌표쌍만 표시되어 있다.
이는 M은 기본 좌표쌍만을 파라미터로 가지는 시작 명령이므로, 이후에 명령어 없이 표시된 좌표쌍은 L 명령으로 해석하고, 앞 명령의 절대/상대 좌표 기준을 따르기 때문이다.
즉 M을 제외하고, 다른 명령어와 파라미터 직후의 직선을 그리는 경우에만 L을 명시하면 된다.

H, V (Horizon to, Vertical to)

예제에서도 L과는 달리 하나의 좌표값만을 사용하는데, 이름에서 알 수 있듯이 수직, 수평으로 이어진 직선을 그리는 명령어다.

Z (Close Path)

d 값의 마지막에 파라미터 없이 표시되는것에서 유추 가능하지만, path를 시작한 M명령어 좌표까지의 직선을 긋는 명령어다.
혼자 Close의 C가 아닌 알파벳 Z를 명령어로 사용하는데, 이는 아래서 정리할 Curve와 혼동되기 때문으로 보인다.
만약 닫힌 도형 여럿이 하나의 path 안에 포함되는 경우(Illustrator의 컴파운드 패스처럼) 도형을 구분하기 위해서 d 속성값 중간에 사용되기도 하며, 당연히 그 뒤에 M 명령어가 붙는다.

곡선(Curve) 명령

연결된 선분의 집합인 Polygon, Polyline과 달리 Path는 선분뿐 아니라 곡선까지도 포함할 수 있는 넓은 개념이기도 하다.
직선에 비해서는 복잡하지만, d 속성값 텍스트에서 도형의 형태를 전부 이해하기 위해 정리해보자.

C (Cubic Bézier)

그래픽 툴을 사용해봤다면 익숙할 3차 베지어 곡선을 사용하는 명령으로,
3차 베지어 곡선은 선의 양쪽 끝점 AA, BB에 한개씩의 제어점 A1A^1, B1B^1을 가지는 곡선이다.
출처 : https://developer.mozilla.org/ko/docs/Web/SVG/Tutorial/Paths#베지어 곡선
여기서는 베지어 커브에 대한 이 이상의 설명은 생략하고 명령어 형태 위주로 정리해보자.

C x1 y1 x2 y2 x y

간단하다. 베지어 커브 명령 이전의 좌표가 시작점 AA이고 x1 y1은 제어점 A1A^1의 좌표, x2 y2는 제어점 B1B^1의 좌표, 마지막 x y가 끝점BB이다.

S (Smooth cubic Bézier)

3차 베지어 곡선은 그 특성상 한 선의 끝 제어점을 다음 선의 첫 제어점과 대칭으로 배치해 부드럽게 이어지는 곡선을 그리기도 한다.
출처 : https://developer.mozilla.org/ko/docs/Web/SVG/Tutorial/Paths#베지어 곡선
이 경우에는 C나 S와 같은 3차 베지어 곡선 명령어 이후에 S 명령어를 사용하며, 끝점과 그 제어점만을 파라미터로 표기한다.

S x2 y2 x y

만약 S 명령어 앞에 3차 베지어 곡선이 없다면, 아래의 2차 베지어 곡선으로 해석한다.

Q (Quadratic Bézier)

2차 베지어 곡선은 단순히 하나의 제어점이 시작점과 끝점 모두의 방향을 결정하는 곡선이다.

S x2 y2 x y

S 명령어와 동일하게, 하나의 제어점과 끝점만을 파라미터로 표기한다.

T (Smooth quadratic Bézier)

2차 베지어 곡선 역시 부드러운 곡선을 위한 명령어가 있으며, 명령어를 알파벳 T로 사용하는 이유는 단순히 S 다음 알파벳인것으로 보인다.

T x y

하나의 좌표만을 가지는 특성상, 만약 T 명령어 앞에 베지어 곡선이 없는 경우 단순한 직선을 그린다.

S 명령어와 T 명령어의 특성상, 하나의 제어점 패턴을 계속 사용하는 파형 곡선의 경우 하나의 제어점만으로 복잡한 곡선을 표현할 수 있다.

A (Arc Curve)

원호의 경우 파라미터도 복잡하고 직관성도 떨어져 그래픽 요소에는 잘 사용하지 않지만, 다른 그래픽스와 다른 독특한 규칙이 있다.
원호는 그 특성상 시작점 AA, 끝점 BB타원의 x,y축 반지름과 그 회전각 θθ이 주어질 경우, (두 점의 거리가 타원보다 크지 않을 때) 4가지 형태의 원호를 그릴 수 있다.

그렇기 때문에 명령어 A는 7개의 파라미터를 사용한다.

 A rx ry x축-회전각 큰-호-플래그 쓸기-방향-플래그 x y

딱 봐도 끔찍한데, 일단 첫 rx, ry는 타원의 x,y 반지름이고,
x축-회전각은 X축을 기준으로 타원의 회전 각도(°)값,
마지막 x,y는 원호가 끝나는 지점의 좌표이다.
원호 명령 이전의 좌표가 시작점이고, 타원의 두 반지름끝점, 그리고 타원의 회전각이 정해졌으므로, 원호를 선택하기 위해 나머지 두 파라미터를 사용한다.
출처 : https://developer.mozilla.org/ko/docs/Web/SVG/Tutorial/Paths#원호
이미지가 좀 알아보기 어려운데, 쉽게 표현하자면 아래와 같다.
큰-호-플래그(large-arc-flag)
0 - 그려진 원호의 쌍 중 짧은 호를 사용
1 - 그려진 원호의 쌍 중 긴 호를 사용
쓸기-방향-플래그(sweep-flag)
0 - 원호의 꼭짓점 방향의 반대쪽 호를 사용
1 - 원호의 꼭짓점 방향의 호를 사용

마지막으로, 원호는 그 특성상 베지어 곡선으로는 그릴 수 없다.
베지어 곡선으로 그린 원호는 정확한 원호가 아닌 원호에 근사화한 곡선이기 때문인데, 일단은 그렇기 때문에 A 명령어가 존재한다고만 생각하면 될 것 같다.

복잡하긴 하지만 원호까지 정리한 이유는, 모든 곡선 명령어의 파라미터는 끝점 좌표로 끝난다는 통일성을 설명하기 위해서다.
이로 인해 곡선이 아무리 복잡하더라도 명령어 알파벳의 앞뒤 값으로 선의 시작점, 끝점을 직관적으로 이해할 수 있다.

마무리

그래픽 툴을 많이 사용해봤다면 위 내용 이해에 어려움은 없을 것이다.
글로 정리해보면서 꽤 많은 부분을 체득하게 되었는데, MDN 튜토리얼에 꽤 공감되는 내용이 있어 인용하고 마무리하겠다.

...그렇기에 SVG를 그릴 때 패스에 대해 이해하는 것은 매우 중요하다고 할 수 있다. 복잡한 패스를 XML 편집기 또는 일반적인 텍스트 에디터로 그리는 것은 권장하지 않지만, SVG가 표시될 때 문제점을 찾고 고치는 데는 충분히 도움이 될 것이다.
MDN 개발자를 위한 웹 기술 > SVG 튜토리얼 > 패스 서문

0개의 댓글