def vertex_area(self, key): """Compute the tributary area of a vertex. Parameters ---------- key : int The identifier of the vertex. Returns ------- float The tributary are. """ area = 0. p0 = self.vertex_coordinates(key) for nbr in self.halfedge[key]: p1 = self.vertex_coordinates(nbr) v1 = subtract_vectors(p1, p0) fkey = self.halfedge[key][nbr] if fkey is not None: p2 = self.face_centroid(fkey) v2 = subtract_vectors(p2, p0) area += length_vector(cross_vectors(v1, v2)) fkey = self.halfedge[nbr][key] if fkey is not None: p3 = self.face_centroid(fkey) v3 = subtract_vectors(p3, p0) area += length_vector(cross_vectors(v1, v3)) return 0.25 * area
def from_basis_vectors(cls, xaxis, yaxis): """Construct a rotation transformation from basis vectors (= orthonormal vectors). Parameters ---------- xaxis : [float, float, float] | :class:`compas.geometry.Vector` The x-axis of the frame. yaxis : [float, float, float] | :class:`compas.geometry.Vector` The y-axis of the frame. Returns ------- :class:`compas.geometry.Rotation` Examples -------- >>> xaxis = [0.68, 0.68, 0.27] >>> yaxis = [-0.67, 0.73, -0.15] >>> R = Rotation.from_basis_vectors(xaxis, yaxis) """ xaxis = normalize_vector(list(xaxis)) yaxis = normalize_vector(list(yaxis)) zaxis = cross_vectors(xaxis, yaxis) yaxis = cross_vectors(zaxis, xaxis) matrix = [[xaxis[0], yaxis[0], zaxis[0], 0], [xaxis[1], yaxis[1], zaxis[1], 0], [xaxis[2], yaxis[2], zaxis[2], 0], [0, 0, 0, 1]] R = cls() R.matrix = matrix return R
def calculate_selfweight(xyz): fkey_centroid = { fkey: mesh.face_centroid(fkey) for fkey in mesh.faces() } for u in mesh.vertices(): i = key_index[u] p0 = xyz[i] a = 0 for v in mesh.halfedge[u]: j = key_index[v] p1 = xyz[j] p01 = [p1[axis] - p0[axis] for axis in range(3)] fkey = mesh.halfedge[u][v] if fkey in fkey_centroid: p2 = fkey_centroid[fkey] p02 = [p2[axis] - p0[axis] for axis in range(3)] a += 0.25 * length_vector(cross_vectors(p01, p02)) fkey = mesh.halfedge[v][u] if fkey in fkey_centroid: p3 = fkey_centroid[fkey] p03 = [p3[axis] - p0[axis] for axis in range(3)] a += 0.25 * length_vector(cross_vectors(p01, p03)) sw[i] = a * ro[i] return sw
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 matrix_from_basis_vectors(xaxis, yaxis): """Creates a rotation matrix from basis vectors (= orthonormal vectors). Parameters ---------- xaxis : list of float The x-axis of the frame. yaxis : list of float The y-axis of the frame. Examples -------- >>> xaxis = [0.68, 0.68, 0.27] >>> yaxis = [-0.67, 0.73, -0.15] >>> R = matrix_from_basis_vectors(xaxis, yaxis) """ xaxis = normalize_vector(list(xaxis)) yaxis = normalize_vector(list(yaxis)) zaxis = cross_vectors(xaxis, yaxis) yaxis = cross_vectors(zaxis, xaxis) # correction R = identity_matrix(4) R[0][0], R[1][0], R[2][0] = xaxis R[0][1], R[1][1], R[2][1] = yaxis R[0][2], R[1][2], R[2][2] = zaxis return R
def from_basis_vectors(cls, xaxis, yaxis): """Creates a ``Rotation`` from basis vectors (= orthonormal vectors). Parameters ---------- xaxis : :class:`Vector` The x-axis of the frame. yaxis : :class:`Vector` The y-axis of the frame. Examples -------- >>> xaxis = [0.68, 0.68, 0.27] >>> yaxis = [-0.67, 0.73, -0.15] >>> R = Rotation.from_basis_vectors(xaxis, yaxis) """ xaxis = normalize_vector(list(xaxis)) yaxis = normalize_vector(list(yaxis)) zaxis = cross_vectors(xaxis, yaxis) yaxis = cross_vectors(zaxis, xaxis) # correction R = cls() R.matrix[0][0], R.matrix[1][0], R.matrix[2][0] = xaxis R.matrix[0][1], R.matrix[1][1], R.matrix[2][1] = yaxis R.matrix[0][2], R.matrix[1][2], R.matrix[2][2] = zaxis return R
def from_points(cls, point, point_xaxis, point_xyplane): """Constructs a frame from 3 points. Parameters ---------- point : point The origin of the frame. point_xaxis : point A point on the x-axis of the frame. point_xyplane : point A point within the xy-plane of the frame. Returns ------- :class:`compas.geometry.Frame` The constructed frame. Examples -------- >>> frame = Frame.from_points([0, 0, 0], [1, 0, 0], [0, 1, 0]) >>> frame.point Point(0.000, 0.000, 0.000) >>> frame.xaxis Vector(1.000, 0.000, 0.000) >>> frame.yaxis Vector(0.000, 1.000, 0.000) """ xaxis = subtract_vectors(point_xaxis, point) xyvec = subtract_vectors(point_xyplane, point) yaxis = cross_vectors(cross_vectors(xaxis, xyvec), xaxis) return cls(point, xaxis, yaxis)
def orthonormals_from_two_vectors(u, v): """Given two non-parallel vectors, creates a set of three orthonormal vectors. Parameters ---------- u : (sequence of float) – XYZ components of the first vector. v : (sequence of float) – XYZ components of the second vector. Returns ------- list The three orthonormal vectors. """ k = cross_vectors(u, v) # check if vectors are parallel if np.linalg.norm(k) == 0.0: sys.exit('Input vectors cannot be parallel.') k = k / np.linalg.norm(k) i = u / np.linalg.norm(u) j = cross_vectors(k, i) j = j / np.linalg.norm(j) return [i, j, k]
def _tributary_areas(self, xyz): mesh = self.mesh key_index = self.key_index fkey_index = self.fkey_index is_loaded = self.is_loaded C = self.F.dot(xyz) areas = zeros((xyz.shape[0], 1)) for u in mesh.vertices(): p0 = xyz[key_index[u]] a = 0 for v in mesh.halfedge[u]: p1 = xyz[key_index[v]] p01 = p1 - p0 fkey = mesh.halfedge[u][v] if fkey is not None and is_loaded[fkey]: p2 = C[fkey_index[fkey]] a += 0.25 * length_vector(cross_vectors(p01, p2 - p0)) fkey = mesh.halfedge[v][u] if fkey is not None and is_loaded[fkey]: p3 = C[fkey_index[fkey]] a += 0.25 * length_vector(cross_vectors(p01, p3 - p0)) areas[key_index[u]] = a return areas
def orthonormalize_axes(xaxis, yaxis): """Corrects xaxis and yaxis to be unit vectors and orthonormal. Parameters ---------- xaxis: :class:`Vector` or list of float yaxis: :class:`Vector` or list of float Returns ------- tuple: (xaxis, yaxis) The corrected axes. Raises ------ ValueError: If xaxis and yaxis cannot span a plane. Examples -------- >>> xaxis = [1, 4, 5] >>> yaxis = [1, 0, -2] >>> xaxis, yaxis = orthonormalize_axes(xaxis, yaxis) >>> allclose(xaxis, [0.1543, 0.6172, 0.7715], tol=0.001) True >>> allclose(yaxis, [0.6929, 0.4891, -0.5298], tol=0.001) True """ xaxis = normalize_vector(xaxis) yaxis = normalize_vector(yaxis) zaxis = cross_vectors(xaxis, yaxis) if not norm_vector(zaxis): raise ValueError("Xaxis and yaxis cannot span a plane.") yaxis = cross_vectors(normalize_vector(zaxis), xaxis) return xaxis, yaxis
def from_basis_vectors(cls, xaxis, yaxis): """Creates a ``Rotation`` from basis vectors (= orthonormal vectors). Parameters ---------- xaxis : compas.geometry.Vector or list The x-axis of the frame. yaxis : compas.geometry.Vector or list The y-axis of the frame. Examples -------- >>> xaxis = [0.68, 0.68, 0.27] >>> yaxis = [-0.67, 0.73, -0.15] >>> R = Rotation.from_basis_vectors(xaxis, yaxis) """ xaxis = normalize_vector(list(xaxis)) yaxis = normalize_vector(list(yaxis)) zaxis = cross_vectors(xaxis, yaxis) yaxis = cross_vectors(zaxis, xaxis) matrix = [[xaxis[0], yaxis[0], zaxis[0], 0], [xaxis[1], yaxis[1], zaxis[1], 0], [xaxis[2], yaxis[2], zaxis[2], 0], [0, 0, 0, 1]] R = cls() R.matrix = matrix return R
def _triangle_xform(triangle): o = triangle[0] u = triangle[1] - o v = triangle[2] - o w = cross_vectors(u, v) v = cross_vectors(w, u) A = normalizerow(array([u, v, w])).T return o, A
class LoadUpdater(object): """""" def __init__(self, mesh, p0, thickness=1.0, density=1.0, live=0.0): self.mesh = mesh self.p0 = p0 self.thickness = thickness self.density = density self.live = live self.key_index = mesh.key_index() self.fkey_index = {fkey: index for index, fkey in enumerate(mesh.faces())} self.is_loaded = {fkey: mesh.get_face_attribute(fkey, 'is_loaded') for fkey in mesh.faces()} self.F = self.face_matrix() def __call__(self, p, xyz): ta = self._tributary_areas(xyz) sw = ta * self.thickness * self.density + ta * self.live p[:, 2] = self.p0[:, 2] + sw[:, 0] def face_matrix(self): face_vertices = [None] * self.mesh.number_of_faces() for fkey in self.mesh.fes(): face_vertices[self.fkey_index[fkey]] = [self.key_index[key] for key in self.mesh.face_vertices(fkey)] return face_matrix(face_vertices, rtype='csr', normalize=True) def _tributary_areas(self, xyz):ac mesh = self.mesh key_index = self.key_index fkey_index = self.fkey_index is_loaded = self.is_loaded C = self.F.dot(xyz) areas = zeros((xyz.shape[0], 1)) for u in mesh.vertices(): p0 = xyz[key_index[u]] a = 0 for v in mesh.halfedge[u]: p1 = xyz[key_index[v]] p01 = p1 - p0 fkey = mesh.halfedge[u][v] if fkey is not None and is_loaded[fkey]: p2 = C[fkey_index[fkey]] a += 0.25 * length_vector(cross_vectors(p01, p2 - p0)) fkey = mesh.halfedge[v][u] if fkey is not None and is_loaded[fkey]: p3 = C[fkey_index[fkey]] a += 0.25 * length_vector(cross_vectors(p01, p3 - p0)) areas[key_index[u]] = a return areas
def orthonormal_vectors(vec_1, vec_2): """ create a set of three orthonormal vectors vec_1 & vec_2: list of three floats return a set of the three orthonormal vectors """ vec_cros_1=cross_vectors(vec_1, vec_2) vec_cros_2=cross_vectors(vec_cros_1, vec_1) vec_list=normalize_vectors([vec_1, vec_cros_1, vec_cros_2]) return vec_list
def from_basis_vectors(cls, xaxis, yaxis): """Create rotation matrix from basis vectors (= orthonormal vectors). """ xaxis = normalize_vector(list(xaxis)) yaxis = normalize_vector(list(yaxis)) zaxis = cross_vectors(xaxis, yaxis) yaxis = cross_vectors(zaxis, xaxis) # slight correction rotation = cls() rotation.matrix[0][0], rotation.matrix[1][0], rotation.matrix[2][0] = xaxis rotation.matrix[0][1], rotation.matrix[1][1], rotation.matrix[2][1] = yaxis rotation.matrix[0][2], rotation.matrix[1][2], rotation.matrix[2][2] = zaxis return rotation
def get_boundary_plane(boundary): a = cablenet.vertex_coordinates(boundary[0]) b = cablenet.vertex_coordinates(boundary[-1]) x_axis = subtract_vectors(b, a) y_axis = [0, 0, 1.0] z_axis = cross_vectors(x_axis, y_axis) x_axis = cross_vectors(y_axis, z_axis) frame_0 = Frame(a, x_axis, y_axis) point = add_vectors(frame_0.point, scale_vector(frame_0.zaxis, OFFSET)) frame_1 = Frame(point, x_axis, y_axis) return frame_0, frame_1
def intersection_line_triangle(line, triangle, tol=1e-6): """Computes the intersection point of a line (ray) and a triangle based on the Moeller Trumbore intersection algorithm Parameters ---------- line : [point, point] | :class:`compas.geometry.Line` Two points defining the line. triangle : [point, point, point] XYZ coordinates of the triangle corners. tol : float, optional A tolerance for membership verification. Returns ------- [float, float, float] | None The intersection point between the line and the triangle, or None if the line and the plane are parallel. """ a, b, c = triangle ab = subtract_vectors(b, a) ac = subtract_vectors(c, a) n = cross_vectors(ab, ac) plane = a, n x = intersection_line_plane(line, plane, tol=tol) if x: if is_point_in_triangle(x, triangle): return x
def DrawForeground(self, e): draw_dot = e.Display.DrawDot draw_arrows = e.Display.DrawArrows a = self.mouse.p1 b = self.mouse.p2 ab = subtract_vectors(b, a) Lab = length_vector(ab) if not Lab: return for index, vertex in enumerate(self.force_vertex_xyz): c = self.force_vertex_xyz[vertex] D = length_vector( cross_vectors(subtract_vectors(a, c), subtract_vectors(b, c))) if D / Lab < self.tol: point = Point3d(*c) draw_dot(point, str(index), self.dotcolor, self.textcolor) lines = List[Line](len(self.force_vertex_edges[vertex])) for u, v in self.force_vertex_edges[vertex]: lines.Add( Line(Point3d(*self.force_vertex_xyz[u]), Point3d(*self.force_vertex_xyz[v]))) draw_arrows(lines, self.linecolor) lines = List[Line](len(self.form_face_edges[vertex])) for u, v in self.form_face_edges[vertex]: lines.Add( Line(Point3d(*self.form_vertex_xyz[u]), Point3d(*self.form_vertex_xyz[v]))) draw_arrows(lines, self.linecolor) break
def draw_circle(circle, color=None, n=100): (center, normal), radius = circle cx, cy, cz = center a, b, c = normal u = -1.0, 0.0, a v = 0.0, -1.0, b w = cross_vectors(u, v) uvw = [normalize_vector(u), normalize_vector(v), normalize_vector(w)] color = color if color else (1.0, 0.0, 0.0, 0.5) sector = 2 * pi / n glColor4f(*color) glBegin(GL_POLYGON) for i in range(n): a = i * sector x = radius * cos(a) y = radius * sin(a) z = 0 x, y, z = global_coords_numpy(center, uvw, [[x, y, z]]).tolist()[0] glVertex3f(x, y, z) glEnd() glBegin(GL_POLYGON) for i in range(n): a = -i * sector x = radius * cos(a) y = radius * sin(a) z = 0 x, y, z = global_coords_numpy(center, uvw, [[x, y, z]]).tolist()[0] glVertex3f(x, y, z) glEnd()
def DrawForeground(self, e): p1 = self.mouse.p1 p2 = self.mouse.p2 v12 = subtract_vectors(p2, p1) l12 = length_vector(v12) # force diagram for ckey in self.volmesh.cell: p0 = self.volmesh.cell_center(ckey) v01 = subtract_vectors(p1, p0) v02 = subtract_vectors(p2, p0) l = length_vector(cross_vectors(v01, v02)) color = self.edgecolor if self.color_dict: color = FromArgb(*self.color_dict[ckey]) if l12 == 0.0 or (l / l12) < self.tol: for hfkey in self.volmesh.cell_halffaces(ckey): vkeys = self.volmesh.halfface_vertices(hfkey) face_coordinates = [ self.volmesh.vertex_coordinates(vkey) for vkey in vkeys ] face_coordinates.append(face_coordinates[0]) polygon_xyz = [Point3d(*xyz) for xyz in face_coordinates] e.Display.DrawPolyline(polygon_xyz, color, 3) break
def _cross_edges(edge1, edge2): a, b = edge1 c, d = edge2 edge1_vec = normalize_vector(subtract_vectors(b, a)) edge2_vec = normalize_vector(subtract_vectors(d, c)) cross = cross_vectors(edge1_vec, edge2_vec) return cross
def intersection_line_triangle(line, triangle, tol=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 : list of list of float XYZ coordinates of the triangle corners. tol : float, optional A tolerance for membership verification. Default is ``1e-6``. Returns ------- point or None """ a, b, c = triangle ab = subtract_vectors(b, a) ac = subtract_vectors(c, a) n = cross_vectors(ab, ac) plane = a, n x = intersection_line_plane(line, plane, tol=tol) if x: if is_point_in_triangle(x, triangle): return x
def basis_vectors_from_matrix(R): """Returns the basis vectors from the rotation matrix R. Raises ------ ValueError If rotation matrix is invalid. Returns ------- list of two vectors The X and Y basis vectors of the rotation. Examples -------- >>> from compas.geometry import Frame >>> f = Frame([0, 0, 0], [0.68, 0.68, 0.27], [-0.67, 0.73, -0.15]) >>> R = matrix_from_frame(f) >>> xaxis, yaxis = basis_vectors_from_matrix(R) """ xaxis = [R[0][0], R[1][0], R[2][0]] yaxis = [R[0][1], R[1][1], R[2][1]] zaxis = [R[0][2], R[1][2], R[2][2]] if not allclose(zaxis, cross_vectors(xaxis, yaxis)): raise ValueError("Matrix is invalid rotation matrix.") return [xaxis, yaxis]
def world_to_local_coords_numpy(frame, xyz): """Convert global coordinates to local coordinates. Parameters ---------- frame : :class:`Frame` or [point, xaxis, yaxis] The local coordinate system. xyz : array-like The global coordinates of the points to convert. Returns ------- array The coordinates of the given points in the local coordinate system. Examples -------- >>> import numpy as np >>> frame = Frame([0, 1, 0], [3, 4, 1], [1, 5, 9]) >>> xyz = [Point(2, 3, 5)] >>> rst = world_to_local_coords_numpy(frame, xyz) >>> np.allclose(rst, [[3.726, 4.088, 1.550]], rtol=1e-3) True """ origin = frame[0] uvw = [frame[1], frame[2], cross_vectors(frame[1], frame[2])] uvw = asarray(uvw).T xyz = asarray(xyz).T - asarray(origin).reshape((-1, 1)) rst = solve(uvw, xyz) return rst.T
def local_to_world_coords_numpy(frame, rst): """Convert local coordinates to global (world) coordinates. Parameters ---------- frame : :class:`Frame` or [point, xaxis, yaxis] The local coordinate system. rst : array-like The coordinates of the points wrt the local coordinate system. Returns ------- array The world coordinates of the given points. Notes ----- ``origin`` and ``uvw`` together form the frame of local coordinates. Examples -------- >>> frame = Frame([0, 1, 0], [3, 4, 1], [1, 5, 9]) >>> rst = [Point(3.726, 4.088, 1.550)] >>> xyz = local_to_world_coords_numpy(frame, rst) >>> numpy.allclose(xyz, [[2.000, 3.000, 5.000]], rtol=1e-3) True """ origin = frame[0] uvw = [frame[1], frame[2], cross_vectors(frame[1], frame[2])] uvw = asarray(uvw).T rst = asarray(rst).T xyz = uvw.dot(rst) + asarray(origin).reshape((-1, 1)) return xyz.T
def trimesh_edge_cotangent(mesh, u, v): """Compute the cotangent of the angle opposite a halfedge of the triangle mesh. Parameters ---------- mesh : compas.datastructures.Mesh The triangle mesh data structure. u : int The identifier of the first vertex of the halfedge. v : int The identifier of the second vertex of the halfedge. Returns ------- float The edge cotangent. Examples -------- .. code-block:: python pass """ fkey = mesh.halfedge[u][v] cotangent = 0.0 if fkey is not None: w = mesh.face_vertex_ancestor(fkey, u) wu = mesh.edge_vector(w, u) wv = mesh.edge_vector(w, v) l = length_vector(cross_vectors(wu, wv)) if l: cotangent = dot_vectors(wu, wv) / l return cotangent
def polygon_normal_oriented(polygon, unitized=True): """Compute the oriented normal of any closed polygon (can be convex, concave or complex). Parameters ---------- polygon : sequence The XYZ coordinates of the vertices/corners of the polygon. The vertices are assumed to be in order. The polygon is assumed to be closed: the first and last vertex in the sequence should not be the same. Returns ------- list The weighted or unitized normal vector of the polygon. """ p = len(polygon) assert p > 2, "At least three points required" w = centroid_points(polygon) normal_sum = (0, 0, 0) for i in range(-1, len(polygon) - 1): u = polygon[i] v = polygon[i + 1] uv = subtract_vectors(v, u) vw = subtract_vectors(w, v) normal = scale_vector(cross_vectors(uv, vw), 0.5) normal_sum = add_vectors(normal_sum, normal) if not unitized: return normal_sum return normalize_vector(normal_sum)
def convex_polygon_area_compas(polygon_vertices): """Compute the area of a convex polygon using compas. Parameters ---------- polygon : sequence The XY coordinates of the vertices/corners of the polygon. The vertices are assumed to be in order. The polygon is assumed to be closed: the first and last vertex in the sequence should not be the same. Returns ------- float The area of the polygon. """ polygon = Polygon(polygon_vertices) if not polygon.is_convex: sys.exit("the polygon is not convex.") vectors = [] centroid = polygon.centroid area = 0.0 for p in polygon.points: vectors.append(Vector.from_start_end(centroid, p)) for i in range(len(vectors)): area += cross_vectors(vectors[i - 1], vectors[i])[2] / 2 return area
def trimesh_edge_cotangent(mesh, u, v): """Compute the cotangent of the angle opposite a halfedge of the triangle mesh. Parameters ---------- mesh : :class:`compas.datastructures.Mesh` Instance of mesh. u : int The identifier of the first vertex of the halfedge. v : int The identifier of the second vertex of the halfedge. Returns ------- float The edge cotangent. """ fkey = mesh.halfedge[u][v] cotangent = 0.0 if fkey is not None: w = mesh.face_vertex_ancestor(fkey, u) wu = mesh.edge_vector(w, u) wv = mesh.edge_vector(w, v) length = length_vector(cross_vectors(wu, wv)) if length: cotangent = dot_vectors(wu, wv) / length return cotangent
def polygon_area_footprint(polygon): """Compute the non-oriented area of a polygon (can be convex or concave). Parameters ---------- polygon : list of lists A list of polygon point coordinates. Returns ------- float The non-oriented area of the polygon. """ area = 0 w = centroid_points(polygon) for i in range(-1, len(polygon) - 1): u = polygon[i] v = polygon[i + 1] uv = subtract_vectors(v, u) vw = subtract_vectors(w, v) normal = scale_vector(cross_vectors(uv, vw), 0.5) area += length_vector(normal) return area