bspy 2.2.2__py3-none-any.whl → 3.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
bspy/spline.py CHANGED
@@ -35,16 +35,11 @@ class Spline:
35
35
  coefs : array-like
36
36
  A list of the B-spline coefficients of the spline.
37
37
 
38
- accuracy : `float`, optional
39
- Each spline is presumed to be an approximation of something else.
40
- The `accuracy` stores the infinity norm error of the difference between
41
- the given spline and that something else. Default is zero.
42
-
43
38
  metadata : `dict`, optional
44
39
  A dictionary of ancillary data to store with the spline. Default is {}.
45
40
  """
46
41
 
47
- def __init__(self, nInd, nDep, order, nCoef, knots, coefs, accuracy = 0.0, metadata = {}):
42
+ def __init__(self, nInd, nDep, order, nCoef, knots, coefs, metadata = {}):
48
43
  if not(nInd >= 0): raise ValueError("nInd < 0")
49
44
  self.nInd = int(nInd)
50
45
  if not(nDep >= 0): raise ValueError("nDep < 0")
@@ -74,7 +69,6 @@ class Spline:
74
69
  self.coefs = self.coefs.reshape((*self.nCoef[::-1], self.nDep)).T
75
70
  else:
76
71
  self.coefs = np.array([c.T for c in self.coefs]).reshape((self.nDep, *self.nCoef))
77
- self.accuracy = accuracy
78
72
  self.metadata = dict(metadata)
79
73
 
80
74
  def __call__(self, *uvw, **kwargs):
@@ -82,8 +76,7 @@ class Spline:
82
76
 
83
77
  def __repr__(self):
84
78
  return f"Spline({self.nInd}, {self.nDep}, {self.order}, " + \
85
- f"{self.nCoef}, {self.knots} {self.coefs}, {self.accuracy}, " + \
86
- f"{self.metadata})"
79
+ f"{self.nCoef}, {self.knots} {self.coefs}, {self.metadata})"
87
80
 
88
81
  def __add__(self, other):
89
82
  if isinstance(other, Spline):
@@ -305,14 +298,15 @@ class Spline:
305
298
  """
306
299
  return bspy._spline_domain.clamp(self, left, right)
307
300
 
308
- def common_basis(self, splines, indMap):
301
+ @staticmethod
302
+ def common_basis(splines, indMap = None):
309
303
  """
310
304
  Align a collection of splines to a common basis, elevating the order and adding knots as needed.
311
305
 
312
306
  Parameters
313
307
  ----------
314
308
  splines : `iterable`
315
- The collection of N - 1 splines to align (N total splines, including self).
309
+ The collection of N splines to align.
316
310
 
317
311
  indMap : `iterable`
318
312
  The collection of independent variables to align. Since each spline can have multiple
@@ -321,6 +315,7 @@ class Spline:
321
315
  The domains of mapped independent variables must match.
322
316
  An independent variable can map to no more than one other independent variable.
323
317
  If all the splines are curves (1 independent variable), then `indMap` is ((0, 0, .. 0),).
318
+ If indMap is None (the default), then it is set to [N * [i] for i in range(nInd)]
324
319
 
325
320
  Returns
326
321
  -------
@@ -335,7 +330,47 @@ class Spline:
335
330
  -----
336
331
  Uses `elevate_and_insert_knots` to ensure mapped variables share the same order and knots.
337
332
  """
338
- return bspy._spline_domain.common_basis(self, splines, indMap)
333
+ return bspy._spline_domain.common_basis(splines, indMap)
334
+
335
+ @staticmethod
336
+ def cone(radius1, radius2, height, tolerance = None):
337
+ """
338
+ Construct a cone of the two given radii and height.
339
+
340
+ Parameters
341
+ ----------
342
+ radius1 : scalar
343
+ The desired radius of the cone at the bottom
344
+
345
+ radius2 : scalar
346
+ The desired radius of the cone at the top
347
+
348
+ height : scalar
349
+ The desired height of the cone
350
+
351
+ tolerance : scalar, optional
352
+ The desired absolute tolerance to which the cylinder should be constructed. Defaults
353
+ to 1.0e-12 if tolerance == None.
354
+
355
+ Returns
356
+ -------
357
+ spline : `Spline`
358
+ A bi-quartic spline approximation to a cone of the specified radii and height,
359
+ accurate to the given tolerance.
360
+
361
+ Notes
362
+ -----
363
+ The resulting mapping is defined over the unit square. The first independent variable
364
+ corresponds to the radial position on the cone, while the second independent variable
365
+ is the location along the height.
366
+
367
+ See Also
368
+ --------
369
+ `circular_arc` : Create a 2D circular arc for a given radius and angle accurate to within
370
+ a given tolerance.
371
+ `ruled_surface` : Construct a ruled surface between two curves.
372
+ """
373
+ return bspy._spline_fitting.cone(radius1, radius2, height, tolerance)
339
374
 
340
375
  def confine(self, range_bounds):
341
376
  """
@@ -390,8 +425,7 @@ class Spline:
390
425
 
391
426
  epsilon : `float`, optional
392
427
  Tolerance for contour precision. Evaluating `F` with contour values will be within epsilon
393
- of zero. The default is square root of machine epsilon. The actual accuracy of the contour
394
- is returned as `spline.accuracy`.
428
+ of zero. The default is square root of machine epsilon.
395
429
 
396
430
  metadata : `dict`, optional
397
431
  A dictionary of ancillary data to store with the spline. Default is {}.
@@ -517,7 +551,7 @@ class Spline:
517
551
  spline : `Spline`
518
552
  The spline copy.
519
553
  """
520
- return type(self)(self.nInd, self.nDep, self.order, self.nCoef, self.knots, self.coefs, self.accuracy, metadata)
554
+ return type(self)(self.nInd, self.nDep, self.order, self.nCoef, self.knots, self.coefs, metadata)
521
555
 
522
556
  def cross(self, vector):
523
557
  """
@@ -560,6 +594,43 @@ class Spline:
560
594
  """
561
595
  return bspy._spline_evaluation.curvature(self, uvw)
562
596
 
597
+ @staticmethod
598
+ def cylinder(radius, height, tolerance = None):
599
+ """
600
+ Construct a cylinder of the given radius and height.
601
+
602
+ Parameters
603
+ ----------
604
+ radius : scalar
605
+ The desired radius of the cylinder
606
+
607
+ height : scalar
608
+ The desired height of the cylinder
609
+
610
+ tolerance : scalar, optional
611
+ The desired absolute tolerance to which the cylinder should be constructed. Defaults
612
+ to 1.0e-12 if tolerance == None.
613
+
614
+ Returns
615
+ -------
616
+ spline : `Spline`
617
+ A bi-quartic spline approximation to a cylinder of the specified radius and height,
618
+ accurate to the given tolerance.
619
+
620
+ Notes
621
+ -----
622
+ The resulting mapping is defined over the unit square. The first independent variable
623
+ corresponds to the radial position on the cylinder, while the second independent variable
624
+ is the location along the height.
625
+
626
+ See Also
627
+ --------
628
+ `circular_arc` : Create a 2D circular arc for a given radius and angle accurate to within
629
+ a given tolerance.
630
+ `ruled_surface` : Construct a ruled surface between two curves.
631
+ """
632
+ return bspy._spline_fitting.cylinder(radius, height, tolerance)
633
+
563
634
  def derivative(self, with_respect_to, *uvw, **kwargs):
564
635
  """
565
636
  Compute the derivative of the spline at given parameter values.
@@ -597,11 +668,18 @@ class Spline:
597
668
  evaluated, then the dot product of those B-splines with the vector of
598
669
  B-spline coefficients is computed.
599
670
  """
600
- if len(uvw) > 1 or (not np.isscalar(uvw[0]) and len(uvw[0]) > self.nInd):
671
+ if len(uvw) == 0 and self.nInd == 0:
672
+ return np.zeros(self.nDep, self.coefs.dtype)
673
+ elif np.isscalar(uvw[0]):
674
+ return bspy._spline_evaluation.derivative(self, with_respect_to, uvw)
675
+ elif len(uvw) > 1 or len(uvw[0]) > self.nInd:
601
676
  def vectorized(*uvwInstance):
602
677
  return tuple(bspy._spline_evaluation.derivative(self, with_respect_to, uvwInstance))
603
678
  uFunc = np.frompyfunc(vectorized, self.nInd, self.nDep)
604
- return uFunc(*uvw, **kwargs)
679
+ if self.nDep > 1:
680
+ return tuple([a.astype(self.coefs.dtype, copy=False) for a in uFunc(*uvw, **kwargs)])
681
+ else:
682
+ return np.array([x[0] for x in uFunc(*uvw, **kwargs)], self.coefs.dtype)
605
683
  else:
606
684
  return bspy._spline_evaluation.derivative(self, with_respect_to, *uvw)
607
685
 
@@ -760,11 +838,16 @@ class Spline:
760
838
  """
761
839
  if len(uvw) == 0 and self.nInd == 0:
762
840
  return self.coefs
763
- if len(uvw) > 1 or (not np.isscalar(uvw[0]) and len(uvw[0]) > self.nInd):
841
+ elif np.isscalar(uvw[0]):
842
+ return bspy._spline_evaluation.evaluate(self, uvw)
843
+ elif len(uvw) > 1 or len(uvw[0]) > self.nInd:
764
844
  def vectorized(*uvwInstance):
765
845
  return tuple(bspy._spline_evaluation.evaluate(self, uvwInstance))
766
846
  uFunc = np.frompyfunc(vectorized, self.nInd, self.nDep)
767
- return uFunc(*uvw, **kwargs)
847
+ if self.nDep > 1:
848
+ return tuple([a.astype(self.coefs.dtype, copy=False) for a in uFunc(*uvw, **kwargs)])
849
+ else:
850
+ return np.array([x[0] for x in uFunc(*uvw, **kwargs)], self.coefs.dtype)
768
851
  else:
769
852
  return bspy._spline_evaluation.evaluate(self, *uvw)
770
853
 
@@ -1091,41 +1174,72 @@ class Spline:
1091
1174
  return bspy._spline_domain.join(splineList)
1092
1175
 
1093
1176
  @staticmethod
1094
- def least_squares(nInd, nDep, order, dataPoints, knots = None, compression = 0, metadata = {}):
1177
+ def least_squares(uValues, dataPoints, order = None, knots = None, compression = 0.0,
1178
+ tolerance = None, fixEnds = False, metadata = {}):
1095
1179
  """
1096
1180
  Fit a spline to an array of data points using the method of least squares.
1097
1181
 
1098
1182
  Parameters
1099
1183
  ----------
1100
- nInd : `int`
1101
- The number of independent variables of the spline.
1184
+ uValues : `iterable` of array-like values
1185
+ The values of the independent variables in each of the coordinate directions for
1186
+ each of the data points. It's either a simple array of length nU or it is an array
1187
+ of length nInd of arrays with corresponding lengths nU, nV, . . ., nW, where each
1188
+ length indicates the number of data points in that independent variable. For each
1189
+ independent variable, the values must be ordered, i.e. uValue[i] <= uValue[i+1]. The
1190
+ corresponding point in dataPoints will be an array of length nDep which represents the
1191
+ desired function values at that point. If the values in uValues are repeated, then
1192
+ the corresponding data in dataPoints represents a derivative value. Examples:
1193
+
1194
+ If uValues == [0.0, 1.0, 1.0, 2.0, 3.0, 3.0, 3.0], then nInd == 1 and dataPoints
1195
+ contains
1196
+ [f(0.0), f(1.0), f'(1.0), f(2.0), f(3.0), f'(3.0), f''(3.0)]
1102
1197
 
1103
- nDep : `int`
1104
- The number of dependent variables of the spline.
1105
-
1106
- order : `tuple`
1107
- A tuple of length nInd where each integer entry represents the
1108
- polynomial order of the spline in that variable. When in doubt,
1109
- use `[4] * nInd` (a cubic spline).
1198
+ if uValues == [[0.0, 1.0, 1.0, 2.0], [0.0, 1.0, 2.0, 2.0, 3.0]], then nInd == 2 and
1199
+ dataPoints contains
1200
+ [[f(0,0), f(0,1), f(0,2), f_v(0,2), f(0,3)],
1201
+ [f(1,0), f(1,1), f(1,2), f_v(1,2), f(1,3)],
1202
+ [f_u(1,0), f_u(1,1), f_u(1,2), f_uv(1,2), f_u(1,3)],
1203
+ [f(2,0), f(2,1), f(2,2), f_v(2,2), f(2,3)]]
1110
1204
 
1111
1205
  dataPoints : `iterable` of array-like values
1112
- A collection of data points. Each data point is an array of length `nInd + nDep`.
1113
- The first `nInd` values designate the independent values for the point.
1114
- The next `nDep` values designate the dependent values for the point.
1115
- The data points need not form a regular mesh nor be in any particular order.
1116
- In addition, each data point may instead have `nInd + nDep * (nInd + 1)` values,
1117
- the first `nInd` being independent and the next `nInd + 1` sets of `nDep` values designating
1118
- the dependent point and its first derivatives with respect to each independent variable.
1206
+ A collection of data points. The shape of this array mirrors the shape of
1207
+ Spline.coefs, i.e. it has shape (nDep, nU, nV, . . . , nW). The indices into this
1208
+ array also index into uValues to retrieve the independent variable values.
1209
+ For example, if nInd == 2 and nDep == 3, then the shape of dataPoints is (3, nU, nV)
1210
+ and dataPoint[:,i,j] has independent variable values given by uValues[0,i] and
1211
+ uValues[1,j].
1212
+
1213
+ order : `tuple`
1214
+ A tuple of length nInd where each integer entry represents the
1215
+ polynomial order of the spline in that variable. If order == None, then
1216
+ order == [4] * nInd is assumed.
1119
1217
 
1120
1218
  knots : `list`, optional
1121
- A list of the lists of the knots of the spline in each independent variable.
1122
- Default is `None`, in which case knots are chosen automatically.
1219
+ A list of the lists of the knots of the spline in each independent variable. The
1220
+ length of knots should be nInd if it is specified, where each of the arrays specify
1221
+ the knots of the spline to use in that independent variable. All of the dataPoints
1222
+ must lie within the domain specified by the knots. If knots == None (the default),
1223
+ then the knots are automatically determined.
1224
+
1225
+ compression : scalar, optional
1226
+ The desired compression of data used as a fraction of the number of data points, i.e
1227
+ compression is a number between 0 and 1. This fraction is used to determine the
1228
+ total number of spline coefficients when knots are chosen automatically (it's if the
1229
+ knots are specified). The actual compression will be slightly less because the number
1230
+ of coefficients is rounded up. The default value is zero (interpolation, no compression).
1231
+
1232
+ tolerance : scalar, optional
1233
+ If tolerance is specified, then a fit to tolerance is performed. Using the given knots
1234
+ as the initial set of knots, additional knots are inserted until the 2-norm of the
1235
+ difference between each of the each given dataPoints and the vector given by the
1236
+ spline is no larger than the specified tolerance.
1123
1237
 
1124
- compression : `int`, optional
1125
- The desired compression of data used as a percentage of the number of data points (0 - 99).
1126
- This percentage is used to determine the total number of spline coefficients when
1127
- knots are chosen automatically (it's ignored otherwise). The actual compression will be slightly less
1128
- because the number of coefficients is rounded up. The default value is zero (interpolation, no compression).
1238
+ fixEnds : Boolean, optional
1239
+ if fixEnds is True, then all of the dataPoints which lie on any of the domain
1240
+ boundaries of the spline become interpolation conditions, i.e. the spline will be
1241
+ fit in such a way that those boundary points are exactly satisfied by the computed
1242
+ spline. Otherwise, least-squares fitting will be performed on all of the dataPoints.
1129
1243
 
1130
1244
  metadata : `dict`, optional
1131
1245
  A dictionary of ancillary data to store with the spline. Default is {}.
@@ -1137,12 +1251,10 @@ class Spline:
1137
1251
 
1138
1252
  Notes
1139
1253
  -----
1140
- Uses `numpy.linalg.lstsq` to compute the least squares solution. The returned spline.accuracy is computed
1141
- from the sum of the residual across dependent variables and the system epsilon.
1142
- The algorithm to choose knots automatically is from Piegl, Les A., and Wayne Tiller.
1143
- "Surface approximation to scanned data." The visual computer 16 (2000): 386-395.
1254
+ Uses `numpy.linalg.lstsq` to compute the least squares solution.
1144
1255
  """
1145
- return bspy._spline_fitting.least_squares(nInd, nDep, order, dataPoints, knots, compression, metadata)
1256
+ return bspy._spline_fitting.least_squares(uValues, dataPoints, order, knots, compression,
1257
+ tolerance, fixEnds, metadata)
1146
1258
 
1147
1259
  @staticmethod
1148
1260
  def line(startPoint, endPoint):
@@ -1175,7 +1287,7 @@ class Spline:
1175
1287
  @staticmethod
1176
1288
  def load(fileName):
1177
1289
  """
1178
- Load spline(s) in json format from the specified filename (full path).
1290
+ Load splines in json format from the specified filename (full path).
1179
1291
 
1180
1292
  Parameters
1181
1293
  ----------
@@ -1184,8 +1296,8 @@ class Spline:
1184
1296
 
1185
1297
  Returns
1186
1298
  -------
1187
- spline(s) : `Spline` or list of `Spline`
1188
- The loaded spline(s).
1299
+ splines : list of `Spline`
1300
+ The loaded splines.
1189
1301
 
1190
1302
  See Also
1191
1303
  --------
@@ -1200,7 +1312,7 @@ class Spline:
1200
1312
  for i in range(nInd):
1201
1313
  knots.append(kw[f"knots{i}"])
1202
1314
  coefficients = kw["coefficients"]
1203
- return Spline(nInd, coefficients.shape[0], order, coefficients.shape[1:], knots, coefficients, metadata=dict(Path=path, Name=path.splitext(path.split(fileName)[1])[0]))
1315
+ return [Spline(nInd, coefficients.shape[0], order, coefficients.shape[1:], knots, coefficients, metadata=dict(Path=path, Name=path.splitext(path.split(fileName)[1])[0]))]
1204
1316
  except:
1205
1317
  pass # Do nothing, an error is expected for json files
1206
1318
 
@@ -1208,15 +1320,13 @@ class Spline:
1208
1320
  with open(fileName, 'r', encoding='utf-8') as file:
1209
1321
  splineData = json.load(file)
1210
1322
 
1323
+ splines = []
1211
1324
  if isinstance(splineData, dict):
1212
- return Spline(splineData["nInd"], splineData["nDep"], splineData["order"], splineData["nCoef"],
1213
- [np.array(knots) for knots in splineData["knots"]], np.array(splineData["coefs"]), splineData["accuracy"], splineData["metadata"])
1214
- else:
1215
- splines = []
1216
- for splineDict in splineData:
1217
- splines.append(Spline(splineDict["nInd"], splineDict["nDep"], splineDict["order"], splineDict["nCoef"],
1218
- [np.array(knots) for knots in splineDict["knots"]], np.array(splineDict["coefs"]), splineDict["accuracy"], splineDict["metadata"]))
1219
- return splines
1325
+ splineData = [splineData]
1326
+ for splineDict in splineData:
1327
+ splines.append(Spline(splineDict["nInd"], splineDict["nDep"], splineDict["order"], splineDict["nCoef"],
1328
+ [np.array(knots) for knots in splineDict["knots"]], np.array(splineDict["coefs"]), splineDict["metadata"]))
1329
+ return splines
1220
1330
 
1221
1331
  def multiply(self, other, indMap = None, productType = 'S'):
1222
1332
  """
@@ -1479,6 +1589,34 @@ class Spline:
1479
1589
  """
1480
1590
  return bspy._spline_fitting.revolve(self, angle)
1481
1591
 
1592
+ def rotate(self, vector, angle):
1593
+ """
1594
+ Rotate a spline around a vector by the specified angle.
1595
+
1596
+ Parameters
1597
+ ----------
1598
+ vector : array-like
1599
+ A vector of length three about which to rotate the spline
1600
+
1601
+ angle : scalar
1602
+ The number of degrees to rotate the spline around vector by
1603
+
1604
+ Returns
1605
+ -------
1606
+ spline : `Spline`
1607
+ The rotated spline
1608
+
1609
+ See Also
1610
+ --------
1611
+ `transform` : Multiply a spline by a given matrix.
1612
+
1613
+ Notes
1614
+ -----
1615
+ Uses the well-known Rodrigues' rotation formula and mathematical operations
1616
+ on splines.
1617
+ """
1618
+ return bspy._spline_operations.rotate(self, vector, angle)
1619
+
1482
1620
  @staticmethod
1483
1621
  def ruled_surface(spline1, spline2):
1484
1622
  """
@@ -1513,7 +1651,7 @@ class Spline:
1513
1651
 
1514
1652
  See Also
1515
1653
  --------
1516
- `load` : Load spline(s) in json format from the specified filename (full path).
1654
+ `load` : Load splines in json format from the specified filename (full path).
1517
1655
  """
1518
1656
  class SplineEncoder(json.JSONEncoder):
1519
1657
  def default(self, obj):
@@ -1527,7 +1665,6 @@ class Spline:
1527
1665
  "nCoef" : obj.nCoef,
1528
1666
  "knots" : obj.knots,
1529
1667
  "coefs" : obj.coefs,
1530
- "accuracy" : obj.accuracy,
1531
1668
  "metadata" : obj.metadata
1532
1669
  }
1533
1670
  return super().default(obj)
@@ -1708,7 +1845,7 @@ class Spline:
1708
1845
  """
1709
1846
  return bspy._spline_fitting.torus(innerRadius, outerRadius, tolerance)
1710
1847
 
1711
- def transform(self, matrix, maxSingularValue=None):
1848
+ def transform(self, matrix):
1712
1849
  """
1713
1850
  Transform a spline by the given matrix.
1714
1851
 
@@ -1717,10 +1854,6 @@ class Spline:
1717
1854
  matrix : array-like
1718
1855
  An array of size `newNDep`x`nDep` that specifies the transform matrix.
1719
1856
 
1720
- maxSingularValue : `float`, optional
1721
- The largest singular value of `matrix`, used to update the accuracy of the spline.
1722
- If no value is provided (default), the largest singular value is computed.
1723
-
1724
1857
  Returns
1725
1858
  -------
1726
1859
  spline : `Spline`
@@ -1735,7 +1868,7 @@ class Spline:
1735
1868
  -----
1736
1869
  Equivalent to spline @ matrix.
1737
1870
  """
1738
- return bspy._spline_operations.transform(self, matrix, maxSingularValue)
1871
+ return bspy._spline_operations.transform(self, matrix)
1739
1872
 
1740
1873
  def translate(self, translationVector):
1741
1874
  """
@@ -1855,7 +1988,7 @@ class Spline:
1855
1988
  ----------
1856
1989
  epsilon : `float`, optional
1857
1990
  Tolerance for root precision. The root will be within epsilon of the actual root.
1858
- The default is the max of spline accuracy and machine epsilon.
1991
+ The default is the machine epsilon.
1859
1992
 
1860
1993
  Returns
1861
1994
  -------
bspy/splineOpenGLFrame.py CHANGED
@@ -2,13 +2,16 @@ import numpy as np
2
2
  import tkinter as tk
3
3
  from OpenGL.GL import *
4
4
  import OpenGL.GL.shaders as shaders
5
- from pyopengltk import OpenGLFrame
5
+ try:
6
+ from pyopengltk import OpenGLFrame
7
+ except ImportError:
8
+ from tkinter import Frame as OpenGLFrame
6
9
  from bspy import DrawableSpline
7
10
  from bspy.drawableSpline import _set_color
8
11
 
9
12
  class SplineOpenGLFrame(OpenGLFrame):
10
13
  """
11
- A tkinter `OpenGLFrame` with shaders to display a `DrawableSpline` list.
14
+ A tkinter `OpenGLFrame` with shaders to display a `DrawableSpline`.
12
15
  """
13
16
 
14
17
  ROTATE = 1
@@ -939,10 +942,10 @@ class SplineOpenGLFrame(OpenGLFrame):
939
942
  }
940
943
  """
941
944
 
942
- def __init__(self, *args, eye=(0.0, 0.0, 3.0), center=(0.0, 0.0, 0.0), up=(0.0, 1.0, 0.0), **kw):
945
+ def __init__(self, *args, eye=(0.0, 0.0, 3.0), center=(0.0, 0.0, 0.0), up=(0.0, 1.0, 0.0), draw_func=None, **kw):
943
946
  OpenGLFrame.__init__(self, *args, **kw)
944
947
 
945
- self.splineDrawList = []
948
+ self.draw_func = draw_func
946
949
  self.animating = False
947
950
  self.animate = 0 # Set to number of milliseconds before showing next frame (0 means no animation)
948
951
  self.frameCount = 0
@@ -994,12 +997,6 @@ class SplineOpenGLFrame(OpenGLFrame):
994
997
  self.backgroundColor = _set_color(r, g, b, a)
995
998
  if self.glInitialized:
996
999
  glClearColor(self.backgroundColor[0], self.backgroundColor[1], self.backgroundColor[2], self.backgroundColor[3])
997
-
998
- def SetSplineList(self, list):
999
- """
1000
- Set the `DrawableSpline` list which determines the splines to display.
1001
- """
1002
- self.splineDrawList = list
1003
1000
 
1004
1001
  def ResetView(self):
1005
1002
  """
@@ -1174,8 +1171,8 @@ class SplineOpenGLFrame(OpenGLFrame):
1174
1171
  (self.horizon[2], self.vertical[2], self.look[2], 0.0),
1175
1172
  (-np.dot(self.horizon, self.eye), -np.dot(self.vertical, self.eye), -np.dot(self.look, self.eye), 1.0)), np.float32)
1176
1173
 
1177
- for spline in self.splineDrawList:
1178
- spline._Draw(self, transform)
1174
+ if self.draw_func is not None:
1175
+ self.draw_func(self, transform)
1179
1176
 
1180
1177
  glFlush()
1181
1178
 
@@ -1192,7 +1189,10 @@ class SplineOpenGLFrame(OpenGLFrame):
1192
1189
  """
1193
1190
  Update the frame, typically after updating the spline list.
1194
1191
  """
1195
- self.tkExpose(None)
1192
+ try:
1193
+ self.tkExpose(None)
1194
+ except AttributeError:
1195
+ pass
1196
1196
 
1197
1197
  def Reset(self):
1198
1198
  """
@@ -1216,14 +1216,22 @@ class SplineOpenGLFrame(OpenGLFrame):
1216
1216
 
1217
1217
  def SetScale(self, scale):
1218
1218
  """
1219
- Set the flying speed scale.
1219
+ Set anchor distance and/or flying speed (depending on mode).
1220
1220
 
1221
1221
  Parameters
1222
1222
  ----------
1223
1223
  scale : `float`
1224
- Speed scale between 0 and 1.
1224
+ Scale between 0 and 1.
1225
1225
  """
1226
- self.speed = 0.033 * self.anchorDistance * (100.0 ** float(scale) - 1.0) / 99.0
1226
+ if self.mode == self.FLY:
1227
+ self.speed = 0.033 * self.anchorDistance * (100.0 ** float(scale) - 1.0) / 99.0
1228
+ else:
1229
+ defaultAnchorDistance = np.linalg.norm(self.defaultEye - self.defaultCenter)
1230
+ self.anchorDistance = 2.0 * float(scale) * defaultAnchorDistance
1231
+ self.anchorDistance = max(self.anchorDistance, 0.01)
1232
+ self.speed = 0.033 * self.anchorDistance
1233
+ self.eye = self.anchorPosition + self.anchorDistance * self.look
1234
+ self.Update()
1227
1235
 
1228
1236
  def SetAnimating(self, animating):
1229
1237
  self.animating = animating
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: bspy
3
- Version: 2.2.2
4
- Summary: Library for manipulating and rendering non-uniform b-splines
3
+ Version: 3.0.1
4
+ Summary: Library for manipulating and rendering non-uniform B-splines
5
5
  Home-page: http://github.com/ericbrec/BSpy
6
6
  Author: Eric Brechner
7
7
  Author-email: ericbrec@msn.com
@@ -32,12 +32,12 @@ Requires-Dist: pyopengltk
32
32
  Library for manipulating and rendering B-spline curves, surfaces, and multidimensional manifolds with non-uniform knots in each dimension.
33
33
 
34
34
  The [Spline](https://ericbrec.github.io/BSpy/bspy/spline.html) class has a method to fit multidimensional data for
35
- scalar and vector functions of single and multiple variables. It also has methods to create points, lines, circular arcs, spheres, tori, ruled surfaces, surfaces of revolution, and four-sided patches.
36
- Other methods add, subtract, multiply, and linearly transform splines, as well as confine spline curves to a given range.
37
- There are methods to evaluate spline values, derivatives, integrals, normals, curvature, and the Jacobian, as well as methods that return spline representations of derivatives, normals, integrals, graphs, and convolutions. In addition, there are methods to manipulate the domain of splines, including trim, join, reparametrize, transpose, reverse, add and remove knots, elevate and extrapolate, and fold and unfold. Finally, there are methods to compute the zeros and contours of a spline and to intersect two splines.
35
+ scalar and vector functions of single and multiple variables. It also has methods to create points, lines, circular arcs, spheres, cones, cylinders, tori, ruled surfaces, surfaces of revolution, and four-sided patches.
36
+ Other methods add, subtract, and multiply splines, as well as confine spline curves to a given range.
37
+ There are methods to evaluate spline values, derivatives, integrals, normals, curvature, and the Jacobian, as well as methods that return spline representations of derivatives, normals, integrals, graphs, and convolutions. In addition, there are methods to manipulate the domain of splines, including trim, join, reparametrize, transpose, reverse, add and remove knots, elevate and extrapolate, and fold and unfold. There are methods to manipulate the range of splines, including dot product, cross product, translate, rotate, scale, and transform. Finally, there are methods to compute the zeros and contours of a spline and to intersect two splines.
38
38
 
39
39
  The [SplineOpenGLFrame](https://ericbrec.github.io/BSpy/bspy/splineOpenGLFrame.html) class is an
40
- [OpenGLFrame](https://pypi.org/project/pyopengltk/) with custom shaders to render spline curves and surfaces.
40
+ [OpenGLFrame](https://pypi.org/project/pyopengltk/) with custom shaders to render spline curves and surfaces. Only tested on Windows systems.
41
41
 
42
42
  The [DrawableSpline](https://ericbrec.github.io/BSpy/bspy/drawableSpline.html) class converts a
43
43
  [Spline](https://ericbrec.github.io/BSpy/bspy/spline.html) to a curve, surface, or solid that can be drawn in a
@@ -48,14 +48,20 @@ Spline surfaces and solids with more than 3 dependent variables will have their
48
48
  The [bspyApp](https://ericbrec.github.io/BSpy/bspy/bspyApp.html) class is a
49
49
  [tkinter.Tk](https://docs.python.org/3/library/tkinter.html) app that hosts a
50
50
  [SplineOpenGLFrame](https://ericbrec.github.io/BSpy/bspy/splineOpenGLFrame.html),
51
- a listbox full of splines, and a set of controls to adjust and view the selected splines.
51
+ a listbox full of splines, and a set of controls to adjust and view the selected splines. Only tested on Windows systems.
52
52
 
53
53
  The [bspyGraphics](https://ericbrec.github.io/BSpy/bspy/bspyApp.html#bspyGraphics) class is a graphics engine to display splines.
54
54
  It launches a [bspyApp](https://ericbrec.github.io/BSpy/bspy/bspyApp.html) and issues commands to the app for use
55
- in [jupyter](https://jupyter.org/) notebooks and other scripting environments.
55
+ in [jupyter](https://jupyter.org/) notebooks and other scripting environments. Only tested on Windows systems.
56
56
 
57
57
  ![bspyApp rendering the Utah teapot](https://ericbrec.github.io/BSpy/bspyApp.png "bspyApp rendering the Utah teapot")
58
58
 
59
59
  The full documentation for BSpy can be found [here](https://ericbrec.github.io/BSpy), its GitHub project can be found
60
60
  [here](https://github.com/ericbrec/BSpy), a test suite can be found [here](https://github.com/ericbrec/BSpy/tree/main/tests), and
61
61
  a set of examples, including a jupyter notebook, can be found [here](https://github.com/ericbrec/BSpy/tree/main/examples).
62
+
63
+ ### Release 3.0 breaking changes
64
+ * Removed accuracy as a member of Spline
65
+ * Spline.common_basis is now a static method (see documentation for details)
66
+ * Spline.least_squares changed arguments (see documentation for details)
67
+ * Spline.load always returns a list of splines
@@ -0,0 +1,15 @@
1
+ bspy/__init__.py,sha256=6COMVL3vEPGglirXvBWB9vadEMhj0tetOj7134im5wY,851
2
+ bspy/_spline_domain.py,sha256=rES03UdvxB6yu_ccumcckLR0xX51C6Dv_-nK0wuTlGg,28689
3
+ bspy/_spline_evaluation.py,sha256=rZLqruff8BT6MZdlfV5MBFzSnYuJgzgbpxk1W0mRHeI,7892
4
+ bspy/_spline_fitting.py,sha256=P6rwnIdjjrC_tKqWgIIh5OHl48z04MJBT0QMh18Dvyk,31797
5
+ bspy/_spline_intersection.py,sha256=ArdRkYMUc4JWRuj0CfhdGSWyBs_SBDnbvZtwb5Elo4U,40865
6
+ bspy/_spline_operations.py,sha256=eHSRSd7ez6WrjtM-YlvKjn-LRcaRSObIdf9b8eV1chE,42898
7
+ bspy/bspyApp.py,sha256=M7Uc3TL6GcuqwdWeTdQTH79yKdQ_eHD16ed3xWJipIg,18746
8
+ bspy/drawableSpline.py,sha256=tM54Tk11qwGextRDCCkMn-NJUFp_XtlPkUK9X4Xxyyo,25076
9
+ bspy/spline.py,sha256=clO9hxqrjImWDGW7dkDu_vqUGgLEKi5tYKlEVkM044A,84589
10
+ bspy/splineOpenGLFrame.py,sha256=Fns8QjTiVX7jyolhwpty6RqoUE9RtPs8pHkDi1SlwFg,64643
11
+ bspy-3.0.1.dist-info/LICENSE,sha256=nLfJULN68Jw6GfCJp4xeMksGuRdyWNdgEsZGjw2twig,1091
12
+ bspy-3.0.1.dist-info/METADATA,sha256=gEk1OHK15MToVxRcDTeB_4igZzkBo5O39_9pAODUKo8,4688
13
+ bspy-3.0.1.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
14
+ bspy-3.0.1.dist-info/top_level.txt,sha256=fotZnJn6aCwgUbBEV3hslIko7Nw-eqtHLq2eyJLlFsY,5
15
+ bspy-3.0.1.dist-info/RECORD,,
@@ -1,15 +0,0 @@
1
- bspy/__init__.py,sha256=XRBelbYtbhB6AWKh8DTsWVMDIteVxUm7Cf_S1fkqeEs,851
2
- bspy/_spline_domain.py,sha256=EF6f6y1aMSrDf65UoqQwmBOarTO79TO-hszZAFpxeJw,28821
3
- bspy/_spline_evaluation.py,sha256=1aCf4Op5zQNv-_wW2IOR13MieuYPVJrN1cJCBiI8AaY,7534
4
- bspy/_spline_fitting.py,sha256=IT89LPainTWoHtzBK6s2VnWiM1c76zzZLDJmsPFlSNA,31369
5
- bspy/_spline_intersection.py,sha256=gWs22hfIKqD6YZaeFWJO_3t0ohCNzVvxdiWJqVNlJMI,40934
6
- bspy/_spline_operations.py,sha256=javGKxiSPomhTruJU3ojJvGASoblZnjZG8MGQ3PrOHQ,42400
7
- bspy/bspyApp.py,sha256=Q_TAdKEtRgHzdbp9qj1xPq9oACQyRNnJXnGGvkiVQks,19051
8
- bspy/drawableSpline.py,sha256=lLWUjrgNROJ41wtl_XwAL-2RlKdMFo5SDsT5FopI3Xk,25131
9
- bspy/spline.py,sha256=RRJZRdsEE9_tSTTP980NnIUpmMYqMR4WGN2F4qZkHII,79308
10
- bspy/splineOpenGLFrame.py,sha256=h-XDC-KEcjiC8pNj9cqWBjZgVDiI6mOq1jljfk6fmV4,64194
11
- bspy-2.2.2.dist-info/LICENSE,sha256=nLfJULN68Jw6GfCJp4xeMksGuRdyWNdgEsZGjw2twig,1091
12
- bspy-2.2.2.dist-info/METADATA,sha256=QQSRHSYTE03xFgwMLhrXguPDRpdqms-6Yy4n3wroKU4,4183
13
- bspy-2.2.2.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
14
- bspy-2.2.2.dist-info/top_level.txt,sha256=fotZnJn6aCwgUbBEV3hslIko7Nw-eqtHLq2eyJLlFsY,5
15
- bspy-2.2.2.dist-info/RECORD,,
File without changes
File without changes