ParseDXF_Spline.py 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828
  1. # Author: vvlachoudis@gmail.com
  2. # Vasilis Vlachoudis
  3. # Date: 20-Oct-2015
  4. # ##########################################################
  5. # FlatCAM: 2D Post-processing for Manufacturing #
  6. # File modified: Marius Adrian Stanciu #
  7. # Date: 3/10/2019 #
  8. # ##########################################################
  9. import math
  10. import sys
  11. def norm(v):
  12. return math.sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2])
  13. def normalize_2(v):
  14. m = norm(v)
  15. return [v[0]/m, v[1]/m, v[2]/m]
  16. # ------------------------------------------------------------------------------
  17. # Convert a B-spline to polyline with a fixed number of segments
  18. #
  19. # FIXME to become adaptive
  20. # ------------------------------------------------------------------------------
  21. def spline2Polyline(xyz, degree, closed, segments, knots):
  22. """
  23. :param xyz: DXF spline control points
  24. :param degree: degree of the Spline curve
  25. :param closed: closed Spline
  26. :type closed: bool
  27. :param segments: how many lines to use for Spline approximation
  28. :param knots: DXF spline knots
  29. :return: x,y,z coordinates (each is a list)
  30. """
  31. # Check if last point coincide with the first one
  32. if (Vector(xyz[0]) - Vector(xyz[-1])).length2() < 1e-10:
  33. # it is already closed, treat it as open
  34. closed = False
  35. # FIXME we should verify if it is periodic,.... but...
  36. # I am not sure :)
  37. if closed:
  38. xyz.extend(xyz[:degree])
  39. knots = None
  40. else:
  41. # make base-1
  42. knots.insert(0, 0)
  43. npts = len(xyz)
  44. if degree < 1 or degree > 3:
  45. # print "invalid degree"
  46. return None, None, None
  47. # order:
  48. k = degree+1
  49. if npts < k:
  50. # print "not enough control points"
  51. return None, None, None
  52. # resolution:
  53. nseg = segments * npts
  54. # WARNING: base 1
  55. b = [0.0]*(npts*3+1) # polygon points
  56. h = [1.0]*(npts+1) # set all homogeneous weighting factors to 1.0
  57. p = [0.0]*(nseg*3+1) # returned curved points
  58. i = 1
  59. for pt in xyz:
  60. b[i] = pt[0]
  61. b[i+1] = pt[1]
  62. b[i+2] = pt[2]
  63. i += 3
  64. # if periodic:
  65. if closed:
  66. _rbsplinu(npts, k, nseg, b, h, p, knots)
  67. else:
  68. _rbspline(npts, k, nseg, b, h, p, knots)
  69. x = []
  70. y = []
  71. z = []
  72. for i in range(1, 3*nseg+1, 3):
  73. x.append(p[i])
  74. y.append(p[i+1])
  75. z.append(p[i+2])
  76. # for i,xyz in enumerate(zip(x,y,z)):
  77. # print i,xyz
  78. return x, y, z
  79. # ------------------------------------------------------------------------------
  80. # Subroutine to generate a B-spline open knot vector with multiplicity
  81. # equal to the order at the ends.
  82. # c = order of the basis function
  83. # n = the number of defining polygon vertices
  84. # n+2 = index of x[] for the first occurence of the maximum knot vector value
  85. # n+order = maximum value of the knot vector -- $n + c$
  86. # x[] = array containing the knot vector
  87. # ------------------------------------------------------------------------------
  88. def _knot(n, order):
  89. x = [0.0]*(n+order+1)
  90. for i in range(2, n+order+1):
  91. if order < i < n+2:
  92. x[i] = x[i-1] + 1.0
  93. else:
  94. x[i] = x[i-1]
  95. return x
  96. # ------------------------------------------------------------------------------
  97. # Subroutine to generate a B-spline uniform (periodic) knot vector.
  98. #
  99. # order = order of the basis function
  100. # n = the number of defining polygon vertices
  101. # n+order = maximum value of the knot vector -- $n + order$
  102. # x[] = array containing the knot vector
  103. # ------------------------------------------------------------------------------
  104. def _knotu(n, order):
  105. x = [0]*(n+order+1)
  106. for i in range(2, n+order+1):
  107. x[i] = float(i-1)
  108. return x
  109. # ------------------------------------------------------------------------------
  110. # Subroutine to generate rational B-spline basis functions--open knot vector
  111. # C code for An Introduction to NURBS
  112. # by David F. Rogers. Copyright (C) 2000 David F. Rogers,
  113. # All rights reserved.
  114. # Name: rbasis
  115. # Subroutines called: none
  116. # Book reference: Chapter 4, Sec. 4. , p 296
  117. # c = order of the B-spline basis function
  118. # d = first term of the basis function recursion relation
  119. # e = second term of the basis function recursion relation
  120. # h[] = array containing the homogeneous weights
  121. # npts = number of defining polygon vertices
  122. # nplusc = constant -- npts + c -- maximum number of knot values
  123. # r[] = array containing the rational basis functions
  124. # r[1] contains the basis function associated with B1 etc.
  125. # t = parameter value
  126. # temp[] = temporary array
  127. # x[] = knot vector
  128. # ------------------------------------------------------------------------------
  129. def _rbasis(c, t, npts, x, h, r):
  130. nplusc = npts + c
  131. temp = [0.0]*(nplusc+1)
  132. # calculate the first order non-rational basis functions n[i]
  133. for i in range(1, nplusc):
  134. if x[i] <= t < x[i+1]:
  135. temp[i] = 1.0
  136. else:
  137. temp[i] = 0.0
  138. # calculate the higher order non-rational basis functions
  139. for k in range(2, c+1):
  140. for i in range(1, nplusc-k+1):
  141. # if the lower order basis function is zero skip the calculation
  142. if temp[i] != 0.0:
  143. d = ((t-x[i])*temp[i])/(x[i+k-1]-x[i])
  144. else:
  145. d = 0.0
  146. # if the lower order basis function is zero skip the calculation
  147. if temp[i+1] != 0.0:
  148. e = ((x[i+k]-t)*temp[i+1])/(x[i+k]-x[i+1])
  149. else:
  150. e = 0.0
  151. temp[i] = d + e
  152. # pick up last point
  153. if t >= x[nplusc]:
  154. temp[npts] = 1.0
  155. # calculate sum for denominator of rational basis functions
  156. s = 0.0
  157. for i in range(1, npts+1):
  158. s += temp[i]*h[i]
  159. # form rational basis functions and put in r vector
  160. for i in range(1, npts+1):
  161. if s != 0.0:
  162. r[i] = (temp[i]*h[i])/s
  163. else:
  164. r[i] = 0
  165. # ------------------------------------------------------------------------------
  166. # Generates a rational B-spline curve using a uniform open knot vector.
  167. #
  168. # C code for An Introduction to NURBS
  169. # by David F. Rogers. Copyright (C) 2000 David F. Rogers,
  170. # All rights reserved.
  171. #
  172. # Name: rbspline.c
  173. # Subroutines called: _knot, rbasis
  174. # Book reference: Chapter 4, Alg. p. 297
  175. #
  176. # b = array containing the defining polygon vertices
  177. # b[1] contains the x-component of the vertex
  178. # b[2] contains the y-component of the vertex
  179. # b[3] contains the z-component of the vertex
  180. # h = array containing the homogeneous weighting factors
  181. # k = order of the B-spline basis function
  182. # nbasis = array containing the basis functions for a single value of t
  183. # nplusc = number of knot values
  184. # npts = number of defining polygon vertices
  185. # p[,] = array containing the curve points
  186. # p[1] contains the x-component of the point
  187. # p[2] contains the y-component of the point
  188. # p[3] contains the z-component of the point
  189. # p1 = number of points to be calculated on the curve
  190. # t = parameter value 0 <= t <= npts - k + 1
  191. # x[] = array containing the knot vector
  192. # ------------------------------------------------------------------------------
  193. def _rbspline(npts, k, p1, b, h, p, x):
  194. nplusc = npts + k
  195. nbasis = [0.0]*(npts+1) # zero and re-dimension the basis array
  196. # generate the uniform open knot vector
  197. if x is None or len(x) != nplusc+1:
  198. x = _knot(npts, k)
  199. icount = 0
  200. # calculate the points on the rational B-spline curve
  201. t = 0
  202. step = float(x[nplusc])/float(p1-1)
  203. for i1 in range(1, p1+1):
  204. if x[nplusc] - t < 5e-6:
  205. t = x[nplusc]
  206. # generate the basis function for this value of t
  207. nbasis = [0.0]*(npts+1) # zero and re-dimension the knot vector and the basis array
  208. _rbasis(k, t, npts, x, h, nbasis)
  209. # generate a point on the curve
  210. for j in range(1, 4):
  211. jcount = j
  212. p[icount+j] = 0.0
  213. # Do local matrix multiplication
  214. for i in range(1, npts+1):
  215. p[icount+j] += nbasis[i]*b[jcount]
  216. jcount += 3
  217. icount += 3
  218. t += step
  219. # ------------------------------------------------------------------------------
  220. # Subroutine to generate a rational B-spline curve using an uniform periodic knot vector
  221. #
  222. # C code for An Introduction to NURBS
  223. # by David F. Rogers. Copyright (C) 2000 David F. Rogers,
  224. # All rights reserved.
  225. #
  226. # Name: rbsplinu.c
  227. # Subroutines called: _knotu, _rbasis
  228. # Book reference: Chapter 4, Alg. p. 298
  229. #
  230. # b[] = array containing the defining polygon vertices
  231. # b[1] contains the x-component of the vertex
  232. # b[2] contains the y-component of the vertex
  233. # b[3] contains the z-component of the vertex
  234. # h[] = array containing the homogeneous weighting factors
  235. # k = order of the B-spline basis function
  236. # nbasis = array containing the basis functions for a single value of t
  237. # nplusc = number of knot values
  238. # npts = number of defining polygon vertices
  239. # p[,] = array containing the curve points
  240. # p[1] contains the x-component of the point
  241. # p[2] contains the y-component of the point
  242. # p[3] contains the z-component of the point
  243. # p1 = number of points to be calculated on the curve
  244. # t = parameter value 0 <= t <= npts - k + 1
  245. # x[] = array containing the knot vector
  246. # ------------------------------------------------------------------------------
  247. def _rbsplinu(npts, k, p1, b, h, p, x=None):
  248. nplusc = npts + k
  249. nbasis = [0.0]*(npts+1) # zero and re-dimension the basis array
  250. # generate the uniform periodic knot vector
  251. if x is None or len(x) != nplusc+1:
  252. # zero and re dimension the knot vector and the basis array
  253. x = _knotu(npts, k)
  254. icount = 0
  255. # calculate the points on the rational B-spline curve
  256. t = k-1
  257. step = (float(npts)-(k-1))/float(p1-1)
  258. for i1 in range(1, p1+1):
  259. if x[nplusc] - t < 5e-6:
  260. t = x[nplusc]
  261. # generate the basis function for this value of t
  262. nbasis = [0.0]*(npts+1)
  263. _rbasis(k, t, npts, x, h, nbasis)
  264. # generate a point on the curve
  265. for j in range(1, 4):
  266. jcount = j
  267. p[icount+j] = 0.0
  268. # Do local matrix multiplication
  269. for i in range(1, npts+1):
  270. p[icount+j] += nbasis[i]*b[jcount]
  271. jcount += 3
  272. icount += 3
  273. t += step
  274. # Accuracy for comparison operators
  275. _accuracy = 1E-15
  276. def Cmp0(x):
  277. """Compare against zero within _accuracy"""
  278. return abs(x) < _accuracy
  279. def gauss(A, B):
  280. """Solve A*X = B using the Gauss elimination method"""
  281. n = len(A)
  282. s = [0.0] * n
  283. X = [0.0] * n
  284. p = [i for i in range(n)]
  285. for i in range(n):
  286. s[i] = max([abs(x) for x in A[i]])
  287. for k in range(n - 1):
  288. # select j>=k so that
  289. # |A[p[j]][k]| / s[p[i]] >= |A[p[i]][k]| / s[p[i]] for i = k,k+1,...,n
  290. j = k
  291. ap = abs(A[p[j]][k]) / s[p[j]]
  292. for i in range(k + 1, n):
  293. api = abs(A[p[i]][k]) / s[p[i]]
  294. if api > ap:
  295. j = i
  296. ap = api
  297. if j != k:
  298. p[k], p[j] = p[j], p[k] # Swap values
  299. for i in range(k + 1, n):
  300. z = A[p[i]][k] / A[p[k]][k]
  301. A[p[i]][k] = z
  302. for j in range(k + 1, n):
  303. A[p[i]][j] -= z * A[p[k]][j]
  304. for k in range(n - 1):
  305. for i in range(k + 1, n):
  306. B[p[i]] -= A[p[i]][k] * B[p[k]]
  307. for i in range(n - 1, -1, -1):
  308. X[i] = B[p[i]]
  309. for j in range(i + 1, n):
  310. X[i] -= A[p[i]][j] * X[j]
  311. X[i] /= A[p[i]][i]
  312. return X
  313. # Vector class
  314. # Inherits from List
  315. class Vector(list):
  316. """Vector class"""
  317. def __init__(self, x=3, *args):
  318. """Create a new vector,
  319. Vector(size), Vector(list), Vector(x,y,z,...)"""
  320. list.__init__(self)
  321. if isinstance(x, int) and not args:
  322. for i in range(x):
  323. self.append(0.0)
  324. elif isinstance(x, (list, tuple)):
  325. for i in x:
  326. self.append(float(i))
  327. else:
  328. self.append(float(x))
  329. for i in args:
  330. self.append(float(i))
  331. # ----------------------------------------------------------------------
  332. def set(self, x, y, z=None):
  333. """Set vector"""
  334. self[0] = x
  335. self[1] = y
  336. if z:
  337. self[2] = z
  338. # ----------------------------------------------------------------------
  339. def __repr__(self):
  340. return "[%s]" % ", ".join([repr(x) for x in self])
  341. # ----------------------------------------------------------------------
  342. def __str__(self):
  343. return "[%s]" % ", ".join([("%15g" % (x)).strip() for x in self])
  344. # ----------------------------------------------------------------------
  345. def eq(self, v, acc=_accuracy):
  346. """Test for equality with vector v within accuracy"""
  347. if len(self) != len(v):
  348. return False
  349. s2 = 0.0
  350. for a, b in zip(self, v):
  351. s2 += (a - b) ** 2
  352. return s2 <= acc ** 2
  353. def __eq__(self, v):
  354. return self.eq(v)
  355. # ----------------------------------------------------------------------
  356. def __neg__(self):
  357. """Negate vector"""
  358. new = Vector(len(self))
  359. for i, s in enumerate(self):
  360. new[i] = -s
  361. return new
  362. # ----------------------------------------------------------------------
  363. def __add__(self, v):
  364. """Add 2 vectors"""
  365. size = min(len(self), len(v))
  366. new = Vector(size)
  367. for i in range(size):
  368. new[i] = self[i] + v[i]
  369. return new
  370. # ----------------------------------------------------------------------
  371. def __iadd__(self, v):
  372. """Add vector v to self"""
  373. for i in range(min(len(self), len(v))):
  374. self[i] += v[i]
  375. return self
  376. # ----------------------------------------------------------------------
  377. def __sub__(self, v):
  378. """Subtract 2 vectors"""
  379. size = min(len(self), len(v))
  380. new = Vector(size)
  381. for i in range(size):
  382. new[i] = self[i] - v[i]
  383. return new
  384. # ----------------------------------------------------------------------
  385. def __isub__(self, v):
  386. """Subtract vector v from self"""
  387. for i in range(min(len(self), len(v))):
  388. self[i] -= v[i]
  389. return self
  390. # ----------------------------------------------------------------------
  391. # Scale or Dot product
  392. # ----------------------------------------------------------------------
  393. def __mul__(self, v):
  394. """scale*Vector() or Vector()*Vector() - Scale vector or dot product"""
  395. if isinstance(v, list):
  396. return self.dot(v)
  397. else:
  398. return Vector([x * v for x in self])
  399. # ----------------------------------------------------------------------
  400. # Scale or Dot product
  401. # ----------------------------------------------------------------------
  402. def __rmul__(self, v):
  403. """scale*Vector() or Vector()*Vector() - Scale vector or dot product"""
  404. if isinstance(v, Vector):
  405. return self.dot(v)
  406. else:
  407. return Vector([x * v for x in self])
  408. # ----------------------------------------------------------------------
  409. # Divide by floating point
  410. # ----------------------------------------------------------------------
  411. def __div__(self, b):
  412. return Vector([x / b for x in self])
  413. # ----------------------------------------------------------------------
  414. def __xor__(self, v):
  415. """Cross product"""
  416. return self.cross(v)
  417. # ----------------------------------------------------------------------
  418. def dot(self, v):
  419. """Dot product of 2 vectors"""
  420. s = 0.0
  421. for a, b in zip(self, v):
  422. s += a * b
  423. return s
  424. # ----------------------------------------------------------------------
  425. def cross(self, v):
  426. """Cross product of 2 vectors"""
  427. if len(self) == 3:
  428. return Vector(self[1] * v[2] - self[2] * v[1],
  429. self[2] * v[0] - self[0] * v[2],
  430. self[0] * v[1] - self[1] * v[0])
  431. elif len(self) == 2:
  432. return self[0] * v[1] - self[1] * v[0]
  433. else:
  434. raise Exception("Cross product needs 2d or 3d vectors")
  435. # ----------------------------------------------------------------------
  436. def length2(self):
  437. """Return length squared of vector"""
  438. s2 = 0.0
  439. for s in self:
  440. s2 += s ** 2
  441. return s2
  442. # ----------------------------------------------------------------------
  443. def length(self):
  444. """Return length of vector"""
  445. s2 = 0.0
  446. for s in self:
  447. s2 += s ** 2
  448. return math.sqrt(s2)
  449. __abs__ = length
  450. # ----------------------------------------------------------------------
  451. def arg(self):
  452. """return vector angle"""
  453. return math.atan2(self[1], self[0])
  454. # ----------------------------------------------------------------------
  455. def norm(self):
  456. """Normalize vector and return length"""
  457. length = self.length()
  458. if length > 0.0:
  459. invlen = 1.0 / length
  460. for i in range(len(self)):
  461. self[i] *= invlen
  462. return length
  463. normalize = norm
  464. # ----------------------------------------------------------------------
  465. def unit(self):
  466. """return a unit vector"""
  467. v = self.clone()
  468. v.norm()
  469. return v
  470. # ----------------------------------------------------------------------
  471. def clone(self):
  472. """Clone vector"""
  473. return Vector(self)
  474. # ----------------------------------------------------------------------
  475. def x(self):
  476. return self[0]
  477. def y(self):
  478. return self[1]
  479. def z(self):
  480. return self[2]
  481. # ----------------------------------------------------------------------
  482. def orthogonal(self):
  483. """return a vector orthogonal to self"""
  484. xx = abs(self.x())
  485. yy = abs(self.y())
  486. if len(self) >= 3:
  487. zz = abs(self.z())
  488. if xx < yy:
  489. if xx < zz:
  490. return Vector(0.0, self.z(), -self.y())
  491. else:
  492. return Vector(self.y(), -self.x(), 0.0)
  493. else:
  494. if yy < zz:
  495. return Vector(-self.z(), 0.0, self.x())
  496. else:
  497. return Vector(self.y(), -self.x(), 0.0)
  498. else:
  499. return Vector(-self.y(), self.x())
  500. # ----------------------------------------------------------------------
  501. def direction(self, zero=_accuracy):
  502. """return containing the direction if normalized with any of the axis"""
  503. v = self.clone()
  504. length = v.norm()
  505. if abs(length) <= zero:
  506. return "O"
  507. if abs(v[0] - 1.0) < zero:
  508. return "X"
  509. elif abs(v[0] + 1.0) < zero:
  510. return "-X"
  511. elif abs(v[1] - 1.0) < zero:
  512. return "Y"
  513. elif abs(v[1] + 1.0) < zero:
  514. return "-Y"
  515. elif abs(v[2] - 1.0) < zero:
  516. return "Z"
  517. elif abs(v[2] + 1.0) < zero:
  518. return "-Z"
  519. else:
  520. # nothing special about the direction, return N
  521. return "N"
  522. # ----------------------------------------------------------------------
  523. # Set the vector directly in polar coordinates
  524. # @param ma magnitude of vector
  525. # @param ph azimuthal angle in radians
  526. # @param th polar angle in radians
  527. # ----------------------------------------------------------------------
  528. def setPolar(self, ma, ph, th):
  529. """Set the vector directly in polar coordinates"""
  530. sf = math.sin(ph)
  531. cf = math.cos(ph)
  532. st = math.sin(th)
  533. ct = math.cos(th)
  534. self[0] = ma * st * cf
  535. self[1] = ma * st * sf
  536. self[2] = ma * ct
  537. # ----------------------------------------------------------------------
  538. def phi(self):
  539. """return the azimuth angle."""
  540. if Cmp0(self.x()) and Cmp0(self.y()):
  541. return 0.0
  542. return math.atan2(self.y(), self.x())
  543. # ----------------------------------------------------------------------
  544. def theta(self):
  545. """return the polar angle."""
  546. if Cmp0(self.x()) and Cmp0(self.y()) and Cmp0(self.z()):
  547. return 0.0
  548. return math.atan2(self.perp(), self.z())
  549. # ----------------------------------------------------------------------
  550. def cosTheta(self):
  551. """return cosine of the polar angle."""
  552. ptot = self.length()
  553. if Cmp0(ptot):
  554. return 1.0
  555. else:
  556. return self.z() / ptot
  557. # ----------------------------------------------------------------------
  558. def perp2(self):
  559. """return the transverse component squared
  560. (R^2 in cylindrical coordinate system)."""
  561. return self.x() * self.x() + self.y() * self.y()
  562. # ----------------------------------------------------------------------
  563. def perp(self):
  564. """@return the transverse component
  565. (R in cylindrical coordinate system)."""
  566. return math.sqrt(self.perp2())
  567. # ----------------------------------------------------------------------
  568. # Return a random 3D vector
  569. # ----------------------------------------------------------------------
  570. # @staticmethod
  571. # def random():
  572. # cosTheta = 2.0 * random.random() - 1.0
  573. # sinTheta = math.sqrt(1.0 - cosTheta ** 2)
  574. # phi = 2.0 * math.pi * random.random()
  575. # return Vector(math.cos(phi) * sinTheta, math.sin(phi) * sinTheta, cosTheta)
  576. # #===============================================================================
  577. # # Cardinal cubic spline class
  578. # #===============================================================================
  579. # class CardinalSpline:
  580. # def __init__(self, A=0.5):
  581. # # The default matrix is the Catmull-Rom spline
  582. # # which is equal to Cardinal matrix
  583. # # for A = 0.5
  584. # #
  585. # # Note: Vasilis
  586. # # The A parameter should be the fraction in t where
  587. # # the second derivative is zero
  588. # self.setMatrix(A)
  589. #
  590. # #-----------------------------------------------------------------------
  591. # # Set the matrix according to Cardinal
  592. # #-----------------------------------------------------------------------
  593. # def setMatrix(self, A=0.5):
  594. # self.M = []
  595. # self.M.append([ -A, 2.-A, A-2., A ])
  596. # self.M.append([2.*A, A-3., 3.-2.*A, -A ])
  597. # self.M.append([ -A, 0., A, 0.])
  598. # self.M.append([ 0., 1., 0, 0.])
  599. #
  600. # #-----------------------------------------------------------------------
  601. # # Evaluate Cardinal spline at position t
  602. # # @param P list or tuple with 4 points y positions
  603. # # @param t [0..1] fraction of interval from points 1..2
  604. # # @param k index of starting 4 elements in P
  605. # # @return spline evaluation
  606. # #-----------------------------------------------------------------------
  607. # def __call__(self, P, t, k=1):
  608. # T = [t*t*t, t*t, t, 1.0]
  609. # R = [0.0]*4
  610. # for i in range(4):
  611. # for j in range(4):
  612. # R[i] += T[j] * self.M[j][i]
  613. # y = 0.0
  614. # for i in range(4):
  615. # y += R[i]*P[k+i-1]
  616. #
  617. # return y
  618. #
  619. # #-----------------------------------------------------------------------
  620. # # Return the coefficients of a 3rd degree polynomial
  621. # # f(x) = a t^3 + b t^2 + c t + d
  622. # # @return [a, b, c, d]
  623. # #-----------------------------------------------------------------------
  624. # def coefficients(self, P, k=1):
  625. # C = [0.0]*4
  626. # for i in range(4):
  627. # for j in range(4):
  628. # C[i] += self.M[i][j] * P[k+j-1]
  629. # return C
  630. #
  631. # #-----------------------------------------------------------------------
  632. # # Evaluate the value of the spline using the coefficients
  633. # #-----------------------------------------------------------------------
  634. # def evaluate(self, C, t):
  635. # return ((C[0]*t + C[1])*t + C[2])*t + C[3]
  636. #
  637. # #===============================================================================
  638. # # Cubic spline ensuring that the first and second derivative are continuous
  639. # # adapted from Penelope Manual Appending B.1
  640. # # It requires all the points (xi,yi) and the assumption on how to deal
  641. # # with the second derivative on the extremities
  642. # # Option 1: assume zero as second derivative on both ends
  643. # # Option 2: assume the same as the next or previous one
  644. # #===============================================================================
  645. # class CubicSpline:
  646. # def __init__(self, X, Y):
  647. # self.X = X
  648. # self.Y = Y
  649. # self.n = len(X)
  650. #
  651. # # Option #1
  652. # s1 = 0.0 # zero based = s0
  653. # sN = 0.0 # zero based = sN-1
  654. #
  655. # # Construct the tri-diagonal matrix
  656. # A = []
  657. # B = [0.0] * (self.n-2)
  658. # for i in range(self.n-2):
  659. # A.append([0.0] * (self.n-2))
  660. #
  661. # for i in range(1,self.n-1):
  662. # hi = self.h(i)
  663. # Hi = 2.0*(self.h(i-1) + hi)
  664. # j = i-1
  665. # A[j][j] = Hi
  666. # if i+1<self.n-1:
  667. # A[j][j+1] = A[j+1][j] = hi
  668. #
  669. # if i==1:
  670. # B[j] = 6.*(self.d(i) - self.d(j)) - hi*s1
  671. # elif i<self.n-2:
  672. # B[j] = 6.*(self.d(i) - self.d(j))
  673. # else:
  674. # B[j] = 6.*(self.d(i) - self.d(j)) - hi*sN
  675. #
  676. #
  677. # self.s = gauss(A,B)
  678. # self.s.insert(0,s1)
  679. # self.s.append(sN)
  680. # # print ">> s <<"
  681. # # pprint(self.s)
  682. #
  683. # #-----------------------------------------------------------------------
  684. # def h(self, i):
  685. # return self.X[i+1] - self.X[i]
  686. #
  687. # #-----------------------------------------------------------------------
  688. # def d(self, i):
  689. # return (self.Y[i+1] - self.Y[i]) / (self.X[i+1] - self.X[i])
  690. #
  691. # #-----------------------------------------------------------------------
  692. # def coefficients(self, i):
  693. # """return coefficients of cubic spline for interval i a*x**3+b*x**2+c*x+d"""
  694. # hi = self.h(i)
  695. # si = self.s[i]
  696. # si1 = self.s[i+1]
  697. # xi = self.X[i]
  698. # xi1 = self.X[i+1]
  699. # fi = self.Y[i]
  700. # fi1 = self.Y[i+1]
  701. #
  702. # a = 1./(6.*hi)*(si*xi1**3 - si1*xi**3 + 6.*(fi*xi1 - fi1*xi)) + hi/6.*(si1*xi - si*xi1)
  703. # b = 1./(2.*hi)*(si1*xi**2 - si*xi1**2 + 2*(fi1 - fi)) + hi/6.*(si - si1)
  704. # c = 1./(2.*hi)*(si*xi1 - si1*xi)
  705. # d = 1./(6.*hi)*(si1-si)
  706. #
  707. # return [d,c,b,a]
  708. #
  709. # #-----------------------------------------------------------------------
  710. # def __call__(self, i, x):
  711. # # FIXME should interpolate to find the interval
  712. # C = self.coefficients(i)
  713. # return ((C[0]*x + C[1])*x + C[2])*x + C[3]
  714. #
  715. # #-----------------------------------------------------------------------
  716. # # @return evaluation of cubic spline at x using coefficients C
  717. # #-----------------------------------------------------------------------
  718. # def evaluate(self, C, x):
  719. # return ((C[0]*x + C[1])*x + C[2])*x + C[3]
  720. #
  721. # #-----------------------------------------------------------------------
  722. # # Return evaluated derivative at x using coefficients C
  723. # #-----------------------------------------------------------------------
  724. # def derivative(self, C, x):
  725. # a = 3.0*C[0] # derivative coefficients
  726. # b = 2.0*C[1] # ... for sampling with rejection
  727. # c = C[2]
  728. # return (3.0*C[0]*x + 2.0*C[1])*x + C[2]
  729. #