optiwindnet.geometric¶
Module Contents¶
- optiwindnet.geometric.triangle_AR(base1C: CoordPair, base2C: CoordPair, topC: CoordPair) float[source]¶
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.
- Parameters:
uC – triangle vertices coordinates as (2,) numpy arrays
vC – triangle vertices coordinates as (2,) numpy arrays
tC – triangle vertices coordinates as (2,) numpy arrays
- Returns:
Aspect ratio of the triangle defined by the three 2D points.
- optiwindnet.geometric.point_d2line(pC: CoordPair, uC: CoordPair, vC: CoordPair) numpy.float64[source]¶
Calculate the distance from point pC to the uC-vC line.
- optiwindnet.geometric.is_same_side(uC: CoordPair, vC: CoordPair, sC: CoordPair, tC: CoordPair, touch_is_cross: bool = True) bool[source]¶
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).
- optiwindnet.geometric.any_pairs_opposite_edge(nodesC: CoordPairs, uC: CoordPair, vC: CoordPair, margin: float = 0.0) bool[source]¶
Compare relative position of vertices wrt line segment.
- Parameters:
nodesC – (N, 2) array of test coordinates
uC – (2,) array of coordinates of edge ends
vC – (2,) array of coordinates of edge ends
- Returns:
True if any two of nodesC are on opposite sides of the edge.
- optiwindnet.geometric.rotate(coords: CoordPairs, angle: float) CoordPairs[source]¶
Rotates coords (numpy array T×2) by angle (degrees)
- optiwindnet.geometric.angle_numpy(aC: CoordPairs, pivotC: CoordPairs, bC: CoordPairs) numpy.ndarray[tuple[int], numpy.dtype[numpy.float64]][source]¶
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
- Parameters:
aC – (N, 2) numpy arrays of coordinate pairs
pivotC – (N, 2) numpy arrays of coordinate pairs
bC – (N, 2) numpy arrays of coordinate pairs
- Returns:
Angles a-pivot-b (radians)
- optiwindnet.geometric.angle(aC, pivotC, bC)[source]¶
Calculate the angle aC-pivotC-bC.
angle is within ±π (shortest arc from a to b around pivot)
positive direction is counter-clockwise
- Parameters:
aC – (2,) numpy arrays of coordinate pairs
pivotC – (2,) numpy arrays of coordinate pairs
bC – (2,) numpy arrays of coordinate pairs
- Returns:
Angle aC-pivotC-bC (radians)
- optiwindnet.geometric.angle_helpers(L: networkx.Graph, include_borders: bool = True) tuple[numpy.typing.NDArray[numpy.float64], numpy.typing.NDArray[numpy.int_], list[dict[int, int]]][source]¶
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.
- Parameters:
L – location (also works with A or G)
- Returns:
Tuple of (
angle__,angle_rank__,dups_from_root_rank__)
- optiwindnet.geometric.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]][source]¶
Make functions to answer queries about relative angles.
Inputs are the outputs of angle_helpers().
- Parameters:
angle__ – (T, R)-array of angles wrt root (+-pi)
angle_rank__ – (T, R)-array of the relative placement of angles
- Returns:
union_limits() and angle_ccw()
- optiwindnet.geometric.find_edges_bbox_overlaps(VertexC: CoordPairs, u: int, v: int, edges: IndexPairs) numpy.typing.NDArray[numpy.int_][source]¶
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.
- Parameters:
VertexC – (N×2) point coordinates
u – indices of probed edge
v – indices of probed edge
edges – list of index pairs representing edges to check against
- Returns:
numpy array with the indices of overlaps in edges
- optiwindnet.geometric.is_crossing_numpy(u, v, s, t)[source]¶
Checks if (u, v) crosses (s, t).
Parallel or collinear segments (including superposition) are not considered crossings. Touching segments (including common endpoints) are considered crossings.
- Returns:
True in case of crossing.
- optiwindnet.geometric.is_crossing_no_bbox(uC: CoordPair, vC: CoordPair, sC: CoordPair, tC: CoordPair) bool[source]¶
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).
Parallel or collinear segments (including superposition) are not considered crossings. Touching segments (including common endpoints) are considered crossings.
- Returns:
True in case of crossing.
- optiwindnet.geometric.is_crossing(uC: CoordPair, vC: CoordPair, sC: CoordPair, tC: CoordPair, touch_is_cross: bool = True) bool[source]¶
Checks if (uC, vC) crosses (sC, tC).
Parallel or collinear segments (including superposition) are not considered crossings. For non-parallel segments, behavior for touching points (including common endpoints) is determined by touch_is_cross.
- Parameters:
uC – (2,) numpy array coordinates of edge ends
vC – (2,) numpy array coordinates of edge ends
sC – (2,) numpy array coordinates of edge ends
tC – (2,) numpy array coordinates of edge ends
touch_is_cross – whether to consider any common point as a crossing
- Returns:
True in case of crossing.
- optiwindnet.geometric.is_bunch_split_by_corner(bunch, a, o, b, margin=0.001)[source]¶
Check if a cone splits a bunch of points in two sets.
- Parameters:
bunch – numpy array of points (T×2)
a – points that define the cone’s angle
o – points that define the cone’s angle
b – points that define the cone’s angle
- Returns:
True if points in bunch are both inside and outside cone a-o-b
- optiwindnet.geometric.point_to_segment_distance(pC: numpy.ndarray, aC: numpy.ndarray, bC: numpy.ndarray) float[source]¶
Calculate the distance from point pC to the closed segment aC-bC.
The projection of pC onto the line through aC and bC is clamped to the segment, so the result is the distance to the nearest endpoint when the foot of the perpendicular lies outside the segment. For an unclamped line distance, see
point_d2line().- Parameters:
pC – query point coordinates as a (2,) numpy array
aC – segment endpoint coordinates as (2,) numpy arrays
bC – segment endpoint coordinates as (2,) numpy arrays
- Returns:
Euclidean distance from pC to the closest point on the segment.
- optiwindnet.geometric.unique_rays(rays: list[numpy.ndarray], angle_tol: float) list[numpy.ndarray][source]¶
Deduplicate 2D rays by direction; anti-parallel rays are kept distinct.
Two rays are considered duplicates when their unit vectors are parallel and point the same way: cross-product magnitude ≤ angle_tol and dot product ≥ 0. Rays with zero norm are dropped.
- Parameters:
rays – rays from a shared origin, each a (2,) numpy array
angle_tol – maximum unit cross-product magnitude treated as parallel
- Returns:
List of unit-length (2,) numpy arrays, one per distinct direction.
- optiwindnet.geometric.polyline_rays_at_point(coords: numpy.ndarray, pC: numpy.ndarray, *, tol: float, angle_tol: float) list[numpy.ndarray][source]¶
Get the local rays of a polyline at a point lying on (or near) it.
For each segment whose perpendicular distance to pC is at most tol, rays from pC toward each segment endpoint that sits farther than tol from pC are collected, then deduplicated by direction via
unique_rays(). The result is empty if pC is far from every segment, has at most one ray when pC is at an endpoint of an extreme segment, or two anti-parallel rays when pC is interior to a segment.- Parameters:
coords – polyline vertices as an (N, 2) numpy array
pC – query point coordinates as a (2,) numpy array
tol – distance threshold for treating pC as on a segment or at an endpoint
angle_tol – passed through to
unique_rays()for direction dedup
- Returns:
List of unit-length (2,) numpy arrays, the directions from pC along the polyline at pC.
- optiwindnet.geometric.rays_alternate(rays_a: list[numpy.ndarray], rays_b: list[numpy.ndarray]) bool[source]¶
Check whether two ray sets interleave cyclically around the origin.
Picks every 2-ray pair from each set, orders the four rays by polar angle, and checks for the cyclic label pattern ABAB (or BABA). When found, set a and set b separate one another — the geometric definition of a crossing at the shared origin. Returns False when either set has fewer than 2 rays.
- Parameters:
rays_a – 2D rays from a shared origin, each a (2,) numpy array
rays_b – 2D rays from a shared origin, each a (2,) numpy array
- Returns:
True if some 2-ray pairs from rays_a and rays_b alternate around the origin in cyclic order.
- optiwindnet.geometric.polylines_cross_at_point(coords_a: numpy.ndarray, coords_b: numpy.ndarray, pC: numpy.ndarray, *, tol: float, angle_tol: float) bool[source]¶
Check whether two polylines cross each other at a given point.
Returns False when pC is within tol of any vertex of either polyline — those cases are ambiguous and the caller is expected to classify them separately (e.g. via shared-node or endpoint filters). Otherwise extracts the local rays at pC for each polyline (via
polyline_rays_at_point()) and tests whether they alternate (viarays_alternate()).- Parameters:
coords_a – polyline vertices as (N, 2) and (M, 2) numpy arrays
coords_b – polyline vertices as (N, 2) and (M, 2) numpy arrays
pC – query point coordinates as a (2,) numpy array, expected to lie on the geometric intersection of the two polylines
tol – distance threshold for vertex-coincidence and segment-membership
angle_tol – minimum unit cross-product magnitude used during ray dedup
- Returns:
True when the polylines cross transversely at pC.
- optiwindnet.geometric.is_triangle_pair_a_convex_quadrilateral(uC: CoordPair, vC: CoordPair, sC: CoordPair, tC: CoordPair) bool[source]¶
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
- optiwindnet.geometric.perimeter(VertexC, vertices_ordered)[source]¶
Calculate the perimeter of the polygon defined by vertices_ordered.
- Parameters:
vertices_ordered – indices of VertexC in clockwise or counter-clockwise orientation.
- Returns:
The perimeter length.
- optiwindnet.geometric.complete_graph(G_base: networkx.Graph, *, include_roots: bool = False, prune: bool = True, map_crossings: bool = False) networkx.Graph[source]¶
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.
- optiwindnet.geometric.minimum_spanning_forest(A: networkx.Graph) networkx.Graph[source]¶
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.
- optiwindnet.geometric.rotation_checkers_factory(VertexC: CoordPairs) tuple[Callable[[int, int, int], bool], Callable[[int, int, int], bool], Callable[[int, int, int], float]][source]¶
- optiwindnet.geometric.rotating_calipers(convex_hull: numpy.typing.NDArray, metric: str = 'height') tuple[numpy.typing.NDArray[numpy.int_], float, float, CoordPairs][source]¶
Find the shortest width of a polygon.
Reference: Toussaint, Godfried T. “Solving geometric problems with the rotating calipers.” Proc. IEEE Melecon. Vol. 83. 1983.
- Parameters:
convex_hull – (H, 2) array of coordinates of the convex hull in counter-clockwise order
metric – what should be minimized, one of {‘height’, ‘area’}
- Returns:
best_calipers, best_caliper_angle, best_metric, bbox
- optiwindnet.geometric.area_from_polygon_vertices(X: numpy.ndarray, Y: numpy.ndarray) float[source]¶
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).
- Parameters:
X – array of X coordinates
Y – array of Y coordinates
- Returns:
area