def normal_polygon(polygon, unitized=True): """Compute the normal of a polygon defined by a sequence of points. Parameters ---------- polygon : list of list A list of polygon point coordinates. Returns ------- list The normal vector. Raises ------ ValueError If less than three points are provided. Notes ----- The points in the list should be unique. For example, the first and last point in the list should not be the same. """ p = len(polygon) assert p > 2, "At least three points required" nx = 0 ny = 0 nz = 0 o = centroid_points(polygon) a = polygon[-1] oa = subtract_vectors(a, o) for i in range(p): b = polygon[i] ob = subtract_vectors(b, o) n = cross_vectors(oa, ob) oa = ob nx += n[0] ny += n[1] nz += n[2] if not unitized: return nx, ny, nz length = length_vector([nx, ny, nz]) return nx / length, ny / length, nz / length
def center_of_mass_polyhedron(polyhedron): """Compute the center of mass of a polyhedron""" vertices, faces = polyhedron V = 0 x = 0.0 y = 0.0 z = 0.0 ex = [1.0, 0.0, 0.0] ey = [0.0, 1.0, 0.0] ez = [0.0, 0.0, 1.0] for face in faces: if len(face) == 3: triangles = [face] else: triangles = [] # for i in range(1, len(face) - 1): # triangles.append(face[0:1] + vertices[i:i + 2]) for triangle in triangles: a = vertices[triangle[0]] b = vertices[triangle[1]] c = vertices[triangle[2]] ab = subtract_vectors(b, a) ac = subtract_vectors(c, a) n = cross_vectors(ab, ac) V += dot_vectors(a, n) nx = dot_vectors(n, ex) ny = dot_vectors(n, ey) nz = dot_vectors(n, ez) for j in (-1, 0, 1): ab = add_vectors(vertices[triangle[j]], vertices[triangle[j + 1]]) x += nx * dot_vectors(ab, ex)**2 y += ny * dot_vectors(ab, ey)**2 z += nz * dot_vectors(ab, ez)**2 if V < 1e-9: V = 0.0 d = 1.0 / 48.0 else: V = V / 6.0 d = 1.0 / 48.0 / V x *= d y *= d z *= d return x, y, z
def reflect_line_triangle(line, triangle, tol=1e-6): """Bounce a line of a reflection triangle. Parameters ---------- line : tuple Two points defining the line. triangle : tuple The triangle vertices. tol : float, optional A tolerance for membership verification. Default is ``1e-6``. Returns ------- tuple The reflected line defined by the intersection point of the line and triangle and the mirrored start point of the line with respect to a line perpendicular to the triangle through the intersection. Notes ----- The direction of the line and triangle are important. The line is only reflected if it points towards the front of the triangle. This is true if the dot product of the direction vector of the line and the normal vector of the triangle is smaller than zero. Examples -------- >>> triangle = [1.0, 0, 0], [-1.0, 0, 0], [0, 0, 1.0] >>> line = [-1, 1, 0], [-0.5, 0.5, 0] >>> reflect_line_triangle(line, triangle) ([0.0, 0.0, 0.0], [1.0, 1.0, 0.0]) """ x = intersection_line_triangle(line, triangle, tol=tol) if not x: return a, b = line t1, t2, t3 = triangle ab = subtract_vectors(b, a) n = cross_vectors(subtract_vectors(t2, t1), subtract_vectors(t3, t1)) if dot_vectors(ab, n) > 0: # the line does not point towards the front of the triangle return mirror = x, add_vectors(x, n) return x, mirror_point_line(a, mirror)
def intersection_line_triangle(line, triangle, epsilon=1e-6): """ Computes the intersection point of a line (ray) and a triangle based on the Moeller Trumbore intersection algorithm Parameters: line (tuple): Two points defining the line. triangle (sequence of sequence of float): XYZ coordinates of the triangle corners. Returns: point (tuple) if the line (ray) intersects with the triangle, None otherwise. Note: The line is treated as continues, directed ray and not as line segment with a start and end point """ a, b, c = triangle v1 = subtract_vectors(line[1], line[0]) p1 = line[0] # Find vectors for two edges sharing V1 e1 = subtract_vectors(b, a) e2 = subtract_vectors(c, a) # Begin calculating determinant - also used to calculate u parameter p = cross_vectors(v1, e2) # if determinant is near zero, ray lies in plane of triangle det = dot_vectors(e1, p) # NOT CULLING if (det > -epsilon and det < epsilon): return None inv_det = 1.0 / det # calculate distance from V1 to ray origin t = subtract_vectors(p1, a) # Calculate u parameter and make_blocks bound u = dot_vectors(t, p) * inv_det # The intersection lies outside of the triangle if (u < 0.0 or u > 1.0): return None # Prepare to make_blocks v parameter q = cross_vectors(t, e1) # Calculate V parameter and make_blocks bound v = dot_vectors(v1, q) * inv_det # The intersection lies outside of the triangle if (v < 0.0 or u + v > 1.0): return None t = dot_vectors(e2, q) * inv_det if t > epsilon: return add_vectors(p1, scale_vector(v1, t)) # No hit return None
def distance_point_point_sqrd(a, b): """Compute the squared distance bewteen points a and b. Parameters ---------- a : sequence of float XYZ coordinates of point a. b : sequence of float XYZ coordinates of point b. Returns ------- d2 : float Squared distance bewteen a and b. Examples -------- >>> distance_point_point_sqrd([0.0, 0.0, 0.0], [2.0, 0.0, 0.0]) 4.0 See Also -------- distance_point_point_sqrd_xy """ ab = subtract_vectors(b, a) return length_vector_sqrd(ab)
def represent_point_in_local_coordinates(self, point): """Represents a point in the frame's local coordinate system. Parameters ---------- point : :obj:`list` of :obj:`float` or :class:`Point` A point in world XY. Returns ------- :class:`Point` A point in the local coordinate system of the frame. Examples -------- >>> from compas.geometry import Frame >>> f = Frame([1, 1, 1], [0.68, 0.68, 0.27], [-0.67, 0.73, -0.15]) >>> pw1 = [2, 2, 2] >>> pf = f.represent_point_in_local_coordinates(pw1) >>> pw2 = f.represent_point_in_global_coordinates(pf) >>> allclose(pw1, pw2) True """ pt = Point(*subtract_vectors(point, self.point)) T = inverse(matrix_from_basis_vectors(self.xaxis, self.yaxis)) pt.transform(T) return pt
def distance_point_point(a, b): """Compute the distance bewteen a and b. Parameters ---------- a : sequence of float XYZ coordinates of point a. b : sequence of float XYZ coordinates of point b. Returns ------- d : float Distance bewteen a and b. Examples -------- >>> distance_point_point([0.0, 0.0, 0.0], [2.0, 0.0, 0.0]) 2.0 See Also -------- distance_point_point_xy """ ab = subtract_vectors(b, a) return length_vector(ab)
def centroid_polygon_edges(polygon): """Compute the centroid of the edges of a polygon. Parameters ---------- polygon : list of point A sequence of polygon point coordinates. Returns ------- list The XYZ coordinates of the centroid. Notes ----- The centroid of the edges is the centroid of the midpoints of the edges, with each midpoint weighted by the length of the corresponding edge proportional to the total length of the boundary. """ L = 0 cx = 0 cy = 0 cz = 0 p = len(polygon) for i in range(-1, p - 1): p1 = polygon[i] p2 = polygon[i + 1] d = length_vector(subtract_vectors(p2, p1)) cx += 0.5 * d * (p1[0] + p2[0]) cy += 0.5 * d * (p1[1] + p2[1]) cz += 0.5 * d * (p1[2] + p2[2]) L += d return [cx / L, cy / L, cz / L]
def mirror_vector_vector(v1, v2): """Mirrors vector about vector. Parameters ---------- v1 : list of float The vector. v2 : list of float The normalized vector as mirror axis Returns ------- list of float The mirrored vector. Notes ----- For more info, see [1]_. References ---------- .. [1] Math Stack Exchange. *How to get a reflection vector?* Available at: https://math.stackexchange.com/questions/13261/how-to-get-a-reflection-vector. """ return subtract_vectors(v1, scale_vector(v2, 2 * dot_vectors(v1, v2)))
def is_intersection_line_plane(line, plane, tol=1e-6): """Determine if a line (ray) intersects with a plane. Parameters ---------- line : tuple Two points defining the line. plane : tuple The base point and normal defining the plane. tol : float, optional A tolerance for intersection verification. Default is ``1e-6``. Returns ------- bool ``True`` if the line intersects with the plane. ``False`` otherwise. """ pt1 = line[0] pt2 = line[1] p_norm = plane[1] v1 = subtract_vectors(pt2, pt1) dot = dot_vectors(p_norm, v1) if fabs(dot) > tol: return True return False
def distance_line_line(l1, l2, tol=0.0): """Compute the shortest distance between two lines. Parameters ---------- l1 : tuple Two points defining a line. l2 : tuple Two points defining a line. Returns ------- d : float The distance between the two lines. Notes ----- The distance is the absolute value of the dot product of a unit vector that is perpendicular to the two lines, and the vector between two points on the lines ([1]_, [2]_). If each of the lines is defined by two points (:math:`l_1 = (\mathbf{x_1}, \mathbf{x_2})`, :math:`l_2 = (\mathbf{x_3}, \mathbf{x_4})`), then the unit vector that is perpendicular to both lines is... References ---------- .. [1] Weisstein, E.W. *Line-line Distance*. Available at: http://mathworld.wolfram.com/Line-LineDistance.html. .. [2] Wikipedia. *Skew lines Distance*. Available at: https://en.wikipedia.org/wiki/Skew_lines#Distance. Examples -------- >>> """ a, b = l1 c, d = l2 ab = subtract_vectors(b, a) cd = subtract_vectors(d, c) ac = subtract_vectors(c, a) n = cross_vectors(ab, cd) l = length_vector(n) if l <= tol: return distance_point_point(closest_point_on_line(l1[0], l2), l1[0]) n = scale_vector(n, 1.0 / l) return fabs(dot_vectors(n, ac))
def orient_points(points, reference_plane=None, target_plane=None): """Orient points from one plane to another. Parameters: points (sequence of sequence of float): XYZ coordinates of the points. reference_plane (tuple): Base point and normal defining a reference plane. target_plane (tuple): Base point and normal defining a target plane. Returns: points (sequence of sequence of float): XYZ coordinates of the oriented points. Note: This function is useful to orient a planar problem in the xy-plane to simplify the calculation (see example). Examples: .. code-block:: python from compas.geometry.spatial import orient_points from compas.geometry.planar import intersection_segment_segment_2d reference_plane = [(0.57735,0.57735,0.57735),(1.0, 1.0, 1.0)] line_a = [ (0.288675,0.288675,1.1547), (0.866025,0.866025, 0.) ] line_b = [ (1.07735,0.0773503,0.57735), (0.0773503,1.07735,0.57735) ] # orient lines to lie in the xy-plane line_a_xy = orient_points(line_a, reference_plane) line_b_xy = orient_points(line_b, reference_plane) # compute intersection in 2d in the xy-plane intx_point_xy = intersection_segment_segment_2d(line_a_xy, line_b_xy) # re-orient resulting intersection point to lie in the reference plane intx_point = orient_points([intx_point_xy], target_plane=reference_plane)[0] print(intx_point) """ if not target_plane: target_plane = [(0., 0., 0.,), (0., 0., 1.)] if not reference_plane: reference_plane = [(0., 0., 0.,), (0., 0., 1.)] vec_rot = cross_vectors(reference_plane[1], target_plane[1]) angle = angle_smallest_vectors(reference_plane[1], target_plane[1]) points = rotate_points(points, vec_rot, angle, reference_plane[0]) vec_trans = subtract_vectors(target_plane[0], reference_plane[0]) points = translate_points(points, vec_trans) return points
def check_length_sol_one(solution, pt_mean, pt, b1, b2, b1_key, b2_key, b_struct): vec_sol = solution dpp = dropped_perpendicular_points(pt, add_vectors(pt, vec_sol), b1["axis_endpoints"][0], b1["axis_endpoints"][1]) pt_x1 = dpp[0] pt_1 = dpp[1] dpp = dropped_perpendicular_points(pt, add_vectors(pt, vec_sol), b2["axis_endpoints"][0], b2["axis_endpoints"][1]) pt_x2 = dpp[0] pt_2 = dpp[1] if pt_x1 == None: return None if pt_x2 == None: return None pts_all_b1 = [] b_vert_n = b_struct.vertex_neighbors(b1_key) pts_all_b1.append(b_struct.vertex[b1_key]["axis_endpoints"][0]) pts_all_b1.append(b_struct.vertex[b1_key]["axis_endpoints"][1]) for n in b_vert_n: pts_all_b1.append( dropped_perpendicular_points( b1["axis_endpoints"][0], b1["axis_endpoints"][1], b_struct.vertex[n]["axis_endpoints"][0], b_struct.vertex[n]["axis_endpoints"][1])[0]) pts_all_b1.append(pt_1) pts_b1 = find_points_extreme(pts_all_b1, b1["axis_endpoints"]) pts_all_b2 = [] b_vert_n = b_struct.vertex_neighbors(b2_key) pts_all_b2.append(b_struct.vertex[b2_key]["axis_endpoints"][0]) pts_all_b2.append(b_struct.vertex[b2_key]["axis_endpoints"][1]) for n in b_vert_n: pts_all_b2.append( dropped_perpendicular_points( b2["axis_endpoints"][0], b2["axis_endpoints"][1], b_struct.vertex[n]["axis_endpoints"][0], b_struct.vertex[n]["axis_endpoints"][1])[0]) pts_all_b2.append(pt_2) pts_b2 = find_points_extreme(pts_all_b2, b2["axis_endpoints"]) vec_test_dir_1 = subtract_vectors(pt_mean, pt) if not check_dir(vec_sol, vec_test_dir_1): vec_sol = scale_vector(vec_sol, -1) lx1 = distance_point_point(pt, pt_x1) lx2 = distance_point_point(pt, pt_x2) l_max = lx1 if lx1 > lx2 else lx2 sol = [vec_sol, l_max, pts_b1, pts_b2] return sol
def orient_points(points, reference_plane, target_plane): """Orient points from one plane to another. Parameters ---------- points : list of points XYZ coordinates of the points. reference_plane : plane Base point and normal defining a reference plane. target_plane : plane Base point and normal defining a target plane. Returns ------- points : list of point XYZ coordinates of the oriented points. Notes ----- This function is useful to orient a planar problem in the xy-plane to simplify the calculation (see example). Examples -------- >>> from compas.geometry import orient_points >>> from compas.geometry import intersection_segment_segment_xy >>> >>> refplane = ([0.57735, 0.57735, 0.57735], [1.0, 1.0, 1.0]) >>> tarplane = ([0.0, 0.0, 0.0], [0.0, 0.0, 1.0]) >>> >>> points = [\ [0.288675, 0.288675, 1.1547],\ [0.866025, 0.866025, 0.0],\ [1.077350, 0.077350, 0.57735],\ [0.077350, 1.077350, 0.57735]\ ] >>> >>> points = orient_points(points, refplane, tarplane) >>> >>> ab = points[0], points[1] >>> cd = points[2], points[3] >>> >>> point = intersection_segment_segment_xy(ab, cd) >>> >>> points = orient_points([point], tarplane, refplane) >>> Point(*points[0]) Point(0.577, 0.577, 0.577) """ axis = cross_vectors(reference_plane[1], target_plane[1]) angle = angle_vectors(reference_plane[1], target_plane[1]) origin = reference_plane[0] if angle: points = rotate_points(points, angle, axis, origin) vector = subtract_vectors(target_plane[0], reference_plane[0]) points = translate_points(points, vector) return points
def mirror_point_point(point, mirror): """Mirror a point about a point. Parameters: point (sequence of float): XYZ coordinates of the point to mirror. mirror (sequence of float): XYZ coordinates of the mirror point. """ return add_vectors(mirror, subtract_vectors(mirror, point))
def intersection_line_line(l1, l2, tol=1e-6): """Computes the intersection of two lines. Parameters ---------- l1 : tuple, list XYZ coordinates of two points defining the first line. l2 : tuple, list XYZ coordinates of two points defining the second line. tol : float, optional A tolerance for membership verification. Default is ``1e-6``. Returns ------- list XYZ coordinates of the two points marking the shortest distance between the lines. If the lines intersect, these two points are identical. If the lines are skewed and thus only have an apparent intersection, the two points are different. If the lines are parallel, the return value is [None, None]. Examples -------- >>> """ a, b = l1 c, d = l2 ab = subtract_vectors(b, a) cd = subtract_vectors(d, c) n = cross_vectors(ab, cd) n1 = normalize_vector(cross_vectors(ab, n)) n2 = normalize_vector(cross_vectors(cd, n)) plane_1 = (a, n1) plane_2 = (c, n2) i1 = intersection_line_plane(l1, plane_2, tol=tol) i2 = intersection_line_plane(l2, plane_1, tol=tol) return i1, i2
def intersection_segment_plane(segment, plane, tol=1e-6): """Computes the intersection point of a line segment and a plane Parameters ---------- segment : tuple Two points defining the line segment. plane : tuple The base point and normal defining the plane. tol : float, optional A tolerance for membership verification. Default is ``1e-6``. Returns ------- point : tuple if the line segment intersects with the plane, None otherwise. """ a, b = segment o, n = plane ab = subtract_vectors(b, a) cosa = dot_vectors(n, ab) if fabs(cosa) <= tol: # if the dot product (cosine of the angle between segment and plane) # is close to zero the line and the normal are almost perpendicular # hence there is no intersection return None # based on the ratio = -dot_vectors(n, ab) / dot_vectors(n, oa) # there are three scenarios # 1) 0.0 < ratio < 1.0: the intersection is between a and b # 2) ratio < 0.0: the intersection is on the other side of a # 3) ratio > 1.0: the intersection is on the other side of b oa = subtract_vectors(a, o) ratio = -dot_vectors(n, oa) / cosa if 0.0 <= ratio and ratio <= 1.0: ab = scale_vector(ab, ratio) return add_vectors(a, ab) return None
def volume_polyhedron(polyhedron): r"""Compute the volume of a polyhedron represented by a closed mesh. Notes ----- This implementation is based on the divergence theorem, the fact that the *area vector* is constant for each face, and the fact that the area of each face can be computed as half the length of the cross product of two adjacent edge vectors [1]_. .. math:: :nowrap: \begin{align} V = \int_{P} 1 &= \frac{1}{3} \int_{\partial P} \mathbf{x} \cdot \mathbf{n} \\ &= \frac{1}{3} \sum_{i=0}^{N-1} \int{A_{i}} a_{i} \cdot n_{i} \\ &= \frac{1}{6} \sum_{i=0}^{N-1} a_{i} \cdot \hat n_{i} \end{align} References ---------- .. [1] Nurnberg, R. *Calculating the area and centroid of a polygon in 2d*. Available at: http://wwwf.imperial.ac.uk/~rn/centroid.pdf """ V = 0 for fkey in polyhedron.face: vertices = polyhedron.face_vertices(fkey, ordered=True) if len(vertices) == 3: faces = [vertices] else: faces = [] for i in range(1, len(vertices) - 1): faces.append(vertices[0:1] + vertices[i:i + 2]) for face in faces: a = polyhedron.vertex_coordinates(face[0]) b = polyhedron.vertex_coordinates(face[1]) c = polyhedron.vertex_coordinates(face[2]) ab = subtract_vectors(b, a) ac = subtract_vectors(c, a) n = cross_vectors(ab, ac) V += dot_vectors(a, n) return V / 6.
def distance_point_plane(point, plane): r"""Compute the distance from a point to a plane defined by three points. Parameters ---------- point : list Point coordinates. plane : tuple A point and a vector defining a plane. Returns ------- d : float Distance between point and plane. Notes ----- The distance from a pioint to a planbe can be computed from the coefficients of the equation of the plane and the coordinates of the point [1]_. The equation of a plane is .. math:: Ax + By + Cz + D = 0 where .. math:: :nowrap: \begin{align} D &= - Ax_0 - Bx_0 - Cz_0 \\ Q &= (x_0, y_0, z_0) \\ N &= (A, B, C) \end{align} with :math:`Q` a point on the plane, and :math:`N` the normal vector at that point. The distance of any point :math:`P` to a plane is the absolute value of the dot product of the vector from :math:`Q` to :math:`P` and the normal at :math:`Q`. References ---------- .. [1] Nykamp, D. *Distance from point to plane*. Available at: http://mathinsight.org/distance_point_plane. Examples -------- >>> """ base, normal = plane vector = subtract_vectors(point, base) return fabs(dot_vectors(vector, normal))
def project_point_line(point, line): """Project a point onto a line. Parameters: point (sequence of float): XYZ coordinates. line (tuple): Two points defining a line. Returns: list: XYZ coordinates of the projected point. References: https://en.wikibooks.org/wiki/Linear_Algebra/Orthogonal_Projection_Onto_a_Line """ a, b = line ab = subtract_vectors(b, a) ap = subtract_vectors(point, a) c = vector_component(ap, ab) return add_vectors(a, c)
def intersection_line_line(l1, l2): """Computes the intersection of two lines. Parameters ---------- l1 : tuple, list XYZ coordinates of two points defining the first line. l2 : tuple, list XYZ coordinates of two points defining the second line. Returns ------- list XYZ coordinates of the two points marking the shortest distance between the lines. If the lines intersect, these two points are identical. If the lines are skewed and thus only have an apparent intersection, the two points are different. If the lines are parallel, ... Examples -------- >>> """ a, b = l1 c, d = l2 ab = subtract_vectors(b, a) cd = subtract_vectors(d, c) n = cross_vectors(ab, cd) n1 = cross_vectors(ab, n) n2 = cross_vectors(cd, n) plane_1 = (a, n1) plane_2 = (c, n2) i1 = intersection_line_plane(l1, plane_2) i2 = intersection_line_plane(l2, plane_1) return [i1, i2]
def p_planes_tangent_to_cylinder( base_point, line_vect, ref_point, dist, ): """find tangent planes of a cylinder passing through a given point () .. image:: ../images/plane_tangent_to_one_cylinder.png :scale: 80 % :align: center Parameters ---------- base_point : point point M line_vect : vector direction of the existing bar's axis, direction [the other pt, base_pt], **direction very important!** ref_point : point point Q dist : float cylinder radius Returns ------- list of two [ref_point, local_y, local_x] local x = QB local_y // line_vect """ l_vect = normalize_vector(line_vect) tangent_pts = lines_tangent_to_cylinder(base_point, line_vect, ref_point, dist) if tangent_pts is None: return None base_pt, upper_tang_pt, lower_tang_pt = tangent_pts r1 = subtract_vectors(add_vectors(base_pt, upper_tang_pt), ref_point) r1 = normalize_vector(r1) r2 = subtract_vectors(add_vectors(base_pt, lower_tang_pt), ref_point) r2 = normalize_vector(r2) return [[ref_point, l_vect, r1], [ref_point, l_vect, r2]]
def project_point_plane(point, plane): """Project a point onto a plane. Parameters ---------- point : list of float XYZ coordinates of the point. plane : tuple Base point and normal vector defining the projection plane. Returns ------- list XYZ coordinates of the projected point. Notes ----- The projection is in the direction perpendicular to the plane. The projected point is thus the closest point on the plane to the original point [1]_. References ---------- .. [1] Math Stack Exchange. *Project a point in 3D on a given plane*. Available at: https://math.stackexchange.com/questions/444968/project-a-point-in-3d-on-a-given-plane. Examples -------- >>> from compas.geometry import project_point_plane >>> point = [3.0, 3.0, 3.0] >>> plane = ([0.0, 0.0, 0.0], [0.0, 0.0, 1.0]) # the XY plane >>> project_point_plane(point, plane) [3.0, 3.0, 0.0] """ base, normal = plane normal = normalize_vector(normal) vector = subtract_vectors(point, base) snormal = scale_vector(normal, dot_vectors(vector, normal)) return subtract_vectors(point, snormal)
def is_polygon_convex(polygon): """Determine if a polygon is convex. Parameters ---------- polygon : sequence of sequence of floats The XYZ coordinates of the corners of the polygon. Notes ----- Use this function for *spatial* polygons. If the polygon is in a horizontal plane, use :func:`is_polygon_convex_xy` instead. Returns ------- bool ``True`` if the polygon is convex. ``False`` otherwise. See Also -------- is_polygon_convex_xy """ c = centroid_polygon(polygon) for i in range(-1, len(polygon) - 1): p0 = polygon[i] p1 = polygon[i - 1] p2 = polygon[i + 1] v0 = subtract_vectors(c, p0) v1 = subtract_vectors(p1, p0) v2 = subtract_vectors(p2, p0) a1 = angle_vectors(v1, v0) a2 = angle_vectors(v0, v2) if a1 + a2 > pi: return False return True
def tangent_through_two_points(base_point1, line_vect1, ref_point1, base_point2, line_vect2, ref_point2, dist1, dist2): ind = [0, 1] sols_vec = [] sols_pts = [] # print("tangent_through_two_points", base_point1, line_vect1, ref_point1, base_point2, line_vect2, ref_point2, dist1, dist2) for i in ind: ret_p1 = p_planes_tangent_to_cylinder(base_point1, line_vect1, ref_point2, dist1 + dist2 + dist1 + dist2) ret1 = ret_p1[i] z_vec = cross_vectors(line_vect1, ret1[2]) plane1 = (ret1[0], z_vec) # print("plane1", plane1) pp1 = project_points_plane([ref_point1], plane1)[0] vec_move = scale_vector(subtract_vectors(ref_point1, pp1), 0.5) pt1 = add_vectors(pp1, vec_move) for j in ind: ret_p2 = p_planes_tangent_to_cylinder( base_point2, line_vect2, ref_point1, dist1 + dist2 + dist1 + dist2) ret2 = ret_p2[j] z_vec = cross_vectors(line_vect2, ret2[2]) plane2 = (ret2[0], z_vec) pp2 = project_points_plane([ref_point2], plane2)[0] vec_move = scale_vector(subtract_vectors(ref_point2, pp2), 0.5) pt2 = add_vectors(pp2, vec_move) sols_pts.append([pt1, pt2]) sol_vec = subtract_vectors(pt1, pt2) sols_vec.append(sol_vec) return sols_pts
def distance_point_line(point, line): """Compute the distance between a point and a line. Parameters ---------- point : list, tuple Point location. line : list, tuple Line defined by two points. Returns ------- d : float The distance between the point and the line. Notes ----- This implementation computes the *right angle distance* from a point P to a line defined by points A and B as twice the area of the triangle ABP divided by the length of AB [1]_. References ---------- .. [1] Wikipedia. *Distance from a point to a line*. Available at: https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line Examples -------- >>> """ a, b = line ab = subtract_vectors(b, a) pa = subtract_vectors(a, point) pb = subtract_vectors(b, point) l = length_vector(cross_vectors(pa, pb)) l_ab = length_vector(ab) return l / l_ab
def mirror_vector_vector(v1, v2): """Mirrors vector about vector. Parameters: v1 (tuple, list, Vector): The vector. v2 (tuple, list, Vector): The normalized vector as mirror axis Returns: Tuple: mirrored vector Resources: http://math.stackexchange.com/questions/13261/how-to-get-a-reflection-vector """ return subtract_vectors(v1, scale_vector(v2, 2 * dot_vectors(v1, v2)))
def angles_points(a, b, c, deg=False): r"""Compute the two angles between two vectors defined by three points. Parameters ---------- a : sequence of float) XYZ coordinates. b : sequence of float) XYZ coordinates. c : sequence of float) XYZ coordinates. deg : boolean returns angles in degrees if True Returns ------- tuple The smallest angle between the vectors in radians (in degrees if deg == True). The smallest angle is returned first. Notes ----- The vectors are defined in the following way .. math:: \mathbf{u} = \mathbf{b} - \mathbf{a} \\ \mathbf{v} = \mathbf{c} - \mathbf{a} Examples -------- >>> """ u = subtract_vectors(b, a) v = subtract_vectors(c, a) return angles_vectors(u, v, deg)
def is_coplanar(points, tol=0.01): """Determine if the points are coplanar. Parameters ---------- points : sequence A sequence of locations in three-dimensional space. tol : float, optional A tolerance for planarity validation. Default is ``0.01``. Returns ------- bool ``True`` if the points are coplanar. ``False`` otherwise. Notes ----- Compute the normal vector (cross product) of the vectors formed by the first three points. Include one more vector at a time to compute a new normal and compare with the original normal. If their cross product is not zero, they are not parallel, which means the point are not in the same plane. Four points are coplanar if the volume of the tetrahedron defined by them is 0. Coplanarity is equivalent to the statement that the pair of lines determined by the four points are not skew, and can be equivalently stated in vector form as (x2 - x0).[(x1 - x0) x (x3 - x2)] = 0. """ tol2 = tol**2 if len(points) == 4: v01 = subtract_vectors(points[1], points[0]) v02 = subtract_vectors(points[2], points[0]) v23 = subtract_vectors(points[3], points[0]) res = dot_vectors(v02, cross_vectors(v01, v23)) return res**2 < tol2 # len(points) > 4 # compare length of cross product vector to tolerance a, b, c = sample(points, 3) u = subtract_vectors(b, a) v = subtract_vectors(c, a) w = cross_vectors(u, v) for i in range(0, len(points) - 2): u = v v = subtract_vectors(points[i + 2], points[i + 1]) wuv = cross_vectors(w, cross_vectors(u, v)) if wuv[0]**2 > tol2 or wuv[1]**2 > tol2 or wuv[2]**2 > tol2: return False return True
def angles_points(a, b, c): r"""Compute the two angles (radians) define by three points. Parameters ---------- a : sequence of float) XYZ coordinates. b : sequence of float) XYZ coordinates. c : sequence of float) XYZ coordinates. Returns ------- tuple The two angles in radians. The smallest angle is returned first. Notes ----- The vectors are defined in the following way .. math:: \mathbf{u} = \mathbf{b} - \mathbf{a} \\ \mathbf{v} = \mathbf{c} - \mathbf{a} Z components may be provided, but are simply ignored. Examples -------- >>> """ u = subtract_vectors(b, a) v = subtract_vectors(c, a) return angles_vectors(u, v)