summaryrefslogtreecommitdiff
path: root/third_party/spiro/curves/bezfigs.py
blob: b7e408b099bbf42581153a7dc267f850dd89c3ca (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
import sys
from math import *

import fromcubic
import tocubic

import cornu

def eps_prologue(x0, y0, x1, y1, draw_box = False):
    print '%!PS-Adobe-3.0 EPSF'
    print '%%BoundingBox:', x0, y0, x1, y1
    print '%%EndComments'
    print '%%EndProlog'
    print '%%Page: 1 1'
    if draw_box:
        print x0, y0, 'moveto', x0, y1, 'lineto', x1, y1, 'lineto', x1, y0, 'lineto closepath stroke'

def eps_trailer():
    print '%%EOF'

def fit_cubic_superfast(z0, z1, arclen, th0, th1, aab):
    chord = hypot(z1[0] - z0[0], z1[1] - z0[1])
    cth0, sth0 = cos(th0), sin(th0)
    cth1, sth1 = -cos(th1), -sin(th1)
    armlen = .66667 * arclen
    a = armlen * aab
    b = armlen - a
    bz = [z0, (z0[0] + cth0 * a, z0[1] + sth0 * a),
          (z1[0] + cth1 * b, z1[1] + sth1 * b), z1]
    return bz

def fit_cubic(z0, z1, arclen, th_fn, fast, aabmin = 0, aabmax = 1.):
    chord = hypot(z1[0] - z0[0], z1[1] - z0[1])
    if (arclen < 1.000001 * chord):
        return [z0, z1], 0
    th0 = th_fn(0)
    th1 = th_fn(arclen)
    imax = 4
    jmax = 10
    if fast:
        imax = 1
        jmax = 0
    for i in range(imax):
        for j in range(jmax + 1):
            if jmax == 0:
                aab = 0.5 * (aabmin + aabmax)
            else:
                aab = aabmin + (aabmax - aabmin) * j / jmax
            if fast == 2:
                bz = fit_cubic_superfast(z0, z1, arclen, th0, th1, aab)
            else:
                bz = tocubic.fit_cubic_arclen(z0, z1, arclen, th0, th1, aab)
            score = tocubic.measure_bz_rk4(bz, arclen, th_fn)
            print '% aab =', aab, 'score =', score
            sys.stdout.flush()
            if j == 0 or score < best_score:
                best_score = score
                best_aab = aab
                best_bz = bz
        daab = .06 * (aabmax - aabmin)
        aabmin = max(0, best_aab - daab)
        aabmax = min(1, best_aab + daab)
        print '%--- best_aab =', best_aab
    return best_bz, best_score

def cornu_to_cubic(t0, t1, figno):
    if figno == 1:
        aabmin = 0
        aabmax = 0.4
    elif figno == 2:
        aabmin = 0.5
        aabmax = 1.
    else:
        aabmin = 0
        aabmax = 1.
    fast = 0
    if figno == 3:
        fast = 1
    elif figno == 4:
        fast = 2
    def th_fn(s):
        return (s + t0) ** 2
    y0, x0 = cornu.eval_cornu(t0)
    y1, x1 = cornu.eval_cornu(t1)
    bz, score = fit_cubic((x0, y0), (x1, y1), t1 - t0, th_fn, fast, aabmin, aabmax)
    return bz, score

def plot_k_of_bz(bz):
    dbz = tocubic.bz_deriv(bz)
    ddbz = tocubic.bz_deriv(dbz)
    cmd = 'moveto'
    ss = [0]
    def arclength_deriv(x, ss):
        dx, dy = tocubic.bz_eval(dbz, x)
        return [hypot(dx, dy)]
    dt = 0.01
    t = 0
    for i in range(101):
        dx, dy = tocubic.bz_eval(dbz, t)
        ddx, ddy = tocubic.bz_eval(ddbz, t)
        k = (ddy * dx - dy * ddx) / (dx * dx + dy * dy) ** 1.5
        print 100 + 500 * ss[0], 100 + 200 * k, cmd
        cmd = 'lineto'

        dsdx = arclength_deriv(t, ss)
        tocubic.rk4(ss, dsdx, t, .01, arclength_deriv)
        t += dt
    print 'stroke'

def plot_k_nominal(s0, s1):
    k0 = 2 * s0
    k1 = 2 * s1
    print 'gsave 0.5 setlinewidth'
    print 100, 100 + 200 * k0, 'moveto'
    print 100 + 500 * (s1 - s0), 100 + 200 * k1, 'lineto'
    print 'stroke grestore'

def simple_bez():
    eps_prologue(95, 126, 552, 508, 0)
    tocubic.plot_prolog()
    print '/ss 1.5 def'
    print '/circle { ss 0 moveto currentpoint exch ss sub exch ss 0 360 arc } bind def'
    bz, score = cornu_to_cubic(.5, 1.1, 2)
    fromcubic.plot_bzs([[bz]], (-400, 100), 1000, True)
    print 'stroke'
    print '/Times-Roman 12 selectfont'
    print '95 130 moveto ((x0, y0)) show'
    print '360 200 moveto ((x1, y1)) show'
    print '480 340 moveto ((x2, y2)) show'
    print '505 495 moveto ((x3, y3)) show'
    print 'showpage'
    eps_trailer()

def fast_bez(figno):
    if figno == 3:
        y1 = 520
    else:
        y1 = 550
    eps_prologue(95, 140, 552, y1, 0)
    tocubic.plot_prolog()
    print '/ss 1.5 def'
    print '/circle { ss 0 moveto currentpoint exch ss sub exch ss 0 360 arc } bind def'
    bz, score = cornu_to_cubic(.5, 1.1, figno)
    fromcubic.plot_bzs([[bz]], (-400, 100), 1000, True)
    print 'stroke'
    plot_k_nominal(.5, 1.1)
    plot_k_of_bz(bz)
    print 'showpage'
    eps_trailer()

def bezfig(s1):
    eps_prologue(95, 38, 510, 550, 0)
    #print '0.5 0.5 scale 500 100 translate'
    tocubic.plot_prolog()
    print '/ss 1.5 def'
    print '/circle { ss 0 moveto currentpoint exch ss sub exch ss 0 360 arc } bind def'
    bz, score = cornu_to_cubic(.5, 0.85, 1)
    fromcubic.plot_bzs([[bz]], (-400, 0), 1000, True)
    print 'stroke'
    plot_k_nominal(.5, 0.85)
    plot_k_of_bz(bz)
    bz, score = cornu_to_cubic(.5, 0.85, 2)
    fromcubic.plot_bzs([[bz]], (-400, 100), 1000, True)
    print 'stroke'
    print 'gsave 0 50 translate'
    plot_k_nominal(.5, .85)
    plot_k_of_bz(bz)
    print 'grestore'
    print 'showpage'

import sys

if __name__ == '__main__':
    figno = int(sys.argv[1])
    if figno == 0:
        simple_bez()
    elif figno == 1:
        bezfig(1.0)
    elif figno == 2:
        bezfig(0.85)
    else:
        fast_bez(figno)
    #fast_bez(4)