def nurbs_taper_sweep(profile, taper, point, direction, scale_base=SvTaperSweepSurface.UNIT): axis = LineEquation.from_direction_and_point(direction, point) taper_cpts = taper.get_control_points() taper_weights = taper.get_weights() taper_projections = axis.projection_of_points(taper_cpts) control_points = [] weights = [] if scale_base == SvTaperSweepSurface.TAPER: profile_t_min, profile_t_max = profile.get_u_bounds() profile_start = profile.evaluate(profile_t_min) profile_start_projection = axis.projection_of_point(profile_start) divisor = np.linalg.norm(profile_start - profile_start_projection) elif scale_base == SvTaperSweepSurface.PROFILE: taper_t_min, taper_t_max = taper.get_u_bounds() taper_start = taper.evaluate(taper_t_min) taper_start_projection = np.array( axis.projection_of_point(taper_start)) divisor = np.linalg.norm(taper_start_projection - taper_start) else: divisor = 1.0 profile_cpts = profile.get_control_points() n = len(profile_cpts) profile_knotvector = profile.get_knotvector() profile_weights = profile.get_weights() for taper_control_point, taper_projection, taper_weight in zip( taper_cpts, taper_projections, taper_weights): radius = np.linalg.norm(taper_control_point - taper_projection) if radius < 1e-8: parallel_points = np.empty((n, 3)) parallel_points[:] = taper_projection else: parallel_points = place_profile(profile_cpts, taper_projection, radius / divisor) parallel_weights = profile_weights * taper_weight control_points.append(parallel_points) weights.append(parallel_weights) control_points = np.array(control_points) control_points -= point weights = np.array(weights) degree_u = taper.get_degree() degree_v = profile.get_degree() return SvNurbsSurface.build(taper.get_nurbs_implementation(), degree_u, degree_v, taper.get_knotvector(), profile_knotvector, control_points, weights)
def __init__(self, profile, taper, point, direction): self.profile = profile self.taper = taper self.direction = direction self.point = point self.line = LineEquation.from_direction_and_point(direction, point) self.normal_delta = 0.001
def make_section(self, apex, cone_dir, alpha, cone_gen, count, plane_center, plane_dir, maxd): apex = Vector(apex) cone_dir = Vector(cone_dir) plane_dir = Vector(plane_dir) cone_dir2 = cone_dir.orthogonal() if self.cone_mode == 'ANGLE': cone_vector = rotate_vector_around_vector(cone_dir, cone_dir2, alpha) else: cone_vector = Vector(cone_gen) theta = 2 * pi / count angle = 0 plane = PlaneEquation.from_normal_and_point(plane_dir, plane_center) cone_ort_plane = PlaneEquation.from_normal_and_point(cone_dir, apex) def get_branch(v): return cone_ort_plane.side_of_point(v) > 0 if plane.side_of_point(apex) == 0 or ( plane_dir.cross(cone_dir)).length < 1e-10: def get_side(v): return True else: apex_projection = plane.projection_of_point(apex) apex_ort = apex_projection - apex cone_sagital_plane = PlaneEquation.from_point_and_two_vectors( apex, apex_ort, cone_dir) def get_side(v): return cone_sagital_plane.side_of_point(v) > 0 vertices = [] branch_mask = [] side_mask = [] breaks = [] i = 0 while angle < 2 * pi: cone_line = LineEquation.from_direction_and_point( cone_vector, apex) vertex = plane.intersect_with_line(cone_line, min_det=1e-10) if vertex is not None and (vertex - apex).length <= maxd: vertices.append(tuple(vertex)) branch = get_branch(vertex) side = get_side(vertex) branch_mask.append(branch) side_mask.append(side) i += 1 else: breaks.append(i) cone_vector = rotate_vector_around_vector(cone_vector, cone_dir, theta) angle += theta return SectionData(vertices, branch_mask, side_mask, get_branch, get_side, breaks)
def evaluate(self, x, y, z): v = Vector([x, y, z]) dv1 = (v - self.v1).length dv2 = (v - self.v2).length if dv1 > dv2: distance_to_nearest = dv2 nearest_vert = self.v2 another_vert = self.v1 else: distance_to_nearest = dv1 nearest_vert = self.v1 another_vert = self.v2 edge = another_vert - nearest_vert to_nearest = v - nearest_vert if to_nearest.length == 0: return 0 angle = edge.angle(to_nearest) if angle > pi / 2: distance = distance_to_nearest else: distance = LineEquation.from_two_points( self.v1, self.v2).distance_to_point(v) if self.falloff is not None: value = self.falloff(np.array([distance]))[0] return value else: return distance
def evaluate(self, x, y, z): v = Vector([x,y,z]) dv1 = (v - self.v1).length dv2 = (v - self.v2).length if dv1 > dv2: distance_to_nearest = dv2 nearest_vert = self.v2 another_vert = self.v1 else: distance_to_nearest = dv1 nearest_vert = self.v1 another_vert = self.v2 edge = another_vert - nearest_vert to_nearest = v - nearest_vert if to_nearest.length == 0: return 0 angle = edge.angle(to_nearest) if angle > pi/2: distance = distance_to_nearest vector = - to_nearest else: vector = LineEquation.from_two_points(self.v1, self.v2).projection_of_points(v) distance = vector.length vector = np.array(vector) if self.falloff is not None: return self.falloff(distance) * vector / distance else: return vector
def evaluate_grid(self, xs, ys, zs): n = len(xs) vs = np.stack((xs, ys, zs)).T v1 = np.array(self.v1) v2 = np.array(self.v2) dv1s = np.linalg.norm(vs - v1, axis=1) dv2s = np.linalg.norm(vs - v2, axis=1) v1_is_nearest = (dv1s < dv2s) v2_is_nearest = np.logical_not(v1_is_nearest) nearest_verts = np.empty_like(vs) other_verts = np.empty_like(vs) nearest_verts[v1_is_nearest] = v1 nearest_verts[v2_is_nearest] = v2 other_verts[v1_is_nearest] = v2 other_verts[v2_is_nearest] = v1 to_nearest = vs - nearest_verts edges = other_verts - nearest_verts dot = (to_nearest * edges).sum(axis=1) at_edge = (dot > 0) at_vertex = np.logical_not(at_edge) at_v1 = np.logical_and(at_vertex, v1_is_nearest) at_v2 = np.logical_and(at_vertex, v2_is_nearest) distances = np.empty((n,)) distances[at_edge] = LineEquation.from_two_points(self.v1, self.v2).distance_to_points(vs[at_edge]) distances[at_v1] = dv1s[at_v1] distances[at_v2] = dv2s[at_v2] if self.falloff is not None: distances = self.falloff(distances) return distances else: return distances
def _faces_by_cylinder(self, topo, center, direction, radius): line = LineEquation.from_direction_and_point(direction, center) def condition(points): distances = line.distance_to_points(points) return distances < radius return topo.get_faces_by_location_mask(condition, self.include_partial)
def __init__(self, curve, center, direction, radius, coefficient): self.curve = curve self.center = center self.direction = direction self.radius = radius self.coefficient = coefficient self.line = LineEquation.from_direction_and_point(direction, center) self.tangent_delta = 0.001 self.__description__ = "{} casted to Cylinder".format(curve)
def linear_search(segment): cpts = segment.get_control_points() start, end = cpts[0], cpts[-1] line = LineEquation.from_two_points(start, end) p = line.projection_of_point(src_point) t = locate_linear(start, end, p) if 0.0 <= t <= 1.0: u1, u2 = segment.get_u_bounds() u = (1 - t) * u1 + t * u2 return u else: return None
def intersect_line(segment): cpts = segment.get_control_points() p1, p2 = cpts[0], cpts[-1] line = LineEquation.from_two_points(p1, p2) p = plane.intersect_with_line(line) p = np.array(p) u = locate_p(p1, p2, p) u1, u2 = segment.get_u_bounds() if u >= 0 and u <= 1.0: v = (1 - u) * u1 + u * u2 return v, p else: return None
def evaluate_grid(self, xs, ys, zs): n = len(xs) vs = np.stack((xs, ys, zs)).T v1 = np.array(self.v1) v2 = np.array(self.v2) dv1s = np.linalg.norm(vs - v1, axis=1) dv2s = np.linalg.norm(vs - v2, axis=1) v1_is_nearest = (dv1s < dv2s) v2_is_nearest = np.logical_not(v1_is_nearest) nearest_verts = np.empty_like(vs) other_verts = np.empty_like(vs) nearest_verts[v1_is_nearest] = v1 nearest_verts[v2_is_nearest] = v2 other_verts[v1_is_nearest] = v2 other_verts[v2_is_nearest] = v1 to_nearest = vs - nearest_verts edges = other_verts - nearest_verts dot = (to_nearest * edges).sum(axis=1) at_edge = (dot > 0) at_vertex = np.logical_not(at_edge) at_v1 = np.logical_and(at_vertex, v1_is_nearest) at_v2 = np.logical_and(at_vertex, v2_is_nearest) line = LineEquation.from_two_points(self.v1, self.v2) vectors = np.empty((n, 3)) vectors[at_edge] = line.projection_of_points(vs[at_edge]) - vs[at_edge] vectors[at_v1] = v1 - vs[at_v1] vectors[at_v2] = v2 - vs[at_v2] if self.falloff is not None: norms = np.linalg.norm(vectors, axis=1, keepdims=True) lens = self.falloff(norms) nonzero = (norms > 0)[:, 0] vectors[nonzero] = vectors[nonzero] / norms[nonzero][:, 0][ np.newaxis].T R = (lens * vectors).T return R[0], R[1], R[2] else: R = vectors.T return R[0], R[1], R[2]
def test_intersect_with_line(self): plane = PlaneEquation.from_coordinate_plane('XY') line = LineEquation.from_direction_and_point((1, 1, 1), (1, 1, 1)) point = plane.intersect_with_line(line) self.assert_sverchok_data_equal(tuple(point), (0, 0, 0))
def test_distance_to_point(self): line = LineEquation.from_coordinate_axis('Z') point = (0, 2, 0) self.assertEquals(line.distance_to_point(point), 2)
def test_check_no(self): p1 = (1, 1, 1) p2 = (3, 3, 3) line = LineEquation.from_two_points(p1, p2) p3 = (5, 5, 6) self.assertFalse(line.check(p3))
def test_from_two_points(self): p1 = (1, 1, 1) p2 = (3, 3, 3) line = LineEquation.from_two_points(p1, p2) self.assert_sverchok_data_equal(tuple(line.direction), (2, 2, 2)) self.assert_sverchok_data_equal(tuple(line.point), p1)
def test_distance_to_point(self): line = LineEquation.from_coordinate_axis('Z') point = (0, 2, 0) self.assertEquals(line.distance_to_point(point), 2)
def test_check_no(self): p1 = (1, 1, 1) p2 = (3, 3, 3) line = LineEquation.from_two_points(p1, p2) p3 = (5, 5, 6) self.assertFalse(line.check(p3))
def test_from_two_points(self): p1 = (1, 1, 1) p2 = (3, 3, 3) line = LineEquation.from_two_points(p1, p2) self.assert_sverchok_data_equal(tuple(line.direction), (2, 2, 2)) self.assert_sverchok_data_equal(tuple(line.point), p1)
def test_intersect_with_line(self): plane = PlaneEquation.from_coordinate_plane('XY') line = LineEquation.from_direction_and_point((1, 1, 1), (1, 1, 1)) point = plane.intersect_with_line(line) self.assert_sverchok_data_equal(tuple(point), (0, 0, 0))
def intersect_curve_plane_ortho(curve, plane, init_samples=10, ortho_samples=10, tolerance=1e-3, maxiter=50): """ Find intersections of curve and a plane, by combination of orthogonal projections with tangent projections. inputs: * curve : SvCurve * plane : sverchok.utils.geom.PlaneEquation * init_samples: number of samples to subdivide the curve to; this defines the maximum possible number of solutions the method will return (the solution is searched at each segment). * ortho_samples: number of samples for ortho_project_curve method * tolerance: target tolerance * maxiter: maximum number of iterations outputs: list of intersection points dependencies: scipy """ u_min, u_max = curve.get_u_bounds() u_range = np.linspace(u_min, u_max, num=init_samples) init_points = curve.evaluate_array(u_range) init_signs = plane.side_of_points(init_points) good_ranges = [] for u1, u2, sign1, sign2 in zip(u_range, u_range[1:], init_signs, init_signs[1:]): if sign1 * sign2 < 0: good_ranges.append((u1, u2)) if not good_ranges: return [] solutions = [] for u1, u2 in good_ranges: u0 = u1 tangent = curve.tangent(u0) tangent /= np.linalg.norm(tangent) point = curve.evaluate(u0) line = LineEquation.from_direction_and_point(tangent, point) p = plane.intersect_with_line(line) if p is None: u0 = u2 tangent = curve.tangent(u0) tangent /= np.linalg.norm(tangent) point = curve.evaluate(u0) line = LineEquation.from_direction_and_point(tangent, point) p = plane.intersect_with_line(line) if p is None: raise Exception("Can't find initial point for intersection") i = 0 prev_prev_point = None prev_point = np.array(p) while True: i += 1 if i > maxiter: raise Exception( "Maximum number of iterations is exceeded; last step {} - {} = {}" .format(prev_prev_point, point, step)) ortho = ortho_project_curve(prev_point, curve, init_samples=ortho_samples) point = ortho.nearest step = np.linalg.norm(point - prev_point) if step < tolerance: debug("After ortho: Point {}, prev {}, iter {}".format( point, prev_point, i)) break prev_point = point tangent = curve.tangent(ortho.nearest_u) tangent /= np.linalg.norm(tangent) point = curve.evaluate(ortho.nearest_u) line = LineEquation.from_direction_and_point(tangent, point) point = plane.intersect_with_line(line) if point is None: raise Exception( "Can't intersect a line {} with a plane {}".format( line, point)) point = np.array(point) step = np.linalg.norm(point - prev_point) if step < tolerance: debug("After raycast: Point {}, prev {}, iter {}".format( point, prev_point, i)) break prev_prev_point = prev_point prev_point = point solutions.append(point) return solutions
def intersect_curve_plane(curve, plane, init_samples=10, ortho_samples=10, tolerance=1e-3, maxiter=50): u_min, u_max = curve.get_u_bounds() u_range = np.linspace(u_min, u_max, num=init_samples) init_points = curve.evaluate_array(u_range) init_signs = plane.side_of_points(init_points) good_ranges = [] for u1, u2, sign1, sign2 in zip(u_range, u_range[1:], init_signs, init_signs[1:]): if sign1 * sign2 < 0: good_ranges.append((u1, u2)) if not good_ranges: return [] solutions = [] for u1, u2 in good_ranges: u0 = u1 tangent = curve.tangent(u0) tangent /= np.linalg.norm(tangent) point = curve.evaluate(u0) line = LineEquation.from_direction_and_point(tangent, point) p = plane.intersect_with_line(line) if p is None: u0 = u2 tangent = curve.tangent(u0) tangent /= np.linalg.norm(tangent) point = curve.evaluate(u0) line = LineEquation.from_direction_and_point(tangent, point) p = plane.intersect_with_line(line) if p is None: raise Exception("Can't find initial point for intersection") i = 0 prev_prev_point = None prev_point = np.array(p) while True: i += 1 if i > maxiter: raise Exception( "Maximum number of iterations is exceeded; last step {} - {} = {}" .format(prev_prev_point, point, step)) ortho = ortho_project_curve(prev_point, curve, init_samples=ortho_samples) point = ortho.nearest step = np.linalg.norm(point - prev_point) if step < tolerance: debug("After ortho: Point {}, prev {}, iter {}".format( point, prev_point, i)) break prev_point = point tangent = curve.tangent(ortho.nearest_u) tangent /= np.linalg.norm(tangent) point = curve.evaluate(ortho.nearest_u) line = LineEquation.from_direction_and_point(tangent, point) point = plane.intersect_with_line(line) if point is None: raise Exception( "Can't intersect a line {} with a plane {}".format( line, point)) point = np.array(point) step = np.linalg.norm(point - prev_point) if step < tolerance: debug("After raycast: Point {}, prev {}, iter {}".format( point, prev_point, i)) break prev_prev_point = prev_point prev_point = point solutions.append(point) return solutions
def _verts_by_cylinder(self, topo, center, direction, radius): line = LineEquation.from_direction_and_point(direction, center) condition = lambda v: line.distance_to_point(v) < radius return topo.get_vertices_by_location_mask(condition)