고정된 도형을 경로를 따라 이동시켜 브로드 엣지 펜 획 만드는 방법
브로드 엣지 펜 획을 만들려면 폭이 좁은 직사각형 펜촉을 정의하고, 각도를 일정하게 유지한 채 경로를 따라 이동시키세요. 획은 직사각형이 차지하는 모든 위치의 합집합입니다. 펜촉 자체의 크기는 변하지 않지만, 이동 방향에 따라 획의 두께는 달라집니다.
이 가이드에서는 벡터 그림이나 캐릭터의 강조 요소에 사용할 기하 구조를 만드는 방법을 설명합니다. 펜촉을 정의하고, 폴리라인을 따라 연속적으로 이동시키며, 완전한 Python 프로그램으로 SVG를 생성합니다. 핵심 구성 방식은 브러시를 개별 도장으로 시뮬레이션할 때 생길 수 있는 틈을 방지합니다.
고정된 도형을 이동시킨다는 것은 무엇을 뜻하나요?
그림을 두 가지 입력으로 나눕니다. 펜촉의 기준점이 이동하는 위치를 나타내는 경로와, 한 위치에서 펜촉이 덮는 영역을 나타내는 윤곽입니다. 윤곽의 기준점을 원점에 둡니다. 경로상의 각 위치를 윤곽의 모든 점에 더합니다.
수학적으로 결과 집합은 S = {p + n | p belongs to P, n belongs to N}입니다. 여기서 P은 경로이고 N은 펜촉입니다. 이는 CGAL 2D 민코프스키 합 사용자 매뉴얼의 점별 정의를 따르는 민코프스키 합입니다.
이 구성에서 “고정”이란 윤곽과 방향이 모두 그림 좌표계에서 변하지 않는다는 뜻입니다. 펜촉은 경로를 따라 회전하지 않고 평행 이동합니다. 기준점을 직사각형의 중심에 두면 경로가 움직이는 펜촉의 중심을 나타냅니다.
펜촉 각도와 이동 방향은 두께를 어떻게 결정하나요?
세 가지 매개변수를 정합니다. 직사각형의 긴 변 w, 짧은 변 h, 긴 변의 각도 α입니다. 작은 양수 h를 사용하면 펜촉에 눈에 보이는 최소 두께가 생깁니다. 반면 이상적인 선분 펜촉은 두께가 0이므로, 펜촉과 정확히 같은 방향으로 이동하면 넓이가 0인 자국을 남길 수 있습니다.
방향 각도가 θ인 직선 경로 구간에서, 이동 방향에 수직으로 측정한 직사각형 펜촉의 폭은 다음과 같습니다.
width across the stroke = w × |sin(θ − α)| + h × |cos(θ − α)|
이 공식은 직사각형의 서로 수직인 두 변을 경로의 법선 방향으로 투영해 얻습니다. 직선 구간이나 매끄러운 곡선을 따라 국소적으로 투영한 펜촉 윤곽에 해당합니다. 곡선 획에서 서로 겹치는 부분은 전체 실루엣을 더 넓게 만들 수 있습니다.
예시로 24단위 × 3단위 크기의 펜촉을 사용해 보겠습니다. 긴 변과 평행하게 이동하면 폭 3단위의 띠가 생깁니다. 수직으로 이동하면 폭 24단위의 띠가 생깁니다. 방향 차이가 45°일 때 폭은 약 (24 + 3) / √2, 즉 19.09단위입니다. 이는 계산된 치수이지, 브러시로 측정한 결과가 아닙니다.
복잡한 곡선을 그리기 전에 이 세 방향을 보정 테스트로 사용하세요. 여러 번 겹쳐 꺾이는 부분을 해석하지 않아도 각도와 치수가 의도대로 작동하는지 확인할 수 있습니다.
도장 간격 없이 연속적으로 이동시키려면 어떻게 해야 하나요?
볼록 펜촉을 직선 구간을 따라 움직일 때는 양 끝점에 펜촉을 각각 배치한 다음, 두 배치의 꼭짓점을 합쳐 볼록 껍질을 구합니다. 이 껍질은 해당 구간을 따라 평행 이동할 때 쓸린 영역과 정확히 일치합니다.
이유는 간단합니다. 중간 위치의 배치는 양 끝 위치의 배치를 선형 보간한 결과입니다. 펜촉이 볼록하므로 볼록 껍질은 이러한 중간 배치만 정확히 포함합니다. 이 구간 구성은 별도의 브러시 기능이 아니라 민코프스키 합의 정의에서 직접 따라 나옵니다.
직사각형의 경우 각 구간에서 후보 점은 8개뿐입니다. 시작점의 네 꼭짓점과 끝점의 네 꼭짓점입니다. 이들의 껍질을 구하고, 경로의 연속된 각 점 쌍에 대해 반복한 다음, 결과 다각형들을 합칩니다. 인접한 쓸기 영역은 공통 끝점에서 펜촉이 차지하는 전체 영역을 공유하므로 기하 구조가 연속적으로 덮입니다.
전체 경로를 둘러싸는 볼록 껍질 하나를 구하지 마세요. 그러면 꺾이는 부분이 채워지고 의도한 오목한 부분이 사라집니다. 껍질은 각 구간마다 구해야 하며, 전체 획은 그 껍질들의 합집합입니다.
오목한 펜촉에는 다른 처리가 필요합니다. 오목한 부분이 껍질로 채워지기 때문입니다. 펜촉을 여러 볼록 조각으로 분해하고 각 조각을 이동시킨 뒤 결과를 합치세요. CGAL은 비볼록 다각형의 민코프스키 합을 위한 관련 분해 및 합집합 접근법을 설명합니다.
Python에서 쓸기 작업은 어떻게 구현하나요?
다음은 Python 3과 표준 라이브러리만 필요한 완전한 프로그램입니다. 다섯 점으로 된 예시 경로를 정의하고, 직사각형 펜촉을 구성한 뒤, 네 개의 채워진 쓸기 다각형이 포함된 SVG 문서를 출력합니다. broad_edge.py이라는 이름으로 저장한 다음 python broad_edge.py > stroke.svg를 실행해 그림을 만드세요.
좌표는 여기서 사용하는 SVG 규칙을 따릅니다. x는 오른쪽으로, y는 아래쪽으로 증가합니다. 따라서 이 프로그램에서 양의 각도는 화면에서 펜촉을 시계 방향으로 기울입니다. 다른 그림 도구에서는 각도 규칙이 다를 수 있습니다.
from math import cos, sin, radians
def hull(points):
pts = sorted(set(points))
if len(pts) <= 1:
return pts
def cross(o, a, b):
return ((a[0] - o[0]) * (b[1] - o[1])
- (a[1] - o[1]) * (b[0] - o[0]))
def half(sequence):
out = []
for p in sequence:
while len(out) >= 2 and cross(out[-2], out[-1], p) <= 0:
out.pop()
out.append(p)
return out
return half(pts)[:-1] + half(reversed(pts))[:-1]
def sweep(path, width=24, thickness=3, angle=30):
if not path or width <= 0 or thickness <= 0:
raise ValueError(
'Provide a nonempty path and positive nib dimensions'
)
a = radians(angle)
u = (cos(a), sin(a))
v = (-sin(a), cos(a))
nib = [
(s * width * u[0] / 2 + t * thickness * v[0] / 2,
s * width * u[1] / 2 + t * thickness * v[1] / 2)
for s, t in [(-1, -1), (1, -1), (1, 1), (-1, 1)]
]
pairs = list(zip(path, path[1:])) or [(path[0], path[0])]
return [
hull([(p[0] + q[0], p[1] + q[1])
for p in (start, end) for q in nib])
for start, end in pairs
]
path = [(40, 45), (85, 125), (140, 150), (195, 110), (245, 45)]
polygons = sweep(path)
all_points = [p for polygon in polygons for p in polygon]
x0, y0 = (min(p[i] for p in all_points) - 5 for i in (0, 1))
x1, y1 = (max(p[i] for p in all_points) + 5 for i in (0, 1))
svg = [
'<svg xmlns="http://www.w3.org/2000/svg" '
f'viewBox="{x0} {y0} {x1 - x0} {y1 - y0}">',
'<g fill="#23364d" stroke="none">'
]
for polygon in polygons:
coordinates = ' '.join(f'{x:.6f},{y:.6f}' for x, y in polygon)
svg.append(f'<polygon points="{coordinates}"/>')
svg.extend(['</g>', '</svg>'])
print('\n'.join(svg))
프로그램은 경로에 점이 하나만 있을 때 펜촉 윤곽을 하나 그립니다. 연속된 점이 반복되는 경우에도 방향 계산 없이 윤곽을 생성합니다. 빈 경로나 0 이하의 펜촉 치수에는 오류를 발생시킵니다. 유한한 숫자 좌표 쌍을 입력하세요. 이 간단한 예제는 가져온 경로 데이터를 검증하지 않습니다.
예시 좌표로 이 구성을 실행한 결과, 꼭짓점이 6개인 쓸기 다각형 네 개가 생성되었고 SVG가 XML로 올바르게 파싱되었습니다. 단일 점과 반복 점이 있는 경우도 확인했습니다. 이러한 확인은 구성과 문서 구조를 검증하지만, 시각적 렌더링 테스트를 수행한 것은 아닙니다.
곡선, 모서리, 끝점은 어떻게 처리해야 하나요?
부동소수점 연산과 좌표 반올림에 따른 오차를 제외하면, 예제는 폴리라인에 대해 정확합니다. 베지어 곡선을 사용하려면 먼저 직선 구간으로 근사한 다음 동일한 쓸기 방식을 적용하세요. 곡선 매개변수 전체에 점을 균등하게 배치하기보다 급하게 휘는 곳에 구간을 더 추가하세요.
출력 크기에 맞춰 근사 허용 오차를 정하세요. 예를 들어 1/4픽셀 편차를 초기 화면 표시 목표로 삼은 뒤, 계획한 최대 배율에서 확인할 수 있습니다. 이는 예시로 든 엔지니어링 선택이지 보편적인 품질 기준은 아닙니다. 이미 직선인 구간에 세분점을 추가해도 쓸린 영역은 바뀌지 않습니다.
펜을 뗀 경로는 서로 분리해 두세요. 연결되지 않은 두 획의 점을 한 번의 호출에 넣으면 원치 않는 연결 쓸기가 생깁니다. 각 획을 독립적으로 생성하고 결과 다각형을 출력에 모으세요.
끝점에서도 펜촉은 전체 윤곽을 유지합니다. 모서리에서는 인접한 두 쓸기 영역이 겹칩니다. 이 모델에는 별도의 마이터 또는 둥근 캡이 없습니다. SVG의 일반 획 속성은 자체적인 캡과 연결부 구성을 정의하므로, 이를 추가하면 고정 펜촉 쓸기 범위를 벗어난 기하 구조가 생깁니다. SVG 2의 획 캡 및 연결부 정의를 참조하세요.
다각형을 병합해야 하는 경우는 언제인가요?
프로그램은 겹쳐진 채워진 다각형을 내보냅니다. 단색 미리보기에서는 다각형 합집합 라이브러리 없이도 쓸린 영역을 표현할 수 있습니다. 그러나 각 다각형은 여전히 별도의 편집 가능한 개체이며, 프로그램이 하나로 통합된 경계를 계산하지는 않습니다.
나중에 윤곽선을 편집할 수 있는 깔끔한 실루엣이 필요하다면 모든 구간 다각형의 불리언 합집합을 계산하세요. 결과에 남은 구멍은 보존하세요. 경로가 고리를 이뤄 칠해지지 않은 영역을 둘러쌀 수 있으며, 닫힌 윤곽을 모두 채우면 획의 모양이 달라집니다.
투명도도 신중하게 처리해야 합니다. 겹치는 다각형에 각각 부분적인 fill-opacity를 적용하면 겹친 영역이 더 어두워질 수 있습니다. 균일한 반투명도를 원한다면 바깥 그룹에 불투명도를 적용하거나, 채우기 불투명도를 적용하기 전에 기하 구조를 병합하세요. SVG는 페인팅 명세에서 개체 채우기 불투명도와 그룹 불투명도를 구분합니다.
| 원하는 결과 | 적절한 다음 단계 |
|---|---|
| 단색 미리보기 | 채워진 쓸기 다각형 유지 |
| 균일한 반투명 미리보기 | 다각형을 감싸는 그룹에 불투명도 적용 |
| 편집 가능한 단일 실루엣 | 쓸기 다각형을 불리언 합집합으로 병합 |
| 사용자 정의 오목 펜촉 | 쓸기 전에 펜촉 분해 |
| 매끄러운 곡선 획 | 곡선을 근사한 다음 구간을 쓸기 |
예상과 다른 획이 생기면 어떻게 진단하나요?
먼저 보정 방향을 확인한 다음 경로와 출력 구성을 살펴보세요. 눈에 보이는 차이의 원인을 명확히 파악할 수 있도록 한 번에 입력 하나만 바꾸세요.
- 두께가 거의 일정함: 펜촉이 경로를 따라 회전하는지 확인하세요. 그림 좌표계에서 각도는 고정되어야 합니다.
- 표시된 도장 사이에 틈이 생김: 개별 도장 대신 구간 껍질 구성을 사용하세요.
- 꺾이는 부분 안쪽에 넓은 쐐기 모양이 채워짐: 여러 경로 구간을 한꺼번에 둘러싸는 껍질을 구하지 않았는지 확인하세요.
- 끝이 둥글게 보임: 일반 획이나 다른 펜촉 윤곽이 추가되었는지 확인하세요.
- 매끄러운 원본 곡선이 각져 보임: 굽은 곳의 폴리라인 근사를 더 세분화하세요.
- 예상치 못한 연결부가 생김: 연결되지 않은 경로를 별도의 쓸기 호출로 처리하세요.
결과를 Inkscape의 Calligraphy 도구와 비교하려면 해당 문서에서 유용한 매개변수 확인 방법을 참고할 수 있습니다. Fixation 100은 각도를 고정하고, Thinning 0은 속도에 따른 폭 변화를 없애며, 압력 및 기울기 제어는 폭과 각도를 바꿀 수 있습니다. 고정 펜촉의 동작을 살펴볼 때는 변화를 주는 입력을 비활성화하세요. 이러한 설정은 도구의 제어 항목을 설명할 뿐, 도구의 출력이 이 직사각형 구성과 동일하다는 것을 입증하지는 않습니다. Inkscape Calligraphy 도구 모음 문서를 참조하세요.
신뢰할 수 있는 브로드 엣지 획은 단순한 보정 경로와 명확하게 정의된 윤곽에서 시작합니다. 평행, 수직, 대각선 테스트가 올바르게 작동하면 선택한 경로에 같은 구간 쓸기 방식을 적용하고, 출력 크기에 맞춰 곡선 구간을 세분화하세요. 다음 편집 단계에서 단일 실루엣이 필요하면 다각형을 병합하세요.
