def intersection_plane_plane(plane1, plane2, tol=1e-6): """Computes the intersection of two planes Parameters ---------- plane1 : tuple The base point and normal (normalized) defining the 1st plane. plane2 : tuple The base point and normal (normalized) defining the 2nd plane. tol : float, optional A tolerance for membership verification. Default is ``1e-6``. Returns ------- line : tuple Two points defining the intersection line. None if planes are parallel. """ o1, n1 = plane1 o2, n2 = plane2 if fabs(dot_vectors(n1, n2)) >= 1 - tol: return None # direction of intersection line d = cross_vectors(n1, n2) # vector in plane 1 perpendicular to the direction of the intersection line v1 = cross_vectors(d, n1) # point on plane 1 p1 = add_vectors(o1, v1) x1 = intersection_line_plane((o1, p1), plane2, tol=tol) x2 = add_vectors(x1, d) return x1, x2
def closest_point_on_line(point, line): """Computes closest point on line to a given point. Parameters ---------- point : sequence of float XYZ coordinates. line : tuple Two points defining the line. Returns ------- list XYZ coordinates of closest point. Examples -------- >>> See Also -------- :func:`basic.transformations.project_point_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 closest_point_on_line(point, line): """Computes closest point on line to a given point. Parameters ---------- point : [float, float, float] | :class:`compas.geometry.Point` XYZ coordinates. line : [point, point] | :class:`compas.geometry.Line` Two points defining the line. Returns ------- [float, float, float] XYZ coordinates of closest point. Examples -------- >>> See Also -------- :func:`basic.transformations.project_point_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_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)
def centroid_polyhedron(polyhedron): """Compute the center of mass of a polyhedron. Parameters ---------- polyhedron : tuple The coordinates of the vertices, and the indices of the vertices forming the faces. Returns ------- list XYZ coordinates of the center of mass. Warning ------- This function assumes that the vertex cycles of the faces are such that the face normals are consistently pointing outwards, resulting in a *positive* polyhedron. Examples -------- >>> from compas.geometry._core import Polyhedron >>> p = Polyhedron.generate(6) >>> centroid_polyhedron(p) [0.0, 0.0, 0.0] """ 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: centroid = centroid_points([vertices[index] for index in face]) w = len(vertices) vertices.append(centroid) triangles = [[w, u, v] for u, v in pairwise(face + face[0:1])] 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) ab = add_vectors(a, b) bc = add_vectors(b, c) ca = add_vectors(c, a) ab_x2 = dot_vectors(ab, ex)**2 bc_x2 = dot_vectors(bc, ex)**2 ca_x2 = dot_vectors(ca, ex)**2 x += nx * (ab_x2 + bc_x2 + ca_x2) ab_y2 = dot_vectors(ab, ey)**2 bc_y2 = dot_vectors(bc, ey)**2 ca_y2 = dot_vectors(ca, ey)**2 y += ny * (ab_y2 + bc_y2 + ca_y2) ab_z2 = dot_vectors(ab, ez)**2 bc_z2 = dot_vectors(bc, ez)**2 ca_z2 = dot_vectors(ca, ez)**2 z += nz * (ab_z2 + bc_z2 + ca_z2) # 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 V = V / 6.0 if V < 1e-9: d = 1.0 / (2 * 24) else: d = 1.0 / (2 * 24 * V) x *= d y *= d z *= d return [x, y, z]