2011-10-09 6 views
5

벡터 그래픽을 생성하는 Python 스크립트를 작성하려면 어떻게해야할까요? 특히, I는 둥근 모서리 채워진 다각형 그려야 (즉, 직선과 원호 구성되어 평면 수치).PDF 파일을 생성하고 모서리가 둥근 다각형을 그립니다.

matplotlib 그것은 매우 쉽게 둥근 모서리와 날카로운 모서리와 일반 다각형과 사각형을 그릴 수 있습니다 것으로 보인다. 그러나 모서리가 둥근 다각형을 그리려면 모양을 대략적으로 나타내는 베 지어 곡선을 먼저 계산해야합니다.

더 간단한 것이 있습니까? 또는 내가 만들고자하는 모양에 가까운 베 지어 곡선을 계산하는 데 사용할 수있는 또 다른 라이브러리가 있습니까? 이상적으로, 각 꼭지점에 대해 (위치, 코너 반경) 쌍을 지정하기 만하면됩니다. 여기

은 예이다 : I 적색 다각형 (+ 각 모서리의 반경)을 지정하려는 라이브러리 출력 계조도는 것이다 :

example

(볼록 폴리곤 I 속이 수도 두꺼운 펜을 사용하여 다각형의 외곽선을 그 으십시오. 그러나 비 볼록 케이스에서는 작동하지 않습니다.)

답변

4

PDF 파일을 생성하는 방법은 cairo 라이브러리를 참조하십시오. PDF 그래픽에 포함 된 벡터 그래픽 libaray. 파이썬 바인딩도 있습니다. 모서리가 둥근 다각형 그리기에 관해서는

, 나는 상자에서이를 지원하는 그래픽 라이브러리 잘 모르는 것 같아요.

그러나 너무 호를 계산하기 복잡하지 않아야은 모서리 반경을 부여 다각형 모서리 좌표. 기본적으로 두 가장자리에서 거리 r (즉 원하는 반경)을 가진 두 인접한 가장자리의 각도 이등분선을 찾아야합니다. 이것은 호의 중심이며 시작점과 끝점을 찾기 위해이 지점에서 두 가장자리로 투영합니다.

가있을 수 있습니다 사소하지 않은 경우, 예를 들어, 어떻게 해야할지, 다각형 모서리가 두 개의 호를 맞추기에는 너무 짧으면 (이 경우 더 작은 반경을 선택해야 할 것 같네요), 아마도 다른 사람들은 현재 알고 있지 않습니다.

HTH

+0

감사합니다! 물론 원호 좌표와 선분 좌표 계산은 그리 어렵지 않습니다. 즉, 별도의 호와 선분을 사용하여 모양의 윤곽 *을 비교적 쉽게 그릴 수 있습니다. 그러나, 나는 어떻게 채워지 는가 * 평면 숫자를 얻는가? 카이로 라이브러리를 사용하여 호와 선분에서 닫힌 경로를 작성한 다음 경로 내부를 채우도록 라이브러리에 지시 할 수 있습니까? –

+1

네, 할 수 있어요 :) – MartinStettner

13

여기에 다소 해키하기 matplotlib 솔루션입니다. 주요 합병증은 matplotlib Path 개체를 사용하여 복합체 Path을 만드는 것과 관련이 있습니다. 답에 대한

#!/usr/bin/env python 

import numpy as np 
from matplotlib.path import Path 
from matplotlib.patches import PathPatch, Polygon 
from matplotlib.transforms import Bbox, BboxTransformTo 

def side(a, b, c): 
    "On which side of line a-b is point c? Returns -1, 0, or 1." 
    return np.sign(np.linalg.det(np.c_[[a,b,c],[1,1,1]])) 

def center((prev, curr, next), radius): 
    "Find center of arc approximating corner at curr." 
    p0, p1 = prev 
    c0, c1 = curr 
    n0, n1 = next 
    dp = radius * np.hypot(c1 - p1, c0 - p0) 
    dn = radius * np.hypot(c1 - n1, c0 - n0) 
    p = p1 * c0 - p0 * c1 
    n = n1 * c0 - n0 * c1 
    results = \ 
     np.linalg.solve([[p1 - c1, c0 - p0], 
         [n1 - c1, c0 - n0]], 
         [[p - dp, p - dp, p + dp, p + dp], 
         [n - dn, n + dn, n - dn, n + dn]]) 
    side_n = side(prev, curr, next) 
    side_p = side(next, curr, prev) 
    for r in results.T: 
     if (side(prev, curr, r), side(next, curr, r)) == (side_n, side_p): 
      return r 
    raise ValueError, "Cannot find solution" 

def proj((prev, curr, next), center): 
    "Project center onto lines prev-curr and next-curr." 
    p0, p1 = prev = np.asarray(prev) 
    c0, c1 = curr = np.asarray(curr) 
    n0, n1 = next = np.asarray(next) 
    pc = curr - prev 
    nc = curr - next 
    pc2 = np.dot(pc, pc) 
    nc2 = np.dot(nc, nc) 
    return (prev + np.dot(center - prev, pc)/pc2 * pc, 
      next + np.dot(center - next, nc)/nc2 * nc) 

def rad2deg(angle): 
    return angle * 180.0/np.pi 

def angle(center, point): 
    x, y = np.asarray(point) - np.asarray(center) 
    return np.arctan2(y, x) 

def arc_path(center, start, end): 
    "Return a Path for an arc from start to end around center." 
    # matplotlib arcs always go ccw so we may need to mirror 
    mirror = side(center, start, end) < 0 
    if mirror: 
     start *= [1, -1] 
     center *= [1, -1] 
     end *= [1, -1] 
    return Path.arc(rad2deg(angle(center, start)), 
        rad2deg(angle(center, end))), \ 
      mirror 

def path(vertices, radii): 
    "Return a Path for a closed rounded polygon." 
    if np.isscalar(radii): 
     radii = np.repeat(radii, len(vertices)) 
    else: 
     radii = np.asarray(radii) 
    pv = [] 
    pc = [] 
    first = True 
    for i in range(len(vertices)): 
     if i == 0: 
      seg = (vertices[-1], vertices[0], vertices[1]) 
     elif i == len(vertices) - 1: 
      seg = (vertices[-2], vertices[-1], vertices[0]) 
     else: 
      seg = vertices[i-1:i+2] 
     r = radii[i] 
     c = center(seg, r) 
     a, b = proj(seg, c) 
     arc, mirror = arc_path(c, a, b) 
     m = [1,1] if not mirror else [1,-1] 
     bb = Bbox([c, c + (r, r)]) 
     iter = arc.iter_segments(BboxTransformTo(bb)) 
     for v, c in iter: 
      if c == Path.CURVE4: 
       pv.extend([m * v[0:2], m * v[2:4], m * v[4:6]]) 
       pc.extend([c, c, c]) 
      elif c == Path.MOVETO: 
       pv.append(m * v) 
       if first: 
        pc.append(Path.MOVETO) 
        first = False 
       else: 
        pc.append(Path.LINETO) 
    pv.append([0,0]) 
    pc.append(Path.CLOSEPOLY) 

    return Path(pv, pc) 

if __name__ == '__main__': 
    from matplotlib import pyplot 
    fig = pyplot.figure() 
    ax = fig.add_subplot(111) 
    vertices = [[3,0], [5,2], [10,0], [6,9], [6,5], [3, 5], [0,2]] 

    patch = Polygon(vertices, edgecolor='red', facecolor='None', 
        linewidth=1) 
    ax.add_patch(patch) 

    patch = PathPatch(path(vertices, 0.5), 
         edgecolor='black', facecolor='blue', alpha=0.4, 
         linewidth=2) 
    ax.add_patch(patch) 

    ax.set_xlim(-1, 11) 
    ax.set_ylim(-1, 9) 
    fig.savefig('foo.pdf') 

output of script above

+0

고마워, 이거 멋지다! 나는 카이로가 이런 종류의 작업을위한 "올바른"도구 인 것처럼 다른 대답을 받아 들였지만, matplotlib에서도 카이로를 할 수 있다는 것을보기가 정말 좋습니다.특히 경로 단편에서 복합 경로를 구성하는 코드 단편은 매우 유용합니다. –

관련 문제