def project_point_line_xy(point, line): """Project a point onto a line in the XY plane. Parameters ---------- point : [float, float, float] | :class:`compas.geometry.Point` XY(Z) coordinates of the point. line : [point, point] | :class:`compas.geometry.Line` Two points defining the projection line. Returns ------- [float, float, float] XYZ coordinates of the projected point, with Z=0. Notes ----- For more info, see [1]_. References ---------- .. [1] Wiki Books. *Linear Algebra/Orthogonal Projection Onto a Line*. Available at: https://en.wikibooks.org/wiki/Linear_Algebra/Orthogonal_Projection_Onto_a_Line. """ a, b = line ab = subtract_vectors_xy(b, a) ap = subtract_vectors_xy(point, a) c = vector_component_xy(ap, ab) return add_vectors_xy(a, c)
def mirror_point_point_xy(point, mirror): """Mirror a point about a point. Parameters ---------- point : [float, float, float] | :class:`compas.geometry.Point` XY(Z) coordinates of the point to mirror. mirror : [float, float, float] | :class:`compas.geometry.Point` XY(Z) coordinates of the mirror point. Returns ------- [float, float, float] The mirrored point, with Z=0. """ return add_vectors_xy(mirror, subtract_vectors_xy(mirror, point))
def mirror_point_point_xy(point, mirror): """Mirror a point about a point. Parameters ---------- point : list of float XY(Z) coordinates of the point to mirror. mirror : list of float XY(Z) coordinates of the mirror point. Returns ------- list of float The mirrored point, with Z=0. """ return add_vectors_xy(mirror, subtract_vectors_xy(mirror, point))
def mirror_point_line_xy(point, line): """Mirror a point about a line. Parameters ---------- point : [float, float, float] | :class:`compas.geometry.Point` XY(Z) coordinates of the point to mirror. line : [point, point] | :class:`compas.geometry.Line` Two points defining the line. XY(Z) coordinates of the two points defining the mirror line. Returns ------- [float, float, float] The mirrored point, with Z=0. """ closest = closest_point_on_line_xy(point, line) return add_vectors_xy(closest, subtract_vectors_xy(closest, point))
def mirror_point_line_xy(point, line): """Mirror a point about a line. Parameters ---------- point : list of float XY(Z) coordinates of the point to mirror. line : tuple Two points defining the line. XY(Z) coordinates of the two points defining the mirror line. Returns ------- list of float The mirrored point, with Z=0. """ closest = closest_point_on_line_xy(point, line) return add_vectors_xy(closest, subtract_vectors_xy(closest, point))
def intersection_circle_circle_xy(circle1, circle2): """Calculates the intersection points of two circles in 2d lying in the XY plane. Parameters ---------- circle1 : tuple center, radius of the first circle in the xy plane. circle2 : tuple center, radius of the second circle in the xy plane. Returns ------- points : list of tuples the intersection points if there are any None if there are no intersection points """ p1, r1 = circle1[0], circle1[1] p2, r2 = circle2[0], circle2[1] d = length_vector_xy(subtract_vectors_xy(p2, p1)) if d > r1 + r2: return None if d < fabs(r1 - r2): return None if (d == 0) and (r1 == r2): return None a = (r1 * r1 - r2 * r2 + d * d) / (2 * d) h = (r1 * r1 - a * a)**0.5 cx2 = p1[0] + a * (p2[0] - p1[0]) / d cy2 = p1[1] + a * (p2[1] - p1[1]) / d i1 = ((cx2 + h * (p2[1] - p1[1]) / d), (cy2 - h * (p2[0] - p1[0]) / d), 0) i2 = ((cx2 - h * (p2[1] - p1[1]) / d), (cy2 + h * (p2[0] - p1[0]) / d), 0) return i1, i2
def intersection_circle_circle_xy(circle1, circle2): """Calculates the intersection points of two circles in 2d lying in the XY plane. Parameters ---------- circle1 : [plane, float] | :class:`compas.geometry.Circle` Circle defined by a point, with at least XY coordinates, and a radius. circle2 : [plane, float] | :class:`compas.geometry.Circle` Circle defined by a point, with at least XY coordinates, and a radius. Returns ------- tuple[[float, float, float], [float, float, float]] | None The intersection points if there are any. If the circles are tangent to each other, the two intersection points are identical. None otherwise. """ p1, r1 = circle1[0], circle1[1] p2, r2 = circle2[0], circle2[1] d = length_vector_xy(subtract_vectors_xy(p2, p1)) if d > r1 + r2: return None if d < fabs(r1 - r2): return None if (d == 0) and (r1 == r2): return None a = (r1 * r1 - r2 * r2 + d * d) / (2 * d) h = (r1 * r1 - a * a)**0.5 cx2 = p1[0] + a * (p2[0] - p1[0]) / d cy2 = p1[1] + a * (p2[1] - p1[1]) / d i1 = ((cx2 + h * (p2[1] - p1[1]) / d), (cy2 - h * (p2[0] - p1[0]) / d), 0) i2 = ((cx2 - h * (p2[1] - p1[1]) / d), (cy2 + h * (p2[0] - p1[0]) / d), 0) return i1, i2
def network_embed_in_plane(network, fixed=None, straightline=True): """Embed the network in the plane. Parameters ---------- network : Network A network object. fixed : list (None) Two fixed points. straightline : bool (True) Embed using straight lines. Returns ------- bool True if the embedding was successful. False otherwise. Raises ------ ImportError If NetworkX is not installed. Examples -------- >>> """ try: import networkx as nx except ImportError: print( "NetworkX is not installed. Get NetworkX at https://networkx.github.io/." ) raise x = network.nodes_attribute('x') y = network.nodes_attribute('y') xmin, xmax = min(x), max(x) ymin, ymax = min(y), max(y) xspan = xmax - xmin yspan = ymax - ymin edges = [(u, v) for u, v in network.edges() if not network.is_leaf(u) and not network.is_leaf(v)] is_embedded = False count = 100 while count: graph = nx.Graph(edges) pos = nx.spring_layout(graph, iterations=100, scale=max(xspan, yspan)) if not _are_edges_crossed(edges, pos): is_embedded = True break count -= 1 if not is_embedded: return False if fixed: a, b = fixed p0 = network.node_attributes(a, 'xy') p1 = network.node_attributes(b, 'xy') p2 = pos[b] vec0 = subtract_vectors_xy(p1, p0) vec1 = subtract_vectors_xy(pos[b], pos[a]) # rotate angle = angle_vectors_xy(vec0, vec1) if is_ccw_xy(p0, p1, p2): angle = 2 * pi - angle cosa = cos(angle) sina = sin(angle) for key in pos: x, y = pos[key] pos[key][0] = cosa * x - sina * y pos[key][1] = sina * x + cosa * y # scale l0 = (vec0[0]**2 + vec0[1]**2)**0.5 l1 = (vec1[0]**2 + vec1[1]**2)**0.5 scale = l0 / l1 for key in pos: pos[key][0] *= scale pos[key][1] *= scale # translate t = subtract_vectors_xy(p0, pos[a]) for key in pos: pos[key][0] += t[0] pos[key][1] += t[1] # update network node coordinates for key in network.nodes(): if key in pos: network.node_attributes(key, 'xy', pos[key]) return True
def add_feet(self, segments, feet=2): """""" def rotate(point, angle): x = cos(angle) * point[0] - sin(angle) * point[1] y = sin(angle) * point[0] + cos(angle) * point[1] return x, y, 0 def cross_z(ab, ac): return ab[0] * ac[1] - ab[1] * ac[0] scale = self.attributes['feet.scale'] alpha = self.attributes['feet.alpha'] * pi / 180 tol = self.attributes['feet.tol'] key_foot = {} key_xyz = { key: self.vertex_coordinates(key, 'xyz') for key in self.vertices() } for i, vertices in enumerate(segments): key = vertices[0] after = vertices[1] before = segments[i - 1][-2] b = key_xyz[before] o = key_xyz[key] a = key_xyz[after] ob = normalize_vector_xy(subtract_vectors_xy(b, o)) oa = normalize_vector_xy(subtract_vectors_xy(a, o)) z = cross_z(ob, oa) if z > +tol: r = normalize_vector_xy(add_vectors_xy(oa, ob)) r = [-scale * axis for axis in r] elif z < -tol: r = normalize_vector_xy(add_vectors_xy(oa, ob)) r = [+scale * axis for axis in r] else: ba = normalize_vector_xy(subtract_vectors_xy(a, b)) r = cross_vectors([0, 0, 1], ba) r = [+scale * axis for axis in r] if feet == 1: x, y, z = add_vectors_xy(o, r) m = self.add_vertex(x=x, y=y, z=o[2], is_fixed=True, is_external=True) key_foot[key] = m elif feet == 2: lx, ly, lz = add_vectors_xy(o, rotate(r, +alpha)) rx, ry, rz = add_vectors_xy(o, rotate(r, -alpha)) l = self.add_vertex(x=lx, y=ly, z=o[2], is_fixed=True, is_external=True) r = self.add_vertex(x=rx, y=ry, z=o[2], is_fixed=True, is_external=True) key_foot[key] = l, r else: pass for vertices in segments: l = vertices[0] r = vertices[-1] if feet == 1: lm = key_foot[l] rm = key_foot[r] self.add_face([lm] + vertices + [rm], is_loaded=False) self.set_edge_attribute((l, lm), 'is_external', True) self.set_edge_attribute((rm, lm), 'is_edge', False) elif feet == 2: lb = key_foot[l][0] la = key_foot[l][1] rb = key_foot[r][0] self.add_face([lb, l, la], is_loaded=False) self.add_face([la] + vertices + [rb], is_loaded=False) self.set_edge_attribute((l, lb), 'is_external', True) self.set_edge_attribute((l, la), 'is_external', True) self.set_edge_attribute((lb, la), 'is_edge', False) self.set_edge_attribute((la, rb), 'is_edge', False) else: pass