optiwindnet.geometric ===================== .. py:module:: optiwindnet.geometric Module Contents --------------- .. py:function:: triangle_AR(base1C: CoordPair, base2C: CoordPair, topC: CoordPair) -> float Calculate the ratio: dist(base1, base2)/dist(base, top). Numerator is the length of the base of the triagle (base1C, base2C). Denominator is the distance from point topC to the base line. :param uC: triangle vertices coordinates as (2,) numpy arrays :param vC: triangle vertices coordinates as (2,) numpy arrays :param tC: triangle vertices coordinates as (2,) numpy arrays :returns: Aspect ratio of the triangle defined by the three 2D points. .. py:function:: point_d2line(pC: CoordPair, uC: CoordPair, vC: CoordPair) -> numpy.float64 Calculate the distance from point `pC` to the `uC`-`vC` line. .. py:function:: is_same_side(uC: CoordPair, vC: CoordPair, sC: CoordPair, tC: CoordPair, touch_is_cross: bool = True) -> bool Check if points `sC` an `tC` are on the same side of the line defined by points `uC` and `vC`. Note: often used to check crossings with feeder links, where the feeder link `sC`-`tC` is already known to be on a line that crosses the edge `uC`–`vC` (using the angle rank). .. py:function:: any_pairs_opposite_edge(nodesC: CoordPairs, uC: CoordPair, vC: CoordPair, margin: float = 0.0) -> bool Compare relative position of vertices wrt line segment. :param nodesC: (N, 2) array of test coordinates :param uC: (2,) array of coordinates of edge ends :param vC: (2,) array of coordinates of edge ends :returns: True if any two of `nodesC` are on opposite sides of the edge. .. py:function:: rotate(coords: CoordPairs, angle: float) -> CoordPairs Rotates `coords` (numpy array T×2) by `angle` (degrees) .. py:function:: angle_numpy(aC: CoordPairs, pivotC: CoordPairs, bC: CoordPairs) -> numpy.ndarray[tuple[int], numpy.dtype[numpy.float64]] Calculate the angle a-pivot-b. * can operate on multiple point triplets * angle is within ±π (shortest arc from a to b around pivot) * positive direction is counter-clockwise :param aC: (N, 2) numpy arrays of coordinate pairs :param pivotC: (N, 2) numpy arrays of coordinate pairs :param bC: (N, 2) numpy arrays of coordinate pairs :returns: Angles a-pivot-b (radians) .. py:function:: angle(aC, pivotC, bC) Calculate the angle aC-pivotC-bC. * angle is within ±π (shortest arc from a to b around pivot) * positive direction is counter-clockwise :param aC: (2,) numpy arrays of coordinate pairs :param pivotC: (2,) numpy arrays of coordinate pairs :param bC: (2,) numpy arrays of coordinate pairs :returns: Angle aC-pivotC-bC (radians) .. py:function:: angle_helpers(L: networkx.Graph, include_borders: bool = True) -> tuple[numpy.typing.NDArray[numpy.float64], numpy.typing.NDArray[numpy.int_], list[dict[int, int]]] Create auxiliary arrays of node attributes based on polar coordinates. The ranks of the angles and calculated per root and start from 0. The duplicates mapping is a list of dicts and is indexed first by the root. :param L: location (also works with A or G) :returns: Tuple of (angle__, angle_rank__, dups_from_root_rank__) .. py:function:: angle_oracles_factory(angle__: numpy.typing.NDArray[numpy.float64], angle_rank__: numpy.typing.NDArray[numpy.int_]) -> tuple[Callable[[int, int, int, int, int, int, int], tuple[int, int]], Callable[[int, int, int], float]] Make functions to answer queries about relative angles. Inputs are the outputs of `angle_helpers()`. :param angle__: (T, R)-array of angles wrt root (+-pi) :param angle_rank__: (T, R)-array of the relative placement of angles :returns: union_limits() and angle_ccw() .. py:function:: find_edges_bbox_overlaps(VertexC: CoordPairs, u: int, v: int, edges: IndexPairs) -> numpy.typing.NDArray[numpy.int_] Find which `edges` have a bounding box overlap with ⟨u, v⟩. This is a preliminary filter for crossing checks. Enables avoiding the more costly geometric crossing calculations for segments that are clearly disjoint. :param VertexC: (N×2) point coordinates :param u: indices of probed edge :param v: indices of probed edge :param edges: list of index pairs representing edges to check against :returns: numpy array with the indices of overlaps in `edges` .. py:function:: is_crossing_numpy(u, v, s, t) Checks if (u, v) crosses (s, t). :returns: True in case of crossing. .. py:function:: is_crossing_no_bbox(uC: CoordPair, vC: CoordPair, sC: CoordPair, tC: CoordPair) -> bool Checks if (uC, vC) crosses (sC, tC). Does not check for bounding-box overlap. Use `find_edges_bbox_overlap()` first to filter out edges with disjoint bounding boxes (cheaper than the calculations here). :returns: True in case of crossing. .. py:function:: is_crossing(uC: CoordPair, vC: CoordPair, sC: CoordPair, tC: CoordPair, touch_is_cross: bool = True) -> bool Checks if (uC, vC) crosses (sC, tC). :param uC: (2,) numpy array coordinates of edge ends :param vC: (2,) numpy array coordinates of edge ends :param sC: (2,) numpy array coordinates of edge ends :param tC: (2,) numpy array coordinates of edge ends :param touch_is_cross: whether to consider any common point as a crossing :returns: True in case of crossing. .. py:function:: is_bunch_split_by_corner(bunch, a, o, b, margin=0.001) Check if a cone splits a bunch of points in two sets. :param bunch: numpy array of points (T×2) :param a: points that define the cone's angle :param o: points that define the cone's angle :param b: points that define the cone's angle :returns: True if points in bunch are both inside and outside cone a-o-b .. py:function:: is_triangle_pair_a_convex_quadrilateral(uC: CoordPair, vC: CoordPair, sC: CoordPair, tC: CoordPair) -> bool Check convexity of quadrilateral. ⟨u, v⟩ is the common side; ⟨s, t⟩ are the opposing vertices; only works if ⟨s, t⟩ crosses the line defined by ⟨u, v⟩ :returns: True if the quadrilateral is convex and is not a triangle .. py:function:: perimeter(VertexC, vertices_ordered) Calculate the perimeter of the polygon defined by `vertices_ordered`. :param vertices_ordered: indices of `VertexC` in clockwise or counter-clockwise orientation. :returns: The perimeter length. .. py:function:: assign_root(A: networkx.Graph) -> None Add node attribute 'root' with the root closest to each node. Changes A in-place. :param A: available-edges graph .. py:function:: get_crossings_map(Edge, VertexC, prune=True) .. py:function:: complete_graph(G_base: networkx.Graph, *, include_roots: bool = False, prune: bool = True, map_crossings: bool = False) -> networkx.Graph Create a complete graph based on G_base. Produces a networkx Graph connecting all non-root nodes to every other non-root node. Edges with an arc > pi/2 around root are discarded The length of each edge is the euclidean distance between its vertices. .. py:function:: minimum_spanning_forest(A: networkx.Graph) -> networkx.Graph Create the minimum spanning forest from the Delaunay edges of `A`. There is one tree for each root and exactly one root per tree. If the graph has more than one root, the minimum spanning tree of the entire graph is split on its longest links between each root pair. :returns: Topology S containing the forest. .. py:function:: rotation_checkers_factory(VertexC: CoordPairs) -> tuple[Callable[[int, int, int], bool], Callable[[int, int, int], bool]] .. py:function:: rotating_calipers(convex_hull: numpy.typing.NDArray, metric: str = 'height') -> tuple[numpy.typing.NDArray[numpy.int_], float, float, CoordPairs] Find the shortest width of a polygon. Reference: Toussaint, Godfried T. "Solving geometric problems with the rotating calipers." Proc. IEEE Melecon. Vol. 83. 1983. :param convex_hull: (H, 2) array of coordinates of the convex hull in counter-clockwise order :param metric: what should be minimized, one of {'height', 'area'} :returns: best_calipers, best_caliper_angle, best_metric, bbox .. py:function:: area_from_polygon_vertices(X: numpy.ndarray, Y: numpy.ndarray) -> float Calculate the area enclosed by the polygon with the vertices (x, y). Vertices must be in sequence around the perimeter (either clockwise or counter-clockwise). :param X: array of X coordinates :param Y: array of Y coordinates :returns: area .. py:function:: add_link_blockmap(A: networkx.Graph) Experimental. Add attributes 'blocked__' to edges and nodes. Edges' 'blocked__' are R-long list of T-long bitarray maps. A 1-bit in position `t` on the bitarray for root `r` means the edge crosses the line-of-sight t-r. Changes `A` in place. `A` should have no feeder edges. .. py:function:: add_link_cosines(A: networkx.Graph) Add cosine of the angle wrt each root to all links of A as attribute '_cos'. Changes A in-place. The cosine is of the acute angle between the link line and the line that contains the mid-point of the link and the root (for each root).