#
# Copyright (c) 2003 Art Haas
#
# This file is part of PythonCAD.
#
# PythonCAD is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# PythonCAD is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with PythonCAD; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# NURBS curves
import array
class Nurb(object):
def __init__(self, ctrlpts, knots, order):
if not isinstance(ctrlpts, list):
raise TypeError, "Invalid control point list: " + str(ctrlpts)
if not isinstance(knots, list):
raise TypeError, "Invalid knot list: " + str(knots)
if not isinstance(order, int):
raise TypeError, "Invalid order; " + str(order)
if order < 2 or order > 16: # what is a good max value?
raise ValueError, "Invalid order: %d" % order
_ctrlpts = []
for _pt in ctrlpts:
if not isinstance(_pt, tuple):
raise TypeError, "Invalid control point: " + str(_pt)
_len = len(_pt)
if not (1 < _len < 4):
raise ValueError, "Invalid tuple length: " + str(_pt)
if _len == 2:
_x, _y = _pt
_w = 1.0
else:
_x, _y, _w = _pt
if not isinstance(_x, float):
_x = float(_x)
if not isinstance(_y, float):
_y = float(_y)
if not isinstance(_w, float):
_w = float(_w)
if not (_w > 0.0):
raise ValueError, "Invalid weight: %g" % _w
_ctrlpts.append((_x, _y, _w))
_knots = []
for _knot in knots:
if not isinstance(_knot, float):
_knot = float(_knot)
if (_knot < 0.0 or _knot > 1.0):
raise ValueError, "Invalid knot value: %g" % _knot
for _val in _knots:
if _knot < (_val - 1e-10):
raise (ValueError, "Invalid decreasing knot: %g < %g" %
(_knot, _val))
_knots.append(_knot)
print "knots: " + str(_knots)
print "ctrl: " + str(_ctrlpts)
print "order: %d" % order
_clen = len(_ctrlpts)
if _clen < order:
raise ValueError, "Order greater than number of control points."
if len(_knots) != (_clen + order):
raise ValueError, "Knot/Control Point/Order number error."
self.__ctrlpts = _ctrlpts
self.__knots = _knots
self.__order = order
def getControlPoints(self):
return self.__ctrlpts[:]
def getKnots(self):
return self.__knots[:]
def getOrder(self):
return self.__order
def calculate(self, count):
if not isinstance(count, int):
raise TypeError, "Invalid count: " + str(count)
_cpts = self.__ctrlpts
_knots = self.__knots
_dt = 1.0/float(count)
_p = self.__order - 1
_pts = []
for _c in range(count):
_t = _c * _dt
# print "time: %g" % _t
_nx = _ny = _nw = 0.0
for _i in range(len(_cpts)):
# print "using cpt %d" % _i
_x, _y, _w = _cpts[_i]
_Ni = self._N(_i, _p, _t)
_nx = _nx + (_Ni * _x)
_ny = _ny + (_Ni * _y)
_nw = _nw + (_Ni * _w)
# print "nw: %.3f" % _nw
# print "nx: %.3f" % _nx
# print "ny: %.3f" % _ny
if abs(_nw) > 1e-10:
_pts.append((_nx/_nw, _ny/_nw))
else:
print "zero weight: %f, %f" % (_nx, _ny)
return _pts
def _N(self, i, p, t):
# print "_N() ..."
_flag = False
if abs(t - 1.0) < 1e-10 and False:
_flag = True
if _flag:
print "i: %d" % i
print "p: %d" % p
print "t: %.3f" % t
_knots = self.__knots
_ki = _knots[i]
_kin = _knots[i + 1]
if _flag:
print "ki: %.3f" % _ki
print "kin: %.3f" % _kin
if p == 0:
if ((_ki - 1e-10) < t < _kin):
_val = 1.0
else:
_val = 0.0
else:
_kip = _knots[i + p]
_kipn = _knots[i + p + 1]
if _flag:
print "kip: %.3f" % _kip
print "kipn: %.3f" % _kipn
_t1 = 0.0
_v1 = _kip - _ki
if abs(_v1) > 1e-10:
_v2 = t - _ki
if abs(_v2) > 1e-10:
_t1 = (_v2/_v1) * self._N(i, (p - 1), t)
_t2 = 0.0
_v1 = _kipn - _kin
if abs(_v1) > 1e-10:
_v2 = _kipn - t
if abs(_v2) > 1e-10:
_t2 = (_v2/_v1) * self._N((i + 1), (p - 1), t)
_val = _t1 + _t2
if _flag:
print "val: %f" % _val
return _val
def writedata(self, count, fname):
if not isinstance(count, int):
raise TypeError, "Invalid count: " + str(count)
_f = file(fname, "w")
for _pt in self.calculate(count):
_x, _y = _pt
_f.write("%f %f\n" % (_x, _y))
_f.close()
_f = file('control_points', "w")
for _pt in self.getControlPoints():
_x, _y, _w = _pt # ignore weight
_f.write("%f %f\n" % (_x, _y))
_f.close()
_f = file('knots', "w")
for _knot in self.getKnots():
_f.write("%f 0.0\n" % _knot)
_f.close()
def _NN(self, i, p, t):
_cpts = self.__ctrlpts
_cl = len(_cpts)
_knots = self.__knots
_kl = len(_knots) - 1
_val = _kl * [0.0]
#
# calculate values for 0
#
for _i in range(_kl):
if ((_knots[i] - 1e-10) < t < _knots[i + 1]):
_val[_i] = 1.0
#
# calculate values up to the degree
#
for _j in range(1, (p + 1)):
for _i in range(_kl - _j):
_ki = _knots[_i]
_kin = _knots[_i + 1]
_kip = _knots[_i + p]
_kipn = _knots[_i + p + 1]
_t1 = 0.0
_n = _val[_i]
if abs(_n) > 1e-10:
_v1 = _kip - _ki
if abs(_v1) > 1e-10:
_v2 = t - _ki
if abs(_v2) > 1e-10:
_t1 = (_v2/_v1) * _n
_t2 = 0.0
_n = _val[_i + 1]
if abs(_n) > 1e-10:
_v1 = _kipn - _kin
if abs(_v1) > 1e-10:
_v2 = _kipn - t
if abs(_v2) > 1e-10:
_t2 = (_v2/_v1) * _n
_val[_i] = _t1 + _t2