def insert_knot(self, direction, parameter, count=1): if direction == SvNurbsSurface.U: new_points = [] new_weights = [] new_u_degree = None for i in range(self.get_control_points().shape[1]): fixed_v_points = self.get_control_points()[:, i] fixed_v_weights = self.get_weights()[:, i] fixed_v_curve = SvNurbsMaths.build_curve( SvNurbsMaths.NATIVE, self.degree_u, self.knotvector_u, fixed_v_points, fixed_v_weights) fixed_v_curve = fixed_v_curve.insert_knot(parameter, count) fixed_v_knotvector = fixed_v_curve.get_knotvector() new_u_degree = fixed_v_curve.get_degree() fixed_v_points = fixed_v_curve.get_control_points() fixed_v_weights = fixed_v_curve.get_weights() new_points.append(fixed_v_points) new_weights.append(fixed_v_weights) new_points = np.transpose(np.array(new_points), axes=(1, 0, 2)) new_weights = np.array(new_weights).T return SvNativeNurbsSurface(new_u_degree, self.degree_v, fixed_v_knotvector, self.knotvector_v, new_points, new_weights) elif direction == SvNurbsSurface.V: new_points = [] new_weights = [] new_v_degree = None for i in range(self.get_control_points().shape[0]): fixed_u_points = self.get_control_points()[i, :] fixed_u_weights = self.get_weights()[i, :] fixed_u_curve = SvNurbsMaths.build_curve( SvNurbsMaths.NATIVE, self.degree_v, self.knotvector_v, fixed_u_points, fixed_u_weights) fixed_u_curve = fixed_u_curve.insert_knot(parameter, count) fixed_u_knotvector = fixed_u_curve.get_knotvector() new_v_degree = fixed_u_curve.get_degree() fixed_u_points = fixed_u_curve.get_control_points() fixed_u_weights = fixed_u_curve.get_weights() new_points.append(fixed_u_points) new_weights.append(fixed_u_weights) new_points = np.array(new_points) new_weights = np.array(new_weights) return SvNativeNurbsSurface(self.degree_u, new_v_degree, self.knotvector_u, fixed_u_knotvector, new_points, new_weights) else: raise Exception("Unsupported direction")
def to_nurbs(self, implementation=SvNurbsMaths.NATIVE): knotvector = sv_knotvector.generate(3, 4) control_points = np.array([self.p0, self.p1, self.p2, self.p3]) return SvNurbsMaths.build_curve(implementation, degree=3, knotvector=knotvector, control_points=control_points)
def _arc_to_nurbs(self, t_min, t_max, implementation=SvNurbsMaths.NATIVE): alpha = t_max - t_min p0_x = cos(t_min) p0_y = sin(t_min) p2_x = cos(t_max) p2_y = sin(t_max) t_mid = 0.5 * (t_max + t_min) theta = 0.5 * alpha p1_r = 1.0 / cos(theta) p1_x = p1_r * cos(t_mid) p1_y = p1_r * sin(t_mid) control_points = np.array([[p0_x, p0_y, 0], [p1_x, p1_y, 0], [p2_x, p2_y, 0]]) control_points = self.radius * control_points control_points = np.apply_along_axis(lambda v: self.matrix @ v, 1, control_points) control_points = self.center + control_points w1 = cos(theta) weights = np.array([1, w1, 1]) degree = 2 knotvector = sv_knotvector.generate(degree, 3) knotvector = sv_knotvector.rescale(knotvector, t_min, t_max) nurbs = SvNurbsMaths.build_curve(implementation, degree, knotvector, control_points, weights) if alpha > 2 * pi / 3: nurbs = nurbs.insert_knot(t_mid) return nurbs
def interpolate_nurbs_curve(cls, degree, points, metric='DISTANCE', tknots=None): n = len(points) if points.ndim != 2: raise Exception( f"Array of points was expected, but got {points.shape}: {points}") ndim = points.shape[1] # 3 or 4 if ndim not in {3, 4}: raise Exception( f"Only 3D and 4D points are supported, but ndim={ndim}") #points3d = points[:,:3] #print("pts:", points) if tknots is None: tknots = Spline.create_knots( points, metric=metric) # In 3D or in 4D, in general? knotvector = sv_knotvector.from_tknots(degree, tknots) functions = SvNurbsBasisFunctions(knotvector) coeffs_by_row = [ functions.function(idx, degree)(tknots) for idx in range(n) ] A = np.zeros((ndim * n, ndim * n)) for equation_idx, t in enumerate(tknots): for unknown_idx in range(n): coeff = coeffs_by_row[unknown_idx][equation_idx] row = ndim * equation_idx col = ndim * unknown_idx for d in range(ndim): A[row + d, col + d] = coeff B = np.zeros((ndim * n, 1)) for point_idx, point in enumerate(points): row = ndim * point_idx B[row:row + ndim] = point[:, np.newaxis] x = np.linalg.solve(A, B) control_points = [] for i in range(n): row = i * ndim control = x[row:row + ndim, 0].T control_points.append(control) control_points = np.array(control_points) if ndim == 3: weights = np.ones((n, )) else: # 4 control_points, weights = from_homogenous(control_points) if type(cls) == type: return cls.build(cls.get_nurbs_implementation(), degree, knotvector, control_points, weights) elif isinstance(cls, str): return SvNurbsMaths.build_curve(cls, degree, knotvector, control_points, weights) else: raise TypeError(f"Unsupported type of `cls` parameter: {type(cls)}")
def to_nurbs(self, implementation=SvNurbsMaths.NATIVE): control_points = self.get_control_points() degree = self.get_degree() knotvector = sv_knotvector.generate(degree, len(control_points)) nurbs = SvNurbsMaths.build_curve(implementation, degree=degree, knotvector=knotvector, control_points=control_points) return nurbs.cut_segment(*self.u_bounds)
def iso_curve(self, fixed_direction, param, flip=False): controls = self.get_control_points() weights = self.get_weights() k_u, k_v = weights.shape if fixed_direction == SvNurbsSurface.U: q_curves = [ SvNurbsMaths.build_curve(self.get_nurbs_implementation(), self.get_degree_u(), self.get_knotvector_u(), controls[:, j], weights[:, j]) for j in range(k_v) ] q_controls = [q_curve.evaluate(param) for q_curve in q_curves] q_weights = [ q_curve.fraction_single(0, param)[1] for q_curve in q_curves ] curve = SvNurbsMaths.build_curve(self.get_nurbs_implementation(), self.get_degree_v(), self.get_knotvector_v(), q_controls, q_weights) if flip: return curve.reverse() else: return curve elif fixed_direction == SvNurbsSurface.V: q_curves = [ SvNurbsMaths.build_curve(self.get_nurbs_implementation(), self.get_degree_v(), self.get_knotvector_v(), controls[i, :], weights[i, :]) for i in range(k_u) ] q_controls = [q_curve.evaluate(param) for q_curve in q_curves] q_weights = [ q_curve.fraction_single(0, param)[1] for q_curve in q_curves ] curve = SvNurbsMaths.build_curve(self.get_nurbs_implementation(), self.get_degree_u(), self.get_knotvector_u(), q_controls, q_weights) if flip: return curve.reverse() else: return curve
def iso_curve(self, fixed_direction, param, flip=False): if self.surface.rational: raise UnsupportedSurfaceTypeException( "iso_curve() is not supported for rational Geomdl surfaces yet" ) controls = self.get_control_points() weights = self.get_weights() k_u, k_v = weights.shape if fixed_direction == SvNurbsSurface.U: q_curves = [ SvNurbsMaths.build_curve(self.get_nurbs_implementation(), self.get_degree_u(), self.get_knotvector_u(), controls[:, j], weights[:, j]) for j in range(k_v) ] q_controls = [q_curve.evaluate(param) for q_curve in q_curves] q_weights = np.ones((k_v, )) curve = SvNurbsMaths.build_curve(self.get_nurbs_implementation(), self.get_degree_v(), self.get_knotvector_v(), q_controls, q_weights) if flip: return curve.reverse() else: return curve elif fixed_direction == SvNurbsSurface.V: q_curves = [ SvNurbsMaths.build_curve(self.get_nurbs_implementation(), self.get_degree_v(), self.get_knotvector_v(), controls[i, :], weights[i, :]) for i in range(k_u) ] q_controls = [q_curve.evaluate(param) for q_curve in q_curves] q_weights = np.ones((k_u, )) curve = SvNurbsMaths.build_curve(self.get_nurbs_implementation(), self.get_degree_u(), self.get_knotvector_u(), q_controls, q_weights) if flip: return curve.reverse() else: return curve
def to_nurbs(self, implementation=SvNurbsMaths.NATIVE): knotvector = sv_knotvector.generate(1, 2) u_min, u_max = self.get_u_bounds() p1 = self.evaluate(u_min) p2 = self.evaluate(u_max) control_points = np.array([p1, p2]) return SvNurbsMaths.build_curve(implementation, degree=1, knotvector=knotvector, control_points=control_points)
def to_nurbs(self, implementation=SvNurbsMaths.FREECAD): curve = self.curve.toBSpline(*self.u_bounds) #curve.transform(self.edge.Matrix) control_points = np.array(curve.getPoles()) weights = np.array(curve.getWeights()) knotvector = np.array(curve.KnotSequence) curve = SvNurbsMaths.build_curve(implementation, curve.Degree, knotvector, control_points, weights) #curve.u_bounds = self.u_bounds return curve
def build(cls, implementation, degree, knotvector, control_points, weights=None, normalize_knots=False): return SvNurbsMaths.build_curve(implementation, degree, knotvector, control_points, weights, normalize_knots)
def to_nurbs(self, implementation=SvNurbsMaths.NATIVE): t_min, t_max = self.get_u_bounds() epsilon = 1e-6 if -2 * pi < t_min < 0 and 0 < t_max < 2 * pi: arc1 = self.copy() arc1.u_bounds = (2 * pi + t_min, 2 * pi) arc1 = arc1.to_nurbs() arc2 = self.copy() arc2.u_bounds = (0, t_max) arc2 = arc2.to_nurbs() return arc1.concatenate(arc2) if t_min < 0 or t_max > 2 * pi + epsilon: raise UnsupportedCurveTypeException( f"Can't transform a circle arc out of 0-2pi bound ({t_min} - {t_max}) to NURBS" ) #print(f"T {t_min},{t_max}, 2pi {2*pi}") if t_max - t_min < pi: return self._arc_to_nurbs(t_min, t_max, implementation) elif t_max - t_min < 2 * pi + epsilon: half = self._half_circle_nurbs(t_min, implementation) if abs(t_max - t_min - pi) < epsilon: return half arc = self._arc_to_nurbs(t_min + pi, t_max, implementation) return half.concatenate(arc) control_points = np.array([[1, 0, 0], [1, 1, 0], [0, 1, 0], [-1, 1, 0], [-1, 0, 0], [-1, -1, 0], [0, -1, 0], [1, -1, 0], [1, 0, 0]]) control_points = self.radius * control_points control_points = np.apply_along_axis(lambda v: self.matrix @ v, 1, control_points) control_points = self.center + control_points sqrt22 = sqrt(2.0) / 2.0 weights = np.array([1, sqrt22, 1, sqrt22, 1, sqrt22, 1, sqrt22, 1]) pi2 = pi / 2.0 pi32 = 3 * pi / 2.0 knotvector = np.array( [0, 0, 0, pi2, pi2, pi, pi, pi32, pi32, 2 * pi, 2 * pi, 2 * pi]) degree = 2 curve = SvNurbsMaths.build_curve(implementation, degree, knotvector, control_points, weights) #if t_min != 0 or t_max != 2*pi: #print(f"Cut {t_min} - {t_max}") #curve = curve_segment(curve, t_min, t_max) return curve
def to_nurbs(self, implementation=SvNurbsMaths.NATIVE): control_points = self.get_control_points() if control_points.shape[1] == 4: control_points, weights = from_homogenous(control_points) else: weights = None degree = self.get_degree() knotvector = sv_knotvector.generate(degree, len(control_points)) u1, u2 = self.get_u_bounds() knotvector = (u2 - u1) * knotvector + u1 nurbs = SvNurbsMaths.build_curve(implementation, degree=degree, knotvector=knotvector, control_points=control_points, weights=weights) #return nurbs.reparametrize(*self.get_u_bounds()) return nurbs
def curve_to_freecad_nurbs(sv_curve): """ Convert SvCurve to FreeCAD's NURBS curve. Raise an exception if it is not possible. input: SvCurve output: SvFreeCadNurbsCurve """ nurbs = SvNurbsCurve.to_nurbs(sv_curve) if nurbs is None: raise TypeError(f"{sv_curve} is not a NURBS curve") fc_curve = SvNurbsMaths.build_curve(SvNurbsMaths.FREECAD, nurbs.get_degree(), nurbs.get_knotvector(), nurbs.get_control_points(), nurbs.get_weights()) return fc_curve
def to_nurbs(self, implementation=SvNurbsMaths.FREECAD): curve = self.curve.toBSpline(*self.u_bounds) if implementation == SvNurbsMaths.FREECAD: return SvFreeCadNurbsCurve(curve, self.ndim) #curve.transform(self.edge.Matrix) if self.ndim == 2: control_points = [(p.x, p.y, 0) for p in curve.getPoles()] else: control_points = [(p.x, p.y, p.z) for p in curve.getPoles()] control_points = np.array(control_points) weights = np.array(curve.getWeights()) knotvector = np.array(curve.KnotSequence) curve = SvNurbsMaths.build_curve(implementation, curve.Degree, knotvector, control_points, weights) #curve.u_bounds = self.u_bounds return curve
def transform(self, frame, vector): """ Apply transformation matrix to the curve. Inputs: * frame: np.array of shape (3,3) - transformation matrix * vector: np.array of shape (3,) - translation vector Output: new NURBS curve of the same implementation. """ if frame is None and vector is None: return self elif frame is None and vector is not None: fn = lambda p: p + vector elif frame is not None and vector is None: fn = lambda p: frame @ p else: fn = lambda p: frame @ p + vector new_controls = np.apply_along_axis(fn, 1, self.get_control_points()) return SvNurbsMaths.build_curve(self.get_nurbs_implementation(), self.get_degree(), self.get_knotvector(), new_controls, self.get_weights())
def _half_circle_nurbs(self, t_min, implementation=SvNurbsMaths.NATIVE): control_points = np.array([[1, 0, 0], [1, 1, 0], [0, 1, 0], [-1, 1, 0], [-1, 0, 0]]) ct, st = cos(t_min), sin(t_min) rotate = np.array([[ct, -st, 0], [st, ct, 0], [0, 0, 1]]) control_points = np.apply_along_axis(lambda v: rotate @ v, 1, control_points) control_points = self.radius * control_points control_points = np.apply_along_axis(lambda v: self.matrix @ v, 1, control_points) control_points = self.center + control_points sqrt22 = sqrt(2.0) / 2.0 weights = np.array([1, sqrt22, 1, sqrt22, 1]) pi2 = pi / 2.0 knotvector = np.array([0, 0, 0, pi2, pi2, pi, pi, pi]) knotvector += t_min degree = 2 nurbs = SvNurbsMaths.build_curve(implementation, degree, knotvector, control_points, weights) return nurbs
def to_nurbs_arc(self, n=4, t_min=None, t_max=None, implementation=SvNurbsMaths.NATIVE): if t_min is None: t_min = 0.0 if t_max is None: t_max = 2 * pi if t_max < t_min: return self.to_nurbs_arc(n=n, t_max=t_min, t_min=t_max, implementation=implementation).reverse() omega = t_max - t_min alpha = pi / n n_full_arcs = round(omega // (2 * alpha)) small_arc_angle = omega % (2 * alpha) idxs_full = np.array(range(2 * n_full_arcs + 1), dtype=np.float64) ts_full = pi * idxs_full / n + t_min rs_full = np.where(idxs_full % 2 == 0, 1.0, 1.0 / cos(alpha)) xs_full = rs_full * np.cos(ts_full) ys_full = rs_full * np.sin(ts_full) zs_full = np.zeros_like(xs_full) weights_full = np.where(idxs_full % 2 == 0, 1.0, cos(alpha)) knots_full = np.array(range(n_full_arcs + 1), dtype=np.float64) knots_full = 2 * pi * knots_full / n + t_min knots_full = np.repeat(knots_full, 2) if small_arc_angle > 1e-6: t_mid_small_arc = ts_full[-1] + small_arc_angle / 2.0 r_mid_small_arc = 1.0 / cos(small_arc_angle / 2.0) x_mid_small_arc = r_mid_small_arc * cos(t_mid_small_arc) y_mid_small_arc = r_mid_small_arc * sin(t_mid_small_arc) z_mid_small_arc = 0.0 x_end = cos(t_max) y_end = sin(t_max) z_end = 0.0 xs = np.concatenate((xs_full, [x_mid_small_arc, x_end])) ys = np.concatenate((ys_full, [y_mid_small_arc, y_end])) zs = np.concatenate((zs_full, [z_mid_small_arc, z_end])) weight_mid_small_arc = cos(small_arc_angle / 2.0) weight_end = 1.0 weights = np.concatenate( (weights_full, [weight_mid_small_arc, weight_end])) knots = np.concatenate((knots_full, [t_max, t_max])) else: xs = xs_full ys = ys_full zs = zs_full weights = weights_full knots = knots_full knots = np.concatenate(([knots[0]], knots, [knots[-1]])) control_points = np.stack((xs, ys, zs)).T control_points = self.radius * control_points control_points = np.apply_along_axis(lambda v: self.matrix @ v, 1, control_points) control_points = self.center + control_points degree = 2 curve = SvNurbsMaths.build_curve(implementation, degree, knots, control_points, weights) return curve
def elevate_degree(self, direction, delta=None, target=None): if delta is None and target is None: delta = 1 if delta is not None and target is not None: raise Exception( "Of delta and target, only one parameter can be specified") if direction == SvNurbsSurface.U: degree = self.get_degree_u() else: degree = self.get_degree_v() if delta is None: delta = target - degree if delta < 0: raise Exception( f"Surface already has degree {degree}, which is greater than target {target}" ) if delta == 0: return self implementation = self.get_nurbs_implementation() if direction == SvNurbsSurface.U: new_points = [] new_weights = [] new_u_degree = None for i in range(self.get_control_points().shape[1]): fixed_v_points = self.get_control_points()[:, i] fixed_v_weights = self.get_weights()[:, i] fixed_v_curve = SvNurbsMaths.build_curve( implementation, self.get_degree_u(), self.get_knotvector_u(), fixed_v_points, fixed_v_weights) fixed_v_curve = fixed_v_curve.elevate_degree(delta) fixed_v_knotvector = fixed_v_curve.get_knotvector() new_u_degree = fixed_v_curve.get_degree() fixed_v_points = fixed_v_curve.get_control_points() fixed_v_weights = fixed_v_curve.get_weights() new_points.append(fixed_v_points) new_weights.append(fixed_v_weights) new_points = np.transpose(np.array(new_points), axes=(1, 0, 2)) new_weights = np.array(new_weights).T return SvNurbsSurface.build(self.get_nurbs_implementation(), new_u_degree, self.get_degree_v(), fixed_v_knotvector, self.get_knotvector_v(), new_points, new_weights) elif direction == SvNurbsSurface.V: new_points = [] new_weights = [] new_v_degree = None for i in range(self.get_control_points().shape[0]): fixed_u_points = self.get_control_points()[i, :] fixed_u_weights = self.get_weights()[i, :] fixed_u_curve = SvNurbsMaths.build_curve( implementation, self.get_degree_v(), self.get_knotvector_v(), fixed_u_points, fixed_u_weights) fixed_u_curve = fixed_u_curve.elevate_degree(delta) fixed_u_knotvector = fixed_u_curve.get_knotvector() new_v_degree = fixed_u_curve.get_degree() fixed_u_points = fixed_u_curve.get_control_points() fixed_u_weights = fixed_u_curve.get_weights() new_points.append(fixed_u_points) new_weights.append(fixed_u_weights) new_points = np.array(new_points) new_weights = np.array(new_weights) return SvNurbsSurface.build(implementation, self.get_degree_u(), new_v_degree, self.get_knotvector_u(), fixed_u_knotvector, new_points, new_weights)
def to_nurbs(self, implementation=SvNurbsMaths.NATIVE): knotvector = sv_knotvector.generate(self.degree, len(self.points)) return SvNurbsMaths.build_curve(implementation, degree=self.degree, knotvector=knotvector, control_points=self.points)