chiark / gitweb /
helixish: wip
[moebius3.git] / helixish.py
1
2 from __future__ import print_function
3
4 import numpy as np
5 from numpy import cos, sin
6
7 import sys
8
9 def augment(v): return np.append(v, 1)
10 def augment0(v): return np.append(v, 0)
11 def unaugment(v): return v[0:3]
12
13 class HelixishCurve():
14   def __init__(hc, cp):
15     p = cp[0]
16     q = cp[3]
17     dp = unit_v(cp[1]-cp[0])
18     dq = unit_v(cp[3]-cp[2])
19
20     # the initial attempt
21     #   - solve in the plane containing dP and dQ
22     #   - total distance normal to that plane gives mu
23     #   - now resulting curve is not parallel to dP at P
24     #     nor dQ at Q, so tilt it
25     #   - [[ pick as the hinge point the half of the curve
26     #     with the larger s or t ]] not yet implemented
27     #   - increase the other distance {t,s} by a bodge factor
28     #     approx distance between {Q,P} and {Q,P}' due to hinging
29     #     but minimum is 10% of (wlog) {s,t} [[ not quite like this ]]
30
31     dPQplane_normal = np.cross(dp, dq)
32     if (np.norm(dPQplane_normal) < 1E6):
33       dPQplane_normal += [0, 0, 1E5]
34     dPQplane_normal = unit_v(dPQplane_normal)
35
36     dPQplane_basis = np.column_stack(np.cross(dp, dPQplane_normal),
37                                      dp,
38                                      dPQplane_normal,
39                                      p);
40     dPQplane_basis = np.vstack(dPQplane_basis, [0,0,0,1])
41     dPQplane_into = np.linalg.inv(dPQplane_basis)
42
43     dp_plane = unaugment(dPQplane_into * augment0(dp))
44     dq_plane = unaugment(dPQplane_into * augment0(dq))
45     q_plane  = unaugment(dPQplane_into * augment(q))
46     dist_pq_plane = np.linalg.norm(q_plane)
47
48     # two circular arcs of equal maximum possible radius
49     # algorithm courtesy of Simon Tatham (`Railway problem',
50     # pers.comm. to ijackson@chiark 23.1.2004)
51     railway_angleoffset = atan2(*q_plane[0:1])
52     railway_theta =                      tau/4 - railway_angleoffset
53     railway_phi   = atan2(*dq_plane[0:1]) - railway_angleoffset
54     railway_cos_theta = cos(railway_theta)
55     railway_cos_phi   = cos(railway_phi)
56     if railway_cos_theta**2 + railway_cos_phi**2 > 1E6:
57       railway_roots = np.roots([
58         2 * (1 + cos(railway_theta - railway_phi)),
59         2 * (railway_cos_theta - railway_cos_phi),
60         -1
61         ])
62       for railway_r in railway_roots:
63         def railway_CPQ(pq, dpq):
64           nonlocal railway_r
65           return pq + railway_r * [-dpq[1], dpq[0]]
66
67         railway_CP = railway_CPQ([0,0,0],       dp_plane)
68         railway_QP = railway_CPQ(q_plane[0:2], -dq_plane)
69         railway_midpt = 0.5 * (railway_CP + railway_QP)
70
71         best_st = None
72         def railway_ST(C, start, end):
73           nonlocal railway_r
74           delta = atan2(*(end - C)[0:2]) - atan2(start - C)[0:2]
75           s = delta * railway_r
76
77         try_s = railway_ST(railway_CP, [0,0], midpt)
78         try_t = railway_ST(railway_CP, midpt, q_plane)
79         try_st = try_s + try_t
80         if best_st is None or try_st < best_st:
81           start_la = 1/r
82           start_s = try_s
83           start_t = try_t
84           best_st = try_st
85           start_mu = q_plane[2] / (start_s + start_t)
86
87     else: # twoarcs algorithm is not well defined
88       start_la = 0.1
89       start_s = dist_pq_plane * .65
90       start_t = dist_pq_plane * .35
91       start_mu = 0.05
92
93     bodge = max( q_plane[2] * mu,
94                  (start_s + start_t) * 0.1 )
95     start_s += 0.5 * bodge
96     start_t += 0.5 * bodge
97     start_kappa = 0
98     start_gamma = 1
99
100     tilt = atan(mu)
101     tilt_basis = np.array([
102       1,     0,           0,         0,
103       0,   cos(tilt), -sin(tilt),    0,
104       0,   sin(tilt),  cos(tilt),    0,
105       0,     0,           0,         1,
106     ])
107     findcurve_basis = dPQplane_basis * tilt_basis
108     findcurve_into = np.linalg.inv(findcurve_basis)
109
110     q_findcurve = unaugment(findcurve_into, augment(q))
111     dq_findcurve = unaugment(findcurve_into, augment0(dq))
112
113     findcurve_target = np.concatenate(q_findcurve, dq_findcurve)
114     findcurve_start = (sqrt(start_s), sqrt(start_t), start_la,
115                        start_mu, start_gamma, start_kappa)
116     
117     
118                                                   
119
120     # we work in two additional coordinate systems:
121     # for both these:
122     #   P is at the origin
123     #   |PQ| = 1
124     # for findcurve:
125     #   dP is the +ve y axis
126     #   Q lies in the x/y plane
127     # for calculating the initial attempt:
128     #   P is at the origin