Module sverchok.utils.curve.primitives
Module containing primitive curve types: straight lines, circles, ellipses.
Expand source code
# This file is part of project Sverchok. It's copyrighted by the contributors
# recorded in the version control history of the file, available from
# its original location https://github.com/nortikin/sverchok/commit/master
#
# SPDX-License-Identifier: GPL3
# License-Filename: LICENSE
"""
Module containing primitive curve types: straight lines, circles, ellipses.
"""
import numpy as np
from math import sin, cos, pi, radians, sqrt
from mathutils import Vector, Matrix
from sverchok.utils.geom import LineEquation, CircleEquation2D, CircleEquation3D, Ellipse3D, rotate_vector_around_vector_np, rotate_vector_around_vector
from sverchok.utils.nurbs_common import SvNurbsMaths
from sverchok.utils.curve.core import SvCurve, UnsupportedCurveTypeException
from sverchok.utils.surface.primitives import SvPlane
from sverchok.utils.curve import knotvector as sv_knotvector
from sverchok.utils.curve.bezier import SvBezierCurve
from sverchok.utils.curve.algorithms import curve_segment
class SvLine(SvCurve):
"""
Straight line segment curve.
"""
def __init__(self, point, direction, u_bounds=None):
"""
Args:
point: a point on a line.
direction: directing vector of a line.
"""
self.point = np.array(point)
self.direction = np.array(direction)
if u_bounds is None:
u_bounds = (0.0, 1.0)
self.u_bounds = u_bounds
def __repr__(self):
return f"<{self.point} - {self.point+self.direction}>"
@classmethod
def from_two_points(cls, point1, point2):
"""
Generate straight line segment from two points.
"""
direction = np.array(point2) - np.array(point1)
return SvLine(point1, direction)
def copy(self, u_bounds=None):
if u_bounds is None:
u_bounds = self.u_bounds
return SvLine(self.point, self.direction, u_bounds=u_bounds)
def get_degree(self):
return 1
def get_u_bounds(self):
return self.u_bounds
def evaluate(self, t):
return self.point + t * self.direction
def evaluate_array(self, ts):
ts = ts[np.newaxis].T
return self.point + ts * self.direction
def tangent(self, t, tangent_delta=None):
tg = self.direction
n = np.linalg.norm(tg)
return tg / n
def tangent_array(self, ts, tangent_delta=None):
tg = self.direction
n = np.linalg.norm(tg)
tangent = tg / n
result = np.tile(tangent[np.newaxis].T, len(ts)).T
return result
def extrude_along_vector(self, vector):
return SvPlane(self.point, self.direction, vector)
def make_revolution_surface(self, point, direction, v_min, v_max, global_origin):
return self.to_nurbs().make_revolution_surface(point, direction, v_min, v_max, global_origin)
def make_ruled_surface(self, curve2, vmin, vmax):
return self.to_nurbs().make_ruled_surface(curve2, vmin, vmax)
def extrude_to_point(self, point):
return self.to_nurbs().extrude_to_point(point)
def lerp_to(self, curve2, coefficient):
return self.to_nurbs().lerp_to(curve2, coefficient)
def split_at(self, t):
t_min, t_max = self.get_u_bounds()
curve1 = self.copy(u_bounds=(t_min, t))
curve2 = self.copy(u_bounds=(t, t_max))
return curve1, curve2
def reverse(self):
t_min, t_max = self.get_u_bounds()
p1, p2 = self.evaluate(t_min), self.evaluate(t_max)
return SvLine.from_two_points(p2, p1)
def to_nurbs(self, implementation=SvNurbsMaths.NATIVE):
u_min, u_max = self.get_u_bounds()
knotvector = sv_knotvector.generate(1, 2)
knotvector = sv_knotvector.rescale(knotvector, u_min, u_max)
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_bezier(self):
u_min, u_max = self.get_u_bounds()
p1 = self.evaluate(u_min)
p2 = self.evaluate(u_max)
return SvBezierCurve([p1, p2])
def to_bezier_segments(self):
return [self.to_bezier()]
def concatenate(self, curve2, tolerance=1e-6, remove_knots=False):
return self.to_nurbs().concatenate(curve2, tolerance=tolerance, remove_knots=remove_knots)
def reparametrize(self, new_t_min, new_t_max):
t_min, t_max = self.get_u_bounds()
scale = (t_max - t_min) / (new_t_max - new_t_min)
new_direction = self.direction * scale
new_point = self.point + self.direction * (t_min - scale * new_t_min)
return SvLine(new_point, new_direction, u_bounds = (new_t_min, new_t_max))
def is_polyline(self):
return True
def get_polyline_vertices(self):
return np.array(self.get_end_points())
def is_closed(self, *args):
return False
def extrude_along_vector(self, vector):
return self.to_nurbs().extrude_along_vector(vector)
def make_revolution_surface(self, point, direction, v_min, v_max, global_origin):
return self.to_nurbs().make_revolution_surface(point, direction, v_min, v_max, global_origin)
def make_ruled_surface(self, curve2, vmin, vmax):
return self.to_nurbs().make_ruled_surface(curve2, vmin, vmax)
def extrude_to_point(self, point):
return self.to_nurbs().extrude_to_point(point)
def lerp_to(self, curve2, coefficient):
return self.to_nurbs().lerp_to(curve2, coefficient)
class SvPointCurve(SvCurve):
__description__ = "Single-Point"
def __init__(self, point):
self.point = np.asarray(point)
def evaluate(self, t):
return self.point
def evaluate_array(self, ts):
points = np.empty((len(ts),3))
points[:] = self.point
return points
def get_u_bounds(self):
return (0.0, 1.0)
def get_degree(self):
return 1
def to_bezier(self):
u_min, u_max = self.get_u_bounds()
p1 = self.evaluate(u_min)
p2 = self.evaluate(u_max)
return SvBezierCurve([p1, p2])
def to_bezier_segments(self):
return [self.to_bezier()]
def is_closed(self, *args):
return False
def concatenate(self, curve2, *args):
return curve2
def to_nurbs(self, implementation = SvNurbsMaths.NATIVE):
return self.to_bezier().to_nurbs()
def reverse(self):
return SvPointCurve(self.point)
def rotate_radius(radius, normal, thetas):
"""Internal method"""
ct = np.cos(thetas)[np.newaxis].T
st = np.sin(thetas)[np.newaxis].T
normal /= np.linalg.norm(normal)
binormal = np.cross(normal, radius)
vx = radius * ct
vy = binormal * st
return vx + vy
class SvCircle(SvCurve):
"""
Circle (or circular arc) curve.
"""
def __init__(self, matrix=None, radius=None, center=None, normal=None, vectorx=None):
if matrix is not None:
self.matrix = np.array(matrix.to_3x3())
self.center = np.array(matrix.translation)
elif center is not None:
self.center = center
if matrix is None:
self.matrix = SvCircle.calc_matrix(normal, vectorx)
if radius is not None:
self.radius = radius
else:
self.radius = np.linalg.norm(vectorx)
if normal is not None:
self.normal = normal
elif matrix is not None:
z = Vector([0,0,1])
m = matrix.copy()
m.translation = Vector()
self.normal = np.array(m @ z)
if vectorx is not None:
self.vectorx = vectorx
elif matrix is not None:
x = Vector([radius,0,0])
m = matrix.copy()
m.translation = Vector()
self.vectorx = np.array(m @ x)
self.u_bounds = (0.0, 2*pi)
def copy(self):
circle = SvCircle(radius=self.radius,
center=self.center,
normal=self.normal,
vectorx=self.vectorx)
circle.u_bounds = self.u_bounds
return circle
def get_mu_matrix(self):
m = Matrix(self.matrix).to_4x4()
m.translation = Vector(self.center)
return m
@staticmethod
def calc_matrix(normal, vectorx):
normal = normal / np.linalg.norm(normal)
vx = vectorx / np.linalg.norm(vectorx)
vy = np.cross(normal, vx)
vy = vy / np.linalg.norm(vy)
m = np.stack((vx, vy, normal))
return np.linalg.inv(m)
def set_normal(self, normal):
self.normal = normal
self.matrix = SvCircle.calc_matrix(normal, self.vectorx)
def __repr__(self):
try:
r = str(self.get_actual_radius())
except UnsupportedCurveTypeException:
r = f"Matrix @ {self.radius}"
return f"<Circle C={self.center}, N={self.normal}, R={r}>"
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
@classmethod
def from_equation(cls, eq):
"""
Make an instance of SvCircle from an instance of CircleEquation2D/3D.
"""
# isinstance() wont work properly with "reload scripts".
if type(eq).__name__ == 'CircleEquation2D':
matrix = Matrix.Translation(eq.center)
circle = SvCircle(matrix, eq.radius)
return circle
elif type(eq).__name__ == 'CircleEquation3D':
if eq.point1 is not None:
circle = SvCircle(center = np.array(eq.center),
vectorx = np.array(eq.point1) - np.array(eq.center),
normal = eq.normal)
else:
circle = SvCircle(eq.get_matrix(), eq.radius)
if eq.arc_angle:
circle.u_bounds = (0, eq.arc_angle)
return circle
else:
raise TypeError("Unsupported argument type:" + str(eq))
@classmethod
def from_arc(cls, arc, z_axis='Z'):
"""
Make an instance of SvCircle from an instance of sverchok.utils.sv_curve_utils.Arc
"""
if arc.radius.real == arc.radius.imag:
radius = arc.radius.real
radius_dx = radius_dy = 1.0
scale_x = scale_y = Matrix.Identity(4)
else:
radius = abs(arc.radius)
radius_dx = arc.radius.real / radius
radius_dy = arc.radius.imag / radius
scale_x = Matrix.Scale(radius_dx, 4, (1,0,0))
scale_y = Matrix.Scale(radius_dy, 4, (0,1,0))
matrix = Matrix.Translation(Vector((arc.center.real, arc.center.imag, 0)))
rotation = radians(arc.theta)
angle = radians(abs(arc.delta))
rot_z = Matrix.Rotation(rotation, 4, 'Z')
matrix = matrix @ scale_x @ scale_y @ rot_z
if arc.delta < 0:
matrix = matrix @ Matrix.Rotation(radians(180), 4, 'X')
if z_axis == 'Y':
matrix = Matrix.Rotation(radians(90), 4, 'X') @ matrix
elif z_axis == 'X':
matrix = Matrix.Rotation(radians(90), 4, 'Z') @ Matrix.Rotation(radians(90), 4, 'X') @ matrix
circle = SvCircle(matrix=matrix, radius=radius)
circle.u_bounds = (0, angle)
return circle
def get_degree(self):
return 2
def get_u_bounds(self):
return self.u_bounds
def evaluate(self, t):
vx = self.vectorx
return self.center + rotate_vector_around_vector(vx, self.normal, t)
def evaluate_array(self, ts):
#n = len(ts)
#vx = np.broadcast_to(self.vectorx[np.newaxis], (n,3))
#return self.center + rotate_vector_around_vector_np(vx, self.normal, ts)
return self.center + rotate_radius(self.vectorx, self.normal, ts)
def tangent(self, t, tangent_delta=None):
x = - self.radius * sin(t)
y = self.radius * cos(t)
z = 0
return self.matrix @ np.array([x, y, z])
def tangent_array(self, ts, tangent_delta=None):
xs = - self.radius * np.sin(ts)
ys = self.radius * np.cos(ts)
zs = np.zeros_like(xs)
vectors = np.stack((xs, ys, zs)).T
result = np.apply_along_axis(lambda v: self.matrix @ v, 1, vectors)
return result
# def second_derivative_array(self, ts):
# xs = - np.cos(ts)
# ys = - np.sin(ts)
# zs = np.zeros_like(xs)
# vectors = np.stack((xs, ys, zs)).T
# return np.apply_along_axis(lambda v: self.matrix @ v, 1, vectors)
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 _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(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_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 to_nurbs_quadric(self, n=3, t_min=None, t_max=None, parametrization = 'C2', implementation = SvNurbsMaths.NATIVE):
"""
Convert the circle to NURBS curve with 4-degree parametrization.
This implements the algorithm described in the paper:
Carole Blanc, Christophe Schlick.
More Accurate Representation of Conics by NURBS.
Technical Report, LaBRI, 1995.
Args:
n: number of subdivisions, usually 3, 4 or 6.
t_min, t_max: indicate the arc to be converted.
parametrization: 'C2' for parametrization with continuous 2nd
derivative; 'QIDEAL' for quasi-ideal parametrization, i.e. for
parametrization which is almost identical to trigonometric
parametrization.
implementation: implementation of Nurbs mathematics.
Returns:
an instance of SvNurbsCurve.
"""
if parametrization not in {'C2', 'QIDEAL'}:
raise Exception("Unsupported parametrization type")
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_quadric(n=n, t_max=t_min, t_min=t_max, implementation=implementation).reverse()
def make_quad_arc(t1, t2):
p1 = [cos(t1), sin(t1), 0.0]
alpha = (t2 - t1)/2.0
r_mid = 1.0 / cos(alpha)
t_mid = (t1 + t2)/2.0
p_mid = [r_mid*cos(t_mid), r_mid*sin(t_mid), 0.0]
p2 = [cos(t2), sin(t2), 0.0]
points = np.array([p1, p_mid, p2])
w = cos(alpha)
return points, w
def make_quadric_arc(t1, t2):
ps, w = make_quad_arc(t1, t2)
if parametrization == 'C2':
p = (1.0 + sqrt(5.0 + 4.0*w)) / (2*(1.0 + w))
else:
phi = (t2 - t1) / 2.0
cosphi = cos(phi / 5.0)
p = (4 - 2*cosphi**3 + cosphi) / (-1 + 8*cosphi**3 - 4*cosphi)
q0 = ps[0]
q1 = (ps[0] + w*ps[1])/(1.0 + w)
q2 = (p*p*ps[0] + 2*w*(1+p*p)*ps[1] + p*p*ps[2]) / (2*(p*p + p*p*w + w))
q3 = (ps[2] + w*ps[1]) / (1.0 + w)
q4 = ps[2]
w0 = 1.0
w1 = p*(1.0 + w)/2.0
w2 = (p*p + p*p*w + w)/3.0
w3 = w1
w4 = w0
degree = 4
knots = (t2 - t1) * sv_knotvector.generate(degree, 5)
control_points = np.array([q0, q1, q2, q3, q4])
weights = np.array([w0, w1, w2, w3, w4])
return SvNurbsMaths.build_curve(implementation,
degree, knots,
control_points, weights)
omega = t_max - t_min
alpha = pi / n
full_arc_angle = 2*alpha
n_full_arcs = round(omega // full_arc_angle)
small_arc_angle = omega % full_arc_angle
ts_full = [t_min + full_arc_angle*i for i in range(n_full_arcs+1)]
full_arcs = [make_quadric_arc(t1, t2) for t1,t2 in zip(ts_full, ts_full[1:])]
if small_arc_angle > 1e-6:
small_arc = make_quadric_arc(ts_full[-1], t_max)
small_arcs = [small_arc]
else:
small_arcs = []
all_arcs = full_arcs + small_arcs
unit_arc = all_arcs[0]
for arc in all_arcs[1:]:
unit_arc = unit_arc.concatenate(arc)
control_points = unit_arc.get_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
curve = unit_arc.copy(control_points = control_points)
return curve
def to_nurbs_full(self, n=4, parametrization = 'SIMPLE', implementation = SvNurbsMaths.NATIVE):
"""
Convert fulll circle to a NURBS curve.
Args:
n: number of subdivisions, usually 3, 4 or 6.
paramerization: 'SIMPLE' for traditional (2-degree) circle
parametrization; 'C2' for 4-degree parametrization with continuous
2nd derivative; 'QIDEAL' for quasi-ideal parametrization, i.e. for
4-degree parametrization which is almost identical to trigonometric
parametrization.
implementation: implementation of Nurbs mathematics.
Returns:
an instance of SvNurbsCurve.
"""
if parametrization == 'SIMPLE':
return self.to_nurbs_arc(n=n, implementation=implementation)
elif parametrization in {'C2', 'QIDEAL'}:
return self.to_nurbs_quadric(n=n, parametrization=parametrization, implementation=implementation)
else:
raise Exception("Unsupported parametrization type")
def reverse(self):
circle = self.copy()
u1, u2 = self.u_bounds
circle.u_bounds = (2*pi - u2, 2*pi - u1)
circle.normal = - circle.normal
return circle
def make_revolution_surface(self, point, direction, v_min, v_max, global_origin):
return self.to_nurbs().make_revolution_surface(point, direction, v_min, v_max, global_origin)
def extrude_along_vector(self, vector):
return self.to_nurbs().extrude_along_vector(vector)
def make_ruled_surface(self, curve2, vmin, vmax):
return self.to_nurbs().make_ruled_surface(curve2, vmin, vmax)
def extrude_to_point(self, point):
return self.to_nurbs().extrude_to_point(point)
def lerp_to(self, curve2, coefficient):
return self.to_nurbs().lerp_to(curve2, coefficient)
def elevate_degree(self, delta=None, target=None):
return self.to_nurbs().elevate_degree(delta=delta, target=target)
# def concatenate(self, curve2, tolerance=1e-6, remove_knots=False):
# t_min, t_max = self.get_u_bounds()
# return self.to_nurbs_arc(t_min=t_min, t_max=t_max).concatenate(curve2, tolerance=tolerance, remove_knots=remove_knots)
class SvEllipse(SvCurve):
"""
Ellipse curve.
"""
__description__ = "Ellipse"
CENTER = 'center'
F1 = 'f1'
F2 = 'f2'
def __init__(self, matrix, a, b, center_type=CENTER):
self.matrix = np.array(matrix.to_3x3())
self.center = np.array(matrix.translation)
self.center_type = center_type
self.a = a
self.b = b
self.u_bounds = (0, 2*pi)
self.tangent_delta = 0.001
def get_u_bounds(self):
return self.u_bounds
@classmethod
def from_equation(cls, eq):
"""
Build an instance of SvEllipse from `sverchok.utils.geom.Ellipse3D`.
"""
return SvEllipse(eq.get_matrix(), eq.a, eq.b)
def to_equation(self):
"""
Convert an instance of SvEllipse to `sverchok.utils.geom.Ellipse3D`.
"""
major_radius = self.matrix @ np.array([self.a, 0, 0])
minor_radius = self.matrix @ np.array([0, self.b, 0])
eq = Ellipse3D(Vector(self.center), Vector(major_radius), Vector(minor_radius))
return eq
@property
def c(self):
a, b = self.a, self.b
return sqrt(a*a - b*b)
def focal_points(self):
"""
Calculate ellipse focal points.
Returns:
list of two points.
"""
df = self.matrix @ np.array([self.c, 0, 0])
f1 = self.center + df
f2 = self.center - df
return [f1, f2]
def get_center(self):
"""
Calculate ellipse center.
"""
if self.center_type == SvEllipse.CENTER:
return self.center
elif self.center_type == SvEllipse.F1:
df = self.matrix @ np.array([self.c, 0, 0])
return self.center + df
else: # F2
df = self.matrix @ np.array([self.c, 0, 0])
return self.center - df
def get_degree(self):
return 2
def evaluate(self, t):
v = np.array([self.a * cos(t), self.b * sin(t), 0])
center = self.get_center()
v = center + self.matrix @ v
return v
def evaluate_array(self, ts):
xs = self.a * np.cos(ts)
ys = self.b * np.sin(ts)
zs = np.zeros_like(xs)
vs = np.array((xs, ys, zs)).T
vs = np.apply_along_axis(lambda v : self.matrix @ v, 1, vs)
center = self.get_center()
return center + vs
def tangent(self, t, tangent_delta=None):
return self.tangent_array(np.array([t]))[0]
def tangent_array(self, ts, tangent_delta=None):
xs = - self.a * np.sin(ts)
ys = self.b * np.cos(ts)
zs = np.zeros_like(xs)
vs = np.array((xs, ys, zs)).T
vs = np.apply_along_axis(lambda v : self.matrix @ v, 1, vs)
return vs
def second_derivative(self, t, tangent_delta=None):
return self.second_derivative_array(np.array([t]))[0]
def second_derivative_array(self, ts, tangent_delta=None):
xs = - self.a * np.cos(ts)
ys = - self.b * np.sin(ts)
zs = np.zeros_like(xs)
vs = np.array((xs, ys, zs)).T
vs = np.apply_along_axis(lambda v : self.matrix @ v, 1, vs)
return vs
def to_nurbs(self, implementation=SvNurbsMaths.NATIVE):
"""
Convert the ellipse to SvNurbsCurve.
"""
if self.a == 0 and self.b == 0:
coef_x = 0
coef_y = 0
radius = 0
elif self.a == 0:
coef_x = 0
coef_y = 1
radius = self.b
elif self.b == 0:
coef_x = 1
coef_y = 0
radius = self.a
else:
coef_x = 1
coef_y = self.b/self.a
radius = self.a
scale = Matrix([[coef_x,0,0], [0, coef_y, 0], [0, 0, 1]]).to_4x4()
matrix = Matrix(self.matrix).to_4x4()
matrix.translation = Vector(self.get_center())
circle = SvCircle(matrix = matrix @ scale, radius = radius,
center = self.get_center())
return circle.to_nurbs(implementation)
def concatenate(self, curve2, tolerance=1e-6, remove_knots=False):
return self.to_nurbs().concatenate(curve2, tolerance=tolerance, remove_knots=remove_knots)
Functions
def rotate_radius(radius, normal, thetas)
-
Internal method
Expand source code
def rotate_radius(radius, normal, thetas): """Internal method""" ct = np.cos(thetas)[np.newaxis].T st = np.sin(thetas)[np.newaxis].T normal /= np.linalg.norm(normal) binormal = np.cross(normal, radius) vx = radius * ct vy = binormal * st return vx + vy
Classes
class SvCircle (matrix=None, radius=None, center=None, normal=None, vectorx=None)
-
Circle (or circular arc) curve.
Expand source code
class SvCircle(SvCurve): """ Circle (or circular arc) curve. """ def __init__(self, matrix=None, radius=None, center=None, normal=None, vectorx=None): if matrix is not None: self.matrix = np.array(matrix.to_3x3()) self.center = np.array(matrix.translation) elif center is not None: self.center = center if matrix is None: self.matrix = SvCircle.calc_matrix(normal, vectorx) if radius is not None: self.radius = radius else: self.radius = np.linalg.norm(vectorx) if normal is not None: self.normal = normal elif matrix is not None: z = Vector([0,0,1]) m = matrix.copy() m.translation = Vector() self.normal = np.array(m @ z) if vectorx is not None: self.vectorx = vectorx elif matrix is not None: x = Vector([radius,0,0]) m = matrix.copy() m.translation = Vector() self.vectorx = np.array(m @ x) self.u_bounds = (0.0, 2*pi) def copy(self): circle = SvCircle(radius=self.radius, center=self.center, normal=self.normal, vectorx=self.vectorx) circle.u_bounds = self.u_bounds return circle def get_mu_matrix(self): m = Matrix(self.matrix).to_4x4() m.translation = Vector(self.center) return m @staticmethod def calc_matrix(normal, vectorx): normal = normal / np.linalg.norm(normal) vx = vectorx / np.linalg.norm(vectorx) vy = np.cross(normal, vx) vy = vy / np.linalg.norm(vy) m = np.stack((vx, vy, normal)) return np.linalg.inv(m) def set_normal(self, normal): self.normal = normal self.matrix = SvCircle.calc_matrix(normal, self.vectorx) def __repr__(self): try: r = str(self.get_actual_radius()) except UnsupportedCurveTypeException: r = f"Matrix @ {self.radius}" return f"<Circle C={self.center}, N={self.normal}, R={r}>" 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 @classmethod def from_equation(cls, eq): """ Make an instance of SvCircle from an instance of CircleEquation2D/3D. """ # isinstance() wont work properly with "reload scripts". if type(eq).__name__ == 'CircleEquation2D': matrix = Matrix.Translation(eq.center) circle = SvCircle(matrix, eq.radius) return circle elif type(eq).__name__ == 'CircleEquation3D': if eq.point1 is not None: circle = SvCircle(center = np.array(eq.center), vectorx = np.array(eq.point1) - np.array(eq.center), normal = eq.normal) else: circle = SvCircle(eq.get_matrix(), eq.radius) if eq.arc_angle: circle.u_bounds = (0, eq.arc_angle) return circle else: raise TypeError("Unsupported argument type:" + str(eq)) @classmethod def from_arc(cls, arc, z_axis='Z'): """ Make an instance of SvCircle from an instance of sverchok.utils.sv_curve_utils.Arc """ if arc.radius.real == arc.radius.imag: radius = arc.radius.real radius_dx = radius_dy = 1.0 scale_x = scale_y = Matrix.Identity(4) else: radius = abs(arc.radius) radius_dx = arc.radius.real / radius radius_dy = arc.radius.imag / radius scale_x = Matrix.Scale(radius_dx, 4, (1,0,0)) scale_y = Matrix.Scale(radius_dy, 4, (0,1,0)) matrix = Matrix.Translation(Vector((arc.center.real, arc.center.imag, 0))) rotation = radians(arc.theta) angle = radians(abs(arc.delta)) rot_z = Matrix.Rotation(rotation, 4, 'Z') matrix = matrix @ scale_x @ scale_y @ rot_z if arc.delta < 0: matrix = matrix @ Matrix.Rotation(radians(180), 4, 'X') if z_axis == 'Y': matrix = Matrix.Rotation(radians(90), 4, 'X') @ matrix elif z_axis == 'X': matrix = Matrix.Rotation(radians(90), 4, 'Z') @ Matrix.Rotation(radians(90), 4, 'X') @ matrix circle = SvCircle(matrix=matrix, radius=radius) circle.u_bounds = (0, angle) return circle def get_degree(self): return 2 def get_u_bounds(self): return self.u_bounds def evaluate(self, t): vx = self.vectorx return self.center + rotate_vector_around_vector(vx, self.normal, t) def evaluate_array(self, ts): #n = len(ts) #vx = np.broadcast_to(self.vectorx[np.newaxis], (n,3)) #return self.center + rotate_vector_around_vector_np(vx, self.normal, ts) return self.center + rotate_radius(self.vectorx, self.normal, ts) def tangent(self, t, tangent_delta=None): x = - self.radius * sin(t) y = self.radius * cos(t) z = 0 return self.matrix @ np.array([x, y, z]) def tangent_array(self, ts, tangent_delta=None): xs = - self.radius * np.sin(ts) ys = self.radius * np.cos(ts) zs = np.zeros_like(xs) vectors = np.stack((xs, ys, zs)).T result = np.apply_along_axis(lambda v: self.matrix @ v, 1, vectors) return result # def second_derivative_array(self, ts): # xs = - np.cos(ts) # ys = - np.sin(ts) # zs = np.zeros_like(xs) # vectors = np.stack((xs, ys, zs)).T # return np.apply_along_axis(lambda v: self.matrix @ v, 1, vectors) 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 _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(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_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 to_nurbs_quadric(self, n=3, t_min=None, t_max=None, parametrization = 'C2', implementation = SvNurbsMaths.NATIVE): """ Convert the circle to NURBS curve with 4-degree parametrization. This implements the algorithm described in the paper: Carole Blanc, Christophe Schlick. More Accurate Representation of Conics by NURBS. Technical Report, LaBRI, 1995. Args: n: number of subdivisions, usually 3, 4 or 6. t_min, t_max: indicate the arc to be converted. parametrization: 'C2' for parametrization with continuous 2nd derivative; 'QIDEAL' for quasi-ideal parametrization, i.e. for parametrization which is almost identical to trigonometric parametrization. implementation: implementation of Nurbs mathematics. Returns: an instance of SvNurbsCurve. """ if parametrization not in {'C2', 'QIDEAL'}: raise Exception("Unsupported parametrization type") 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_quadric(n=n, t_max=t_min, t_min=t_max, implementation=implementation).reverse() def make_quad_arc(t1, t2): p1 = [cos(t1), sin(t1), 0.0] alpha = (t2 - t1)/2.0 r_mid = 1.0 / cos(alpha) t_mid = (t1 + t2)/2.0 p_mid = [r_mid*cos(t_mid), r_mid*sin(t_mid), 0.0] p2 = [cos(t2), sin(t2), 0.0] points = np.array([p1, p_mid, p2]) w = cos(alpha) return points, w def make_quadric_arc(t1, t2): ps, w = make_quad_arc(t1, t2) if parametrization == 'C2': p = (1.0 + sqrt(5.0 + 4.0*w)) / (2*(1.0 + w)) else: phi = (t2 - t1) / 2.0 cosphi = cos(phi / 5.0) p = (4 - 2*cosphi**3 + cosphi) / (-1 + 8*cosphi**3 - 4*cosphi) q0 = ps[0] q1 = (ps[0] + w*ps[1])/(1.0 + w) q2 = (p*p*ps[0] + 2*w*(1+p*p)*ps[1] + p*p*ps[2]) / (2*(p*p + p*p*w + w)) q3 = (ps[2] + w*ps[1]) / (1.0 + w) q4 = ps[2] w0 = 1.0 w1 = p*(1.0 + w)/2.0 w2 = (p*p + p*p*w + w)/3.0 w3 = w1 w4 = w0 degree = 4 knots = (t2 - t1) * sv_knotvector.generate(degree, 5) control_points = np.array([q0, q1, q2, q3, q4]) weights = np.array([w0, w1, w2, w3, w4]) return SvNurbsMaths.build_curve(implementation, degree, knots, control_points, weights) omega = t_max - t_min alpha = pi / n full_arc_angle = 2*alpha n_full_arcs = round(omega // full_arc_angle) small_arc_angle = omega % full_arc_angle ts_full = [t_min + full_arc_angle*i for i in range(n_full_arcs+1)] full_arcs = [make_quadric_arc(t1, t2) for t1,t2 in zip(ts_full, ts_full[1:])] if small_arc_angle > 1e-6: small_arc = make_quadric_arc(ts_full[-1], t_max) small_arcs = [small_arc] else: small_arcs = [] all_arcs = full_arcs + small_arcs unit_arc = all_arcs[0] for arc in all_arcs[1:]: unit_arc = unit_arc.concatenate(arc) control_points = unit_arc.get_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 curve = unit_arc.copy(control_points = control_points) return curve def to_nurbs_full(self, n=4, parametrization = 'SIMPLE', implementation = SvNurbsMaths.NATIVE): """ Convert fulll circle to a NURBS curve. Args: n: number of subdivisions, usually 3, 4 or 6. paramerization: 'SIMPLE' for traditional (2-degree) circle parametrization; 'C2' for 4-degree parametrization with continuous 2nd derivative; 'QIDEAL' for quasi-ideal parametrization, i.e. for 4-degree parametrization which is almost identical to trigonometric parametrization. implementation: implementation of Nurbs mathematics. Returns: an instance of SvNurbsCurve. """ if parametrization == 'SIMPLE': return self.to_nurbs_arc(n=n, implementation=implementation) elif parametrization in {'C2', 'QIDEAL'}: return self.to_nurbs_quadric(n=n, parametrization=parametrization, implementation=implementation) else: raise Exception("Unsupported parametrization type") def reverse(self): circle = self.copy() u1, u2 = self.u_bounds circle.u_bounds = (2*pi - u2, 2*pi - u1) circle.normal = - circle.normal return circle def make_revolution_surface(self, point, direction, v_min, v_max, global_origin): return self.to_nurbs().make_revolution_surface(point, direction, v_min, v_max, global_origin) def extrude_along_vector(self, vector): return self.to_nurbs().extrude_along_vector(vector) def make_ruled_surface(self, curve2, vmin, vmax): return self.to_nurbs().make_ruled_surface(curve2, vmin, vmax) def extrude_to_point(self, point): return self.to_nurbs().extrude_to_point(point) def lerp_to(self, curve2, coefficient): return self.to_nurbs().lerp_to(curve2, coefficient) def elevate_degree(self, delta=None, target=None): return self.to_nurbs().elevate_degree(delta=delta, target=target)
Ancestors
Static methods
def calc_matrix(normal, vectorx)
-
Expand source code
@staticmethod def calc_matrix(normal, vectorx): normal = normal / np.linalg.norm(normal) vx = vectorx / np.linalg.norm(vectorx) vy = np.cross(normal, vx) vy = vy / np.linalg.norm(vy) m = np.stack((vx, vy, normal)) return np.linalg.inv(m)
def from_arc(arc, z_axis='Z')
-
Make an instance of SvCircle from an instance of sverchok.utils.sv_curve_utils.Arc
Expand source code
@classmethod def from_arc(cls, arc, z_axis='Z'): """ Make an instance of SvCircle from an instance of sverchok.utils.sv_curve_utils.Arc """ if arc.radius.real == arc.radius.imag: radius = arc.radius.real radius_dx = radius_dy = 1.0 scale_x = scale_y = Matrix.Identity(4) else: radius = abs(arc.radius) radius_dx = arc.radius.real / radius radius_dy = arc.radius.imag / radius scale_x = Matrix.Scale(radius_dx, 4, (1,0,0)) scale_y = Matrix.Scale(radius_dy, 4, (0,1,0)) matrix = Matrix.Translation(Vector((arc.center.real, arc.center.imag, 0))) rotation = radians(arc.theta) angle = radians(abs(arc.delta)) rot_z = Matrix.Rotation(rotation, 4, 'Z') matrix = matrix @ scale_x @ scale_y @ rot_z if arc.delta < 0: matrix = matrix @ Matrix.Rotation(radians(180), 4, 'X') if z_axis == 'Y': matrix = Matrix.Rotation(radians(90), 4, 'X') @ matrix elif z_axis == 'X': matrix = Matrix.Rotation(radians(90), 4, 'Z') @ Matrix.Rotation(radians(90), 4, 'X') @ matrix circle = SvCircle(matrix=matrix, radius=radius) circle.u_bounds = (0, angle) return circle
def from_equation(eq)
-
Make an instance of SvCircle from an instance of CircleEquation2D/3D.
Expand source code
@classmethod def from_equation(cls, eq): """ Make an instance of SvCircle from an instance of CircleEquation2D/3D. """ # isinstance() wont work properly with "reload scripts". if type(eq).__name__ == 'CircleEquation2D': matrix = Matrix.Translation(eq.center) circle = SvCircle(matrix, eq.radius) return circle elif type(eq).__name__ == 'CircleEquation3D': if eq.point1 is not None: circle = SvCircle(center = np.array(eq.center), vectorx = np.array(eq.point1) - np.array(eq.center), normal = eq.normal) else: circle = SvCircle(eq.get_matrix(), eq.radius) if eq.arc_angle: circle.u_bounds = (0, eq.arc_angle) return circle else: raise TypeError("Unsupported argument type:" + str(eq))
Methods
def copy(self)
-
Expand source code
def copy(self): circle = SvCircle(radius=self.radius, center=self.center, normal=self.normal, vectorx=self.vectorx) circle.u_bounds = self.u_bounds return circle
def elevate_degree(self, delta=None, target=None)
-
Expand source code
def elevate_degree(self, delta=None, target=None): return self.to_nurbs().elevate_degree(delta=delta, target=target)
def extrude_along_vector(self, vector)
-
Expand source code
def extrude_along_vector(self, vector): return self.to_nurbs().extrude_along_vector(vector)
def extrude_to_point(self, point)
-
Expand source code
def extrude_to_point(self, point): return self.to_nurbs().extrude_to_point(point)
def get_actual_radius(self, tolerance=1e-10)
-
Expand source code
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 get_mu_matrix(self)
-
Expand source code
def get_mu_matrix(self): m = Matrix(self.matrix).to_4x4() m.translation = Vector(self.center) return m
def lerp_to(self, curve2, coefficient)
-
Expand source code
def lerp_to(self, curve2, coefficient): return self.to_nurbs().lerp_to(curve2, coefficient)
def make_revolution_surface(self, point, direction, v_min, v_max, global_origin)
-
Expand source code
def make_revolution_surface(self, point, direction, v_min, v_max, global_origin): return self.to_nurbs().make_revolution_surface(point, direction, v_min, v_max, global_origin)
def make_ruled_surface(self, curve2, vmin, vmax)
-
Expand source code
def make_ruled_surface(self, curve2, vmin, vmax): return self.to_nurbs().make_ruled_surface(curve2, vmin, vmax)
def reverse(self)
-
Expand source code
def reverse(self): circle = self.copy() u1, u2 = self.u_bounds circle.u_bounds = (2*pi - u2, 2*pi - u1) circle.normal = - circle.normal return circle
def set_normal(self, normal)
-
Expand source code
def set_normal(self, normal): self.normal = normal self.matrix = SvCircle.calc_matrix(normal, self.vectorx)
def to_nurbs(self, implementation='NATIVE')
-
Expand source code
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_arc(self, n=4, t_min=None, t_max=None, implementation='NATIVE')
-
Expand source code
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 to_nurbs_full(self, n=4, parametrization='SIMPLE', implementation='NATIVE')
-
Convert fulll circle to a NURBS curve.
Args
n
- number of subdivisions, usually 3, 4 or 6.
paramerization
- 'SIMPLE' for traditional (2-degree) circle parametrization; 'C2' for 4-degree parametrization with continuous 2nd derivative; 'QIDEAL' for quasi-ideal parametrization, i.e. for 4-degree parametrization which is almost identical to trigonometric parametrization.
implementation
- implementation of Nurbs mathematics.
Returns
an instance of SvNurbsCurve.
Expand source code
def to_nurbs_full(self, n=4, parametrization = 'SIMPLE', implementation = SvNurbsMaths.NATIVE): """ Convert fulll circle to a NURBS curve. Args: n: number of subdivisions, usually 3, 4 or 6. paramerization: 'SIMPLE' for traditional (2-degree) circle parametrization; 'C2' for 4-degree parametrization with continuous 2nd derivative; 'QIDEAL' for quasi-ideal parametrization, i.e. for 4-degree parametrization which is almost identical to trigonometric parametrization. implementation: implementation of Nurbs mathematics. Returns: an instance of SvNurbsCurve. """ if parametrization == 'SIMPLE': return self.to_nurbs_arc(n=n, implementation=implementation) elif parametrization in {'C2', 'QIDEAL'}: return self.to_nurbs_quadric(n=n, parametrization=parametrization, implementation=implementation) else: raise Exception("Unsupported parametrization type")
def to_nurbs_quadric(self, n=3, t_min=None, t_max=None, parametrization='C2', implementation='NATIVE')
-
Convert the circle to NURBS curve with 4-degree parametrization.
This implements the algorithm described in the paper: Carole Blanc, Christophe Schlick. More Accurate Representation of Conics by NURBS. Technical Report, LaBRI, 1995.
Args
n
- number of subdivisions, usually 3, 4 or 6.
- t_min, t_max: indicate the arc to be converted.
parametrization
- 'C2' for parametrization with continuous 2nd derivative; 'QIDEAL' for quasi-ideal parametrization, i.e. for parametrization which is almost identical to trigonometric parametrization.
implementation
- implementation of Nurbs mathematics.
Returns
an instance of SvNurbsCurve.
Expand source code
def to_nurbs_quadric(self, n=3, t_min=None, t_max=None, parametrization = 'C2', implementation = SvNurbsMaths.NATIVE): """ Convert the circle to NURBS curve with 4-degree parametrization. This implements the algorithm described in the paper: Carole Blanc, Christophe Schlick. More Accurate Representation of Conics by NURBS. Technical Report, LaBRI, 1995. Args: n: number of subdivisions, usually 3, 4 or 6. t_min, t_max: indicate the arc to be converted. parametrization: 'C2' for parametrization with continuous 2nd derivative; 'QIDEAL' for quasi-ideal parametrization, i.e. for parametrization which is almost identical to trigonometric parametrization. implementation: implementation of Nurbs mathematics. Returns: an instance of SvNurbsCurve. """ if parametrization not in {'C2', 'QIDEAL'}: raise Exception("Unsupported parametrization type") 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_quadric(n=n, t_max=t_min, t_min=t_max, implementation=implementation).reverse() def make_quad_arc(t1, t2): p1 = [cos(t1), sin(t1), 0.0] alpha = (t2 - t1)/2.0 r_mid = 1.0 / cos(alpha) t_mid = (t1 + t2)/2.0 p_mid = [r_mid*cos(t_mid), r_mid*sin(t_mid), 0.0] p2 = [cos(t2), sin(t2), 0.0] points = np.array([p1, p_mid, p2]) w = cos(alpha) return points, w def make_quadric_arc(t1, t2): ps, w = make_quad_arc(t1, t2) if parametrization == 'C2': p = (1.0 + sqrt(5.0 + 4.0*w)) / (2*(1.0 + w)) else: phi = (t2 - t1) / 2.0 cosphi = cos(phi / 5.0) p = (4 - 2*cosphi**3 + cosphi) / (-1 + 8*cosphi**3 - 4*cosphi) q0 = ps[0] q1 = (ps[0] + w*ps[1])/(1.0 + w) q2 = (p*p*ps[0] + 2*w*(1+p*p)*ps[1] + p*p*ps[2]) / (2*(p*p + p*p*w + w)) q3 = (ps[2] + w*ps[1]) / (1.0 + w) q4 = ps[2] w0 = 1.0 w1 = p*(1.0 + w)/2.0 w2 = (p*p + p*p*w + w)/3.0 w3 = w1 w4 = w0 degree = 4 knots = (t2 - t1) * sv_knotvector.generate(degree, 5) control_points = np.array([q0, q1, q2, q3, q4]) weights = np.array([w0, w1, w2, w3, w4]) return SvNurbsMaths.build_curve(implementation, degree, knots, control_points, weights) omega = t_max - t_min alpha = pi / n full_arc_angle = 2*alpha n_full_arcs = round(omega // full_arc_angle) small_arc_angle = omega % full_arc_angle ts_full = [t_min + full_arc_angle*i for i in range(n_full_arcs+1)] full_arcs = [make_quadric_arc(t1, t2) for t1,t2 in zip(ts_full, ts_full[1:])] if small_arc_angle > 1e-6: small_arc = make_quadric_arc(ts_full[-1], t_max) small_arcs = [small_arc] else: small_arcs = [] all_arcs = full_arcs + small_arcs unit_arc = all_arcs[0] for arc in all_arcs[1:]: unit_arc = unit_arc.concatenate(arc) control_points = unit_arc.get_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 curve = unit_arc.copy(control_points = control_points) return curve
Inherited members
class SvEllipse (matrix, a, b, center_type='center')
-
Ellipse curve.
Expand source code
class SvEllipse(SvCurve): """ Ellipse curve. """ __description__ = "Ellipse" CENTER = 'center' F1 = 'f1' F2 = 'f2' def __init__(self, matrix, a, b, center_type=CENTER): self.matrix = np.array(matrix.to_3x3()) self.center = np.array(matrix.translation) self.center_type = center_type self.a = a self.b = b self.u_bounds = (0, 2*pi) self.tangent_delta = 0.001 def get_u_bounds(self): return self.u_bounds @classmethod def from_equation(cls, eq): """ Build an instance of SvEllipse from `sverchok.utils.geom.Ellipse3D`. """ return SvEllipse(eq.get_matrix(), eq.a, eq.b) def to_equation(self): """ Convert an instance of SvEllipse to `sverchok.utils.geom.Ellipse3D`. """ major_radius = self.matrix @ np.array([self.a, 0, 0]) minor_radius = self.matrix @ np.array([0, self.b, 0]) eq = Ellipse3D(Vector(self.center), Vector(major_radius), Vector(minor_radius)) return eq @property def c(self): a, b = self.a, self.b return sqrt(a*a - b*b) def focal_points(self): """ Calculate ellipse focal points. Returns: list of two points. """ df = self.matrix @ np.array([self.c, 0, 0]) f1 = self.center + df f2 = self.center - df return [f1, f2] def get_center(self): """ Calculate ellipse center. """ if self.center_type == SvEllipse.CENTER: return self.center elif self.center_type == SvEllipse.F1: df = self.matrix @ np.array([self.c, 0, 0]) return self.center + df else: # F2 df = self.matrix @ np.array([self.c, 0, 0]) return self.center - df def get_degree(self): return 2 def evaluate(self, t): v = np.array([self.a * cos(t), self.b * sin(t), 0]) center = self.get_center() v = center + self.matrix @ v return v def evaluate_array(self, ts): xs = self.a * np.cos(ts) ys = self.b * np.sin(ts) zs = np.zeros_like(xs) vs = np.array((xs, ys, zs)).T vs = np.apply_along_axis(lambda v : self.matrix @ v, 1, vs) center = self.get_center() return center + vs def tangent(self, t, tangent_delta=None): return self.tangent_array(np.array([t]))[0] def tangent_array(self, ts, tangent_delta=None): xs = - self.a * np.sin(ts) ys = self.b * np.cos(ts) zs = np.zeros_like(xs) vs = np.array((xs, ys, zs)).T vs = np.apply_along_axis(lambda v : self.matrix @ v, 1, vs) return vs def second_derivative(self, t, tangent_delta=None): return self.second_derivative_array(np.array([t]))[0] def second_derivative_array(self, ts, tangent_delta=None): xs = - self.a * np.cos(ts) ys = - self.b * np.sin(ts) zs = np.zeros_like(xs) vs = np.array((xs, ys, zs)).T vs = np.apply_along_axis(lambda v : self.matrix @ v, 1, vs) return vs def to_nurbs(self, implementation=SvNurbsMaths.NATIVE): """ Convert the ellipse to SvNurbsCurve. """ if self.a == 0 and self.b == 0: coef_x = 0 coef_y = 0 radius = 0 elif self.a == 0: coef_x = 0 coef_y = 1 radius = self.b elif self.b == 0: coef_x = 1 coef_y = 0 radius = self.a else: coef_x = 1 coef_y = self.b/self.a radius = self.a scale = Matrix([[coef_x,0,0], [0, coef_y, 0], [0, 0, 1]]).to_4x4() matrix = Matrix(self.matrix).to_4x4() matrix.translation = Vector(self.get_center()) circle = SvCircle(matrix = matrix @ scale, radius = radius, center = self.get_center()) return circle.to_nurbs(implementation) def concatenate(self, curve2, tolerance=1e-6, remove_knots=False): return self.to_nurbs().concatenate(curve2, tolerance=tolerance, remove_knots=remove_knots)
Ancestors
Class variables
var CENTER
var F1
var F2
Static methods
def from_equation(eq)
-
Build an instance of SvEllipse from
Ellipse3D
.Expand source code
@classmethod def from_equation(cls, eq): """ Build an instance of SvEllipse from `sverchok.utils.geom.Ellipse3D`. """ return SvEllipse(eq.get_matrix(), eq.a, eq.b)
Instance variables
var c
-
Expand source code
@property def c(self): a, b = self.a, self.b return sqrt(a*a - b*b)
Methods
def concatenate(self, curve2, tolerance=1e-06, remove_knots=False)
-
Expand source code
def concatenate(self, curve2, tolerance=1e-6, remove_knots=False): return self.to_nurbs().concatenate(curve2, tolerance=tolerance, remove_knots=remove_knots)
def focal_points(self)
-
Calculate ellipse focal points.
Returns
list of two points.
Expand source code
def focal_points(self): """ Calculate ellipse focal points. Returns: list of two points. """ df = self.matrix @ np.array([self.c, 0, 0]) f1 = self.center + df f2 = self.center - df return [f1, f2]
def get_center(self)
-
Calculate ellipse center.
Expand source code
def get_center(self): """ Calculate ellipse center. """ if self.center_type == SvEllipse.CENTER: return self.center elif self.center_type == SvEllipse.F1: df = self.matrix @ np.array([self.c, 0, 0]) return self.center + df else: # F2 df = self.matrix @ np.array([self.c, 0, 0]) return self.center - df
def second_derivative(self, t, tangent_delta=None)
-
Expand source code
def second_derivative(self, t, tangent_delta=None): return self.second_derivative_array(np.array([t]))[0]
def second_derivative_array(self, ts, tangent_delta=None)
-
Expand source code
def second_derivative_array(self, ts, tangent_delta=None): xs = - self.a * np.cos(ts) ys = - self.b * np.sin(ts) zs = np.zeros_like(xs) vs = np.array((xs, ys, zs)).T vs = np.apply_along_axis(lambda v : self.matrix @ v, 1, vs) return vs
def to_equation(self)
-
Convert an instance of SvEllipse to
Ellipse3D
.Expand source code
def to_equation(self): """ Convert an instance of SvEllipse to `sverchok.utils.geom.Ellipse3D`. """ major_radius = self.matrix @ np.array([self.a, 0, 0]) minor_radius = self.matrix @ np.array([0, self.b, 0]) eq = Ellipse3D(Vector(self.center), Vector(major_radius), Vector(minor_radius)) return eq
def to_nurbs(self, implementation='NATIVE')
-
Convert the ellipse to SvNurbsCurve.
Expand source code
def to_nurbs(self, implementation=SvNurbsMaths.NATIVE): """ Convert the ellipse to SvNurbsCurve. """ if self.a == 0 and self.b == 0: coef_x = 0 coef_y = 0 radius = 0 elif self.a == 0: coef_x = 0 coef_y = 1 radius = self.b elif self.b == 0: coef_x = 1 coef_y = 0 radius = self.a else: coef_x = 1 coef_y = self.b/self.a radius = self.a scale = Matrix([[coef_x,0,0], [0, coef_y, 0], [0, 0, 1]]).to_4x4() matrix = Matrix(self.matrix).to_4x4() matrix.translation = Vector(self.get_center()) circle = SvCircle(matrix = matrix @ scale, radius = radius, center = self.get_center()) return circle.to_nurbs(implementation)
Inherited members
class SvLine (point, direction, u_bounds=None)
-
Straight line segment curve.
Args
point
- a point on a line.
direction
- directing vector of a line.
Expand source code
class SvLine(SvCurve): """ Straight line segment curve. """ def __init__(self, point, direction, u_bounds=None): """ Args: point: a point on a line. direction: directing vector of a line. """ self.point = np.array(point) self.direction = np.array(direction) if u_bounds is None: u_bounds = (0.0, 1.0) self.u_bounds = u_bounds def __repr__(self): return f"<{self.point} - {self.point+self.direction}>" @classmethod def from_two_points(cls, point1, point2): """ Generate straight line segment from two points. """ direction = np.array(point2) - np.array(point1) return SvLine(point1, direction) def copy(self, u_bounds=None): if u_bounds is None: u_bounds = self.u_bounds return SvLine(self.point, self.direction, u_bounds=u_bounds) def get_degree(self): return 1 def get_u_bounds(self): return self.u_bounds def evaluate(self, t): return self.point + t * self.direction def evaluate_array(self, ts): ts = ts[np.newaxis].T return self.point + ts * self.direction def tangent(self, t, tangent_delta=None): tg = self.direction n = np.linalg.norm(tg) return tg / n def tangent_array(self, ts, tangent_delta=None): tg = self.direction n = np.linalg.norm(tg) tangent = tg / n result = np.tile(tangent[np.newaxis].T, len(ts)).T return result def extrude_along_vector(self, vector): return SvPlane(self.point, self.direction, vector) def make_revolution_surface(self, point, direction, v_min, v_max, global_origin): return self.to_nurbs().make_revolution_surface(point, direction, v_min, v_max, global_origin) def make_ruled_surface(self, curve2, vmin, vmax): return self.to_nurbs().make_ruled_surface(curve2, vmin, vmax) def extrude_to_point(self, point): return self.to_nurbs().extrude_to_point(point) def lerp_to(self, curve2, coefficient): return self.to_nurbs().lerp_to(curve2, coefficient) def split_at(self, t): t_min, t_max = self.get_u_bounds() curve1 = self.copy(u_bounds=(t_min, t)) curve2 = self.copy(u_bounds=(t, t_max)) return curve1, curve2 def reverse(self): t_min, t_max = self.get_u_bounds() p1, p2 = self.evaluate(t_min), self.evaluate(t_max) return SvLine.from_two_points(p2, p1) def to_nurbs(self, implementation=SvNurbsMaths.NATIVE): u_min, u_max = self.get_u_bounds() knotvector = sv_knotvector.generate(1, 2) knotvector = sv_knotvector.rescale(knotvector, u_min, u_max) 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_bezier(self): u_min, u_max = self.get_u_bounds() p1 = self.evaluate(u_min) p2 = self.evaluate(u_max) return SvBezierCurve([p1, p2]) def to_bezier_segments(self): return [self.to_bezier()] def concatenate(self, curve2, tolerance=1e-6, remove_knots=False): return self.to_nurbs().concatenate(curve2, tolerance=tolerance, remove_knots=remove_knots) def reparametrize(self, new_t_min, new_t_max): t_min, t_max = self.get_u_bounds() scale = (t_max - t_min) / (new_t_max - new_t_min) new_direction = self.direction * scale new_point = self.point + self.direction * (t_min - scale * new_t_min) return SvLine(new_point, new_direction, u_bounds = (new_t_min, new_t_max)) def is_polyline(self): return True def get_polyline_vertices(self): return np.array(self.get_end_points()) def is_closed(self, *args): return False def extrude_along_vector(self, vector): return self.to_nurbs().extrude_along_vector(vector) def make_revolution_surface(self, point, direction, v_min, v_max, global_origin): return self.to_nurbs().make_revolution_surface(point, direction, v_min, v_max, global_origin) def make_ruled_surface(self, curve2, vmin, vmax): return self.to_nurbs().make_ruled_surface(curve2, vmin, vmax) def extrude_to_point(self, point): return self.to_nurbs().extrude_to_point(point) def lerp_to(self, curve2, coefficient): return self.to_nurbs().lerp_to(curve2, coefficient)
Ancestors
Static methods
def from_two_points(point1, point2)
-
Generate straight line segment from two points.
Expand source code
@classmethod def from_two_points(cls, point1, point2): """ Generate straight line segment from two points. """ direction = np.array(point2) - np.array(point1) return SvLine(point1, direction)
Methods
def concatenate(self, curve2, tolerance=1e-06, remove_knots=False)
-
Expand source code
def concatenate(self, curve2, tolerance=1e-6, remove_knots=False): return self.to_nurbs().concatenate(curve2, tolerance=tolerance, remove_knots=remove_knots)
def copy(self, u_bounds=None)
-
Expand source code
def copy(self, u_bounds=None): if u_bounds is None: u_bounds = self.u_bounds return SvLine(self.point, self.direction, u_bounds=u_bounds)
def extrude_along_vector(self, vector)
-
Expand source code
def extrude_along_vector(self, vector): return self.to_nurbs().extrude_along_vector(vector)
def extrude_to_point(self, point)
-
Expand source code
def extrude_to_point(self, point): return self.to_nurbs().extrude_to_point(point)
def get_polyline_vertices(self)
-
Expand source code
def get_polyline_vertices(self): return np.array(self.get_end_points())
def is_closed(self, *args)
-
Expand source code
def is_closed(self, *args): return False
def is_polyline(self)
-
Expand source code
def is_polyline(self): return True
def lerp_to(self, curve2, coefficient)
-
Expand source code
def lerp_to(self, curve2, coefficient): return self.to_nurbs().lerp_to(curve2, coefficient)
def make_revolution_surface(self, point, direction, v_min, v_max, global_origin)
-
Expand source code
def make_revolution_surface(self, point, direction, v_min, v_max, global_origin): return self.to_nurbs().make_revolution_surface(point, direction, v_min, v_max, global_origin)
def make_ruled_surface(self, curve2, vmin, vmax)
-
Expand source code
def make_ruled_surface(self, curve2, vmin, vmax): return self.to_nurbs().make_ruled_surface(curve2, vmin, vmax)
def reparametrize(self, new_t_min, new_t_max)
-
Expand source code
def reparametrize(self, new_t_min, new_t_max): t_min, t_max = self.get_u_bounds() scale = (t_max - t_min) / (new_t_max - new_t_min) new_direction = self.direction * scale new_point = self.point + self.direction * (t_min - scale * new_t_min) return SvLine(new_point, new_direction, u_bounds = (new_t_min, new_t_max))
def reverse(self)
-
Expand source code
def reverse(self): t_min, t_max = self.get_u_bounds() p1, p2 = self.evaluate(t_min), self.evaluate(t_max) return SvLine.from_two_points(p2, p1)
def split_at(self, t)
-
Expand source code
def split_at(self, t): t_min, t_max = self.get_u_bounds() curve1 = self.copy(u_bounds=(t_min, t)) curve2 = self.copy(u_bounds=(t, t_max)) return curve1, curve2
def to_bezier(self)
-
Expand source code
def to_bezier(self): u_min, u_max = self.get_u_bounds() p1 = self.evaluate(u_min) p2 = self.evaluate(u_max) return SvBezierCurve([p1, p2])
def to_bezier_segments(self)
-
Expand source code
def to_bezier_segments(self): return [self.to_bezier()]
def to_nurbs(self, implementation='NATIVE')
-
Expand source code
def to_nurbs(self, implementation=SvNurbsMaths.NATIVE): u_min, u_max = self.get_u_bounds() knotvector = sv_knotvector.generate(1, 2) knotvector = sv_knotvector.rescale(knotvector, u_min, u_max) 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)
Inherited members
class SvPointCurve (point)
-
Expand source code
class SvPointCurve(SvCurve): __description__ = "Single-Point" def __init__(self, point): self.point = np.asarray(point) def evaluate(self, t): return self.point def evaluate_array(self, ts): points = np.empty((len(ts),3)) points[:] = self.point return points def get_u_bounds(self): return (0.0, 1.0) def get_degree(self): return 1 def to_bezier(self): u_min, u_max = self.get_u_bounds() p1 = self.evaluate(u_min) p2 = self.evaluate(u_max) return SvBezierCurve([p1, p2]) def to_bezier_segments(self): return [self.to_bezier()] def is_closed(self, *args): return False def concatenate(self, curve2, *args): return curve2 def to_nurbs(self, implementation = SvNurbsMaths.NATIVE): return self.to_bezier().to_nurbs() def reverse(self): return SvPointCurve(self.point)
Ancestors
Methods
def concatenate(self, curve2, *args)
-
Expand source code
def concatenate(self, curve2, *args): return curve2
def is_closed(self, *args)
-
Expand source code
def is_closed(self, *args): return False
def reverse(self)
-
Expand source code
def reverse(self): return SvPointCurve(self.point)
def to_bezier(self)
-
Expand source code
def to_bezier(self): u_min, u_max = self.get_u_bounds() p1 = self.evaluate(u_min) p2 = self.evaluate(u_max) return SvBezierCurve([p1, p2])
def to_bezier_segments(self)
-
Expand source code
def to_bezier_segments(self): return [self.to_bezier()]
def to_nurbs(self, implementation='NATIVE')
-
Expand source code
def to_nurbs(self, implementation = SvNurbsMaths.NATIVE): return self.to_bezier().to_nurbs()
Inherited members