def elevate_degree(self, 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") degree = self.get_degree() if delta is None: delta = target - degree if delta < 0: raise Exception( f"Curve already has degree {degree}, which is greater than target {target}" ) if delta == 0: return self if self.is_bezier(): control_points = self.get_homogenous_control_points() control_points = elevate_bezier_degree(degree, control_points, delta) control_points, weights = from_homogenous(control_points) knotvector = self.get_knotvector() knotvector = sv_knotvector.elevate_degree(knotvector, delta) return SvNurbsCurve.build(self.get_nurbs_implementation(), degree + delta, knotvector, control_points, weights) else: raise UnsupportedCurveTypeException( "Degree elevation is not implemented for non-bezier curves yet" )
def make_ruled_surface(self, curve2, vmin, vmax): curve = self curve2 = SvNurbsCurve.to_nurbs(curve2) if curve2 is None: raise UnsupportedCurveTypeException("second curve is not NURBS") curve, curve2 = unify_curves_degree([curve, curve2]) if curve.get_degree() != curve2.get_degree(): raise UnsupportedCurveTypeException( f"curves have different degrees: {curve.get_degree()} != {curve2.get_degree()}" ) #print(f"kv1: {curve.get_knotvector().shape}, kv2: {curve2.get_knotvector().shape}") kv1, kv2 = curve.get_knotvector(), curve2.get_knotvector() if kv1.shape != kv2.shape or (kv1 != kv2).any(): curve, curve2 = unify_two_curves(curve, curve2) #raise UnsupportedCurveTypeException("curves have different knot vectors") my_control_points = curve.get_control_points() other_control_points = curve2.get_control_points() if len(my_control_points) != len(other_control_points): raise UnsupportedCurveTypeException( "curves have different number of control points") if vmin != 0: my_control_points = ( 1 - vmin) * my_control_points + vmin * other_control_points if vmax != 0: other_control_points = ( 1 - vmax) * my_control_points + vmax * other_control_points control_points = np.stack((my_control_points, other_control_points)) control_points = np.transpose(control_points, axes=(1, 0, 2)) weights = np.stack((curve.get_weights(), curve2.get_weights())).T knotvector_v = sv_knotvector.generate(1, 2, clamped=True) surface = SvNurbsMaths.build_surface( self.get_nurbs_implementation(), degree_u=curve.get_degree(), degree_v=1, knotvector_u=curve.get_knotvector(), knotvector_v=knotvector_v, control_points=control_points, weights=weights) return surface
def to_bezier(self): points = self.get_control_points() if not self.is_bezier(): n = len(points) p = self.get_degree() raise UnsupportedCurveTypeException( f"Curve with {n} control points and {p}'th degree can not be converted into Bezier curve" ) return SvBezierCurve(points)
def get_actual_radius(self, tolerance=1e-10): x = np.array([self.radius, 0, 0]) y = np.array([0, self.radius, 0]) m = self.matrix vx = m @ x vy = m @ y rx = np.linalg.norm(vx) ry = np.linalg.norm(vy) if abs(rx - ry) > tolerance: raise UnsupportedCurveTypeException( f"This SvCircle instance is not an exact circle: {rx} != {ry}") return (rx + ry) / 2.0
def to_bezier_segments(self): if self.is_rational(): raise UnsupportedCurveTypeException( "Rational NURBS curve can not be converted into non-rational Bezier curves" ) if self.is_bezier(): return [self.to_bezier()] segments = [] rest = self for u in sv_knotvector.get_internal_knots(self.get_knotvector()): segment, rest = rest.split_at(u) segments.append(segment.to_bezier()) segments.append(rest.to_bezier()) return segments
def concatenate(self, curve2, tolerance=1e-6): curve1 = self curve2 = SvNurbsCurve.to_nurbs(curve2) if curve2 is None: raise UnsupportedCurveTypeException("second curve is not NURBS") c1_end = curve1.get_u_bounds()[1] c2_start = curve2.get_u_bounds()[0] pt1 = curve1.evaluate(c1_end) pt2 = curve2.evaluate(c2_start) dpt = np.linalg.norm(pt1 - pt2) if dpt > tolerance: raise UnsupportedCurveTypeException( f"Curve end points do not match: C1({c1_end}) = {pt1} != C2({c2_start}) = {pt2}, distance={dpt}" ) cp1 = curve1.get_control_points()[-1] cp2 = curve2.get_control_points()[0] if np.linalg.norm(cp1 - cp2) > tolerance: raise UnsupportedCurveTypeException( "End control points do not match") w1 = curve1.get_weights()[-1] w2 = curve1.get_weights()[0] if w1 != w2: raise UnsupportedCurveTypeException( "Weights at endpoints do not match") p1, p2 = curve1.get_degree(), curve2.get_degree() if p1 > p2: curve2 = curve2.elevate_degree(delta=p1 - p2) elif p2 > p1: curve1 = curve1.elevate_degree(delta=p2 - p1) p = curve1.get_degree() kv1 = curve1.get_knotvector() kv2 = curve2.get_knotvector() kv1_end_multiplicity = sv_knotvector.to_multiplicity(kv1)[-1][1] kv2_start_multiplicity = sv_knotvector.to_multiplicity(kv2)[0][1] if kv1_end_multiplicity != p + 1: raise UnsupportedCurveTypeException( f"End knot multiplicity of the first curve ({kv1_end_multiplicity}) is not equal to degree+1 ({p+1})" ) if kv2_start_multiplicity != p + 1: raise UnsupportedCurveTypeException( f"Start knot multiplicity of the second curve ({kv2_start_multiplicity}) is not equal to degree+1 ({p+1})" ) knotvector = sv_knotvector.concatenate(kv1, kv2, join_multiplicity=p) #print(f"Concat KV: {kv1} + {kv2} => {knotvector}") weights = np.concatenate( (curve1.get_weights(), curve2.get_weights()[1:])) control_points = np.concatenate( (curve1.get_control_points(), curve2.get_control_points()[1:])) return SvNurbsCurve.build(self.get_nurbs_implementation(), p, knotvector, control_points, weights)
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 lerp_to(self, curve2, coefficient): curve1 = self curve2 = SvNurbsCurve.to_nurbs(curve2) if curve2 is None: raise UnsupportedCurveTypeException("second curve is not NURBS") curve1, curve2 = unify_curves_degree([curve1, curve2]) curve1, curve2 = unify_two_curves(curve1, curve2) #c1cp = curve1.get_homogenous_control_points() #c2cp = curve2.get_homogenous_control_points() c1cp = curve1.get_control_points() c2cp = curve2.get_control_points() ws1 = curve1.get_weights() ws2 = curve2.get_weights() points = c1cp * (1 - coefficient) + coefficient * c2cp weights = ws1 * (1 - coefficient) + coefficient * ws2 return SvNurbsCurve.build(curve1.get_nurbs_implementation(), curve1.get_degree(), curve1.get_knotvector(), points, weights)
def to_nurbs(self, implementation=SvNurbsCurve.NATIVE): t_min, t_max = self.get_u_bounds() if t_min < 0 or t_max > 2 * pi: raise UnsupportedCurveTypeException( f"Can't transform a circle arc out of 0-2pi bound ({t_min} - {t_max}) to NURBS" ) 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) 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 = SvNurbsCurve.build(implementation, degree, knotvector, control_points, weights) if t_min != 0 or t_max != 2 * pi: curve = curve_segment(curve, t_min, t_max) return curve
def concatenate(self, curve2): curve2 = SvNurbsMaths.to_nurbs_curve(curve2) if curve2 is None: raise UnsupportedCurveTypeException("Second curve is not a NURBS") return self.to_nurbs().concatenate(curve2)
def concatenate(self, curve2, tolerance=1e-6, remove_knots=False): curve1 = self curve2 = SvNurbsCurve.to_nurbs(curve2) if curve2 is None: raise UnsupportedCurveTypeException("second curve is not NURBS") c1_end = curve1.get_u_bounds()[1] c2_start = curve2.get_u_bounds()[0] if sv_knotvector.is_clamped(curve1.get_knotvector(), curve1.get_degree(), check_start=True, check_end=False): pt1 = curve1.get_control_points()[-1] else: pt1 = curve1.evaluate(c1_end) if sv_knotvector.is_clamped(curve2.get_knotvector(), curve2.get_degree(), check_start=False, check_end=True): pt2 = curve2.get_control_points()[0] else: pt2 = curve2.evaluate(c2_start) dpt = np.linalg.norm(pt1 - pt2) if dpt > tolerance: raise UnsupportedCurveTypeException( f"Curve end points do not match: C1({c1_end}) = {pt1} != C2({c2_start}) = {pt2}, distance={dpt}" ) cp1 = curve1.get_control_points()[-1] cp2 = curve2.get_control_points()[0] if np.linalg.norm(cp1 - cp2) > tolerance: raise UnsupportedCurveTypeException( "End control points do not match") w1 = curve1.get_weights()[-1] w2 = curve2.get_weights()[0] if abs(w1 - w2) > tolerance: raise UnsupportedCurveTypeException( f"Weights at endpoints do not match: {w1} != {w2}") p1, p2 = curve1.get_degree(), curve2.get_degree() if p1 > p2: curve2 = curve2.elevate_degree(delta=p1 - p2) elif p2 > p1: curve1 = curve1.elevate_degree(delta=p2 - p1) p = curve1.get_degree() kv1 = curve1.get_knotvector() kv2 = curve2.get_knotvector() kv1_end_multiplicity = sv_knotvector.to_multiplicity(kv1)[-1][1] kv2_start_multiplicity = sv_knotvector.to_multiplicity(kv2)[0][1] if kv1_end_multiplicity != p + 1: raise UnsupportedCurveTypeException( f"End knot multiplicity of the first curve ({kv1_end_multiplicity}) is not equal to degree+1 ({p+1})" ) if kv2_start_multiplicity != p + 1: raise UnsupportedCurveTypeException( f"Start knot multiplicity of the second curve ({kv2_start_multiplicity}) is not equal to degree+1 ({p+1})" ) knotvector = sv_knotvector.concatenate(kv1, kv2, join_multiplicity=p) #print(f"Concat KV: {kv1} + {kv2} => {knotvector}") weights = np.concatenate( (curve1.get_weights(), curve2.get_weights()[1:])) control_points = np.concatenate( (curve1.get_control_points(), curve2.get_control_points()[1:])) result = SvNurbsCurve.build(self.get_nurbs_implementation(), p, knotvector, control_points, weights) if remove_knots is not None: if remove_knots == True: remove_knots = p - 1 join_point = kv1[-1] result = result.remove_knot(join_point, count=remove_knots, if_possible=True) return result
def coons_surface(curve1, curve2, curve3, curve4, use_nurbs=NURBS_IF_POSSIBLE): curves = [curve1, curve2, curve3, curve4] nurbs_curves = [SvNurbsCurve.to_nurbs(c) for c in curves] if use_nurbs == GENERIC: return SvCoonsSurface(*curves) if any(c is None for c in nurbs_curves): if use_nurbs == NURBS_ONLY: raise UnsupportedCurveTypeException("Some of curves are not NURBS") else: return SvCoonsSurface(*curves) try: nurbs_curves = [c.reparametrize(0, 1) for c in nurbs_curves] degrees = [c.get_degree() for c in nurbs_curves] implementation = nurbs_curves[0].get_nurbs_implementation() nurbs_curves[0], nurbs_curves[2] = unify_curves( [nurbs_curves[0], nurbs_curves[2]]) nurbs_curves[1], nurbs_curves[3] = unify_curves( [nurbs_curves[1], nurbs_curves[3]]) degree_u = nurbs_curves[0].get_degree() degree_v = nurbs_curves[1].get_degree() nurbs_curves[0] = reverse_curve(nurbs_curves[0]) nurbs_curves[3] = reverse_curve(nurbs_curves[3]) ruled1 = nurbs_curves[0].make_ruled_surface(nurbs_curves[2], 0, 1) ruled2 = nurbs_curves[1].make_ruled_surface(nurbs_curves[3], 0, 1).swap_uv() linear_kv = sv_knotvector.generate(1, 2) c1_t_min, c1_t_max = nurbs_curves[0].get_u_bounds() c3_t_min, c3_t_max = nurbs_curves[2].get_u_bounds() pt1 = nurbs_curves[0].evaluate(c1_t_min) pt2 = nurbs_curves[0].evaluate(c1_t_max) pt3 = nurbs_curves[2].evaluate(c3_t_min) pt4 = nurbs_curves[2].evaluate(c3_t_max) w1 = nurbs_curves[0].get_weights()[0] w2 = nurbs_curves[0].get_weights()[-1] w3 = nurbs_curves[2].get_weights()[0] w4 = nurbs_curves[2].get_weights()[-1] linear_pts = np.array([[pt1, pt3], [pt2, pt4]]) linear_weights = np.array([[w1, w3], [w2, w4]]) #linear_weights = np.array([[1,1], [1,1]]) bilinear = SvNurbsSurface.build(implementation, 1, 1, linear_kv, linear_kv, linear_pts, linear_weights) ruled1, ruled2, bilinear = unify_nurbs_surfaces( [ruled1, ruled2, bilinear]) knotvector_u = ruled1.get_knotvector_u() knotvector_v = ruled1.get_knotvector_v() control_points = ruled1.get_control_points( ) + ruled2.get_control_points() - bilinear.get_control_points() weights = ruled1.get_weights() + ruled2.get_weights( ) - bilinear.get_weights() result = SvNurbsSurface.build(implementation, degree_u, degree_v, knotvector_u, knotvector_v, control_points, weights) return result except UnsupportedCurveTypeException as e: if use_nurbs == NURBS_ONLY: raise else: info("Can't create a native Coons surface from curves %s: %s", curves, e) return SvCoonsSurface(*curves)