def circle_from_points(a, b, c): """Construct a circle from three points. Parameters ---------- a : [float, float, float] | :class:`compas.geometry.Point` XYZ coordinates. b : [float, float, float] | :class:`compas.geometry.Point` XYZ coordinates. c : [float, float, float] | :class:`compas.geometry.Point` XYZ coordinates. Returns ------- [float, float, float] Center of the circle. float Radius of the circle. [float, float, float] Normal of the plane containing the circle. Notes ----- For more information, see [1]_. References ---------- .. [1] Wikipedia. *Circumscribed circle*. Available at: https://en.wikipedia.org/wiki/Circumscribed_circle. Examples -------- >>> """ ab = subtract_vectors(b, a) cb = subtract_vectors(b, c) ba = subtract_vectors(a, b) ca = subtract_vectors(a, c) ac = subtract_vectors(c, a) bc = subtract_vectors(c, b) normal = normalize_vector(cross_vectors(ab, ac)) d = 2 * length_vector_sqrd(cross_vectors(ba, cb)) A = length_vector_sqrd(cb) * dot_vectors(ba, ca) / d B = length_vector_sqrd(ca) * dot_vectors(ab, cb) / d C = length_vector_sqrd(ba) * dot_vectors(ac, bc) / d Aa = scale_vector(a, A) Bb = scale_vector(b, B) Cc = scale_vector(c, C) center = sum_vectors([Aa, Bb, Cc]) radius = length_vector(subtract_vectors(a, center)) return center, radius, normal
def centroid_points_weighted(points, weights): """Compute the weighted centroid of a set of points. The weights can be any between minus and plus infinity. Parameters ---------- points : sequence[[float, float, float] | :class:`compas.geometry.Point`] A list of point coordinates. weights : sequence[float] A list of weight floats. Returns ------- [float, float, float] The coordinates of the weighted centroid. """ vectors = [scale_vector(point, weight) for point, weight in zip(points, weights)] vector = scale_vector(sum_vectors(vectors), 1. / sum(weights)) return vector
def weighted_centroid_points(points, weights): """Compute the weighted centroid of a set of points. The weights can be any between minus and plus infinity. Parameters ---------- points : list A list of point coordinates. weights : list A list of weight floats. Returns ------- list The coordinates of the weighted centroid. """ vectors = [ scale_vector(point, weight) for point, weight in zip(points, weights) ] vector = scale_vector(sum_vectors(vectors), 1. / sum(weights)) return vector
def distance_line_line(l1, l2, tol=0.0): r"""Compute the shortest distance between two lines. Parameters ---------- l1 : tuple Two points defining a line. l2 : tuple Two points defining a line. Returns ------- 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) length = length_vector(n) if length <= tol: return distance_point_point(closest_point_on_line(l1[0], l2), l1[0]) n = scale_vector(n, 1.0 / length) return fabs(dot_vectors(n, ac))
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 intersection_sphere_sphere(sphere1, sphere2): """Computes the intersection of 2 spheres. There are 4 cases of sphere-sphere intersection : 1) the spheres intersect in a circle, 2) they intersect in a point, 3) they overlap, 4) they do not intersect. Parameters ---------- sphere1 : tuple center, radius of the sphere. sphere2 : tuple center, radius of the sphere. Returns ------- case : str `point`, `circle`, or `sphere` result : tuple - point: xyz coordinates - circle: center, radius, normal - sphere: center, radius Examples -------- >>> sphere1 = (3.0, 7.0, 4.0), 10.0 >>> sphere2 = (7.0, 4.0, 0.0), 5.0 >>> result = intersection_sphere_sphere(sphere1, sphere2) >>> if result: ... case, res = result ... if case == "circle": ... center, radius, normal = res ... elif case == "point": ... point = res ... elif case == "sphere": ... center, radius = res References -------- https://gamedev.stackexchange.com/questions/75756/sphere-sphere-intersection-and-circle-sphere-intersection """ center1, radius1 = sphere1 center2, radius2 = sphere2 distance = distance_point_point(center1, center2) # Case 4: No intersection if radius1 + radius2 < distance: return None # Case 4: No intersection, sphere is within the other sphere elif distance + min(radius1, radius2) < max(radius1, radius2): return None # Case 3: sphere's overlap elif radius1 == radius2 and distance == 0: return "sphere", sphere1 # Case 2: point intersection elif radius1 + radius2 == distance: ipt = subtract_vectors(center2, center1) ipt = scale_vector(ipt, radius1/distance) ipt = add_vectors(center1, ipt) return "point", ipt # Case 2: point intersection, smaller sphere is within the bigger elif distance + min(radius1, radius2) == max(radius1, radius2): if radius1 > radius2: ipt = subtract_vectors(center2, center1) ipt = scale_vector(ipt, radius1/distance) ipt = add_vectors(center1, ipt) else: ipt = subtract_vectors(center1, center2) ipt = scale_vector(ipt, radius2/distance) ipt = add_vectors(center2, ipt) return "point", ipt # Case 1: circle intersection h = 0.5 + (radius1**2 - radius2**2)/(2 * distance**2) ci = subtract_vectors(center2, center1) ci = scale_vector(ci, h) ci = add_vectors(center1, ci) ri = sqrt(radius1**2 - h**2 * distance**2) normal = scale_vector(subtract_vectors(center2, center1), 1/distance) return "circle", (ci, ri, normal)