# This file is automatically generated by pyo3_stub_gen
# ruff: noqa: E501, F401, F403, F405
r"""
Placement search for nesting algorithms.

Provides candidate generation strategies (bottom-left, grid, perimeter),
position search, and high-level nesting orchestration.
"""

import collections.abc
import numpy
from raygeo.geo.algo import spatial_grid2d
__all__ = [
    "calculate_fitness",
    "filter_candidates_multi_resolution",
    "find_valid_position",
    "find_valid_position_nfp",
    "find_valid_position_scored",
    "generate_bottom_left_candidates",
    "generate_grid_candidates",
    "generate_perimeter_candidates",
    "place_parts",
]

def calculate_fitness(polygon_groups: collections.abc.Sequence[collections.abc.Sequence[numpy.ndarray]], rotations: collections.abc.Sequence[float], sheet_indices: collections.abc.Sequence[int], *, num_parts: int = 0) -> float:
    r"""
    Calculate fitness score for a set of placements.
    
    Lower is better. Returns infinity if no placements or zero area.
    
    :param polygon_groups: Polygons for each placement.
    :param rotations: Rotation angle in degrees for each placement.
    :param sheet_indices: 0-based sheet index for each placement.
    :param num_parts: Total number of parts (some may be unplaced).
    :returns: Fitness score (lower is better).
    :complexity: O(n) where n = number of placements.
    """

def filter_candidates_multi_resolution(candidates: list[tuple[float, float]], ifp_bounds: tuple[float, float, float, float], min_dist: float) -> list[tuple[float, float]]:
    r"""
    Remove candidates that are too close together.
    
    :param candidates: List of (x, y) candidate positions.
    :param ifp_bounds: IFP bounding box (min_x, min_y, max_x, max_y).
    :param min_dist: Minimum allowed distance between candidates.
    :returns: Filtered list of (x, y) positions.
    :complexity: O(n log n) for spatial distance filtering.
    """

def find_valid_position(ifp_polygons: collections.abc.Sequence[collections.abc.Sequence[tuple[float, float]]], part_polygons: collections.abc.Sequence[collections.abc.Sequence[tuple[float, float]]], part_hulls: collections.abc.Sequence[collections.abc.Sequence[tuple[float, float]]], placed_polys_list: collections.abc.Sequence[collections.abc.Sequence[collections.abc.Sequence[tuple[float, float]]]], placed_hulls_list: collections.abc.Sequence[collections.abc.Sequence[collections.abc.Sequence[tuple[float, float]]]], grid: spatial_grid2d.SpatialGrid, sheet_world_offset: tuple[float, float], spacing: float = 1, min_area: float = 1, curve_tolerance: float = 0.5) -> tuple[float, float] | None:
    r"""
    Find a valid position: heuristic search first, NFP fallback second.
    
    Supports hull-based collision detection and sheet world offsets.
    
    :param ifp_polygons: IFP polygons (valid placement region).
    :param part_polygons: Part polygons to place.
    :param part_hulls: Convex hulls for collision (may be empty).
    :param placed_polys_list: Already-placed parts, each a list of polygons.
    :param placed_hulls_list: Hulls of already-placed parts, each a list.
    :param grid: SpatialGrid for fast neighbor lookup.
    :param sheet_world_offset: (offset_x, offset_y) for this sheet.
    :param spacing: Minimum spacing between parts.
    :param min_area: Minimum overlap area (clipper coords).
    :param curve_tolerance: Curve tolerance for distance filtering.
    :returns: (x, y) position or None.
    :complexity: O(n * m) for candidate search with overlap checks.
    """

def find_valid_position_nfp(ifp_polygons: collections.abc.Sequence[collections.abc.Sequence[tuple[float, float]]], part_polygons: collections.abc.Sequence[collections.abc.Sequence[tuple[float, float]]], part_hulls: collections.abc.Sequence[collections.abc.Sequence[tuple[float, float]]], placed_polys_list: collections.abc.Sequence[collections.abc.Sequence[collections.abc.Sequence[tuple[float, float]]]], placed_hulls_list: collections.abc.Sequence[collections.abc.Sequence[collections.abc.Sequence[tuple[float, float]]]], grid: spatial_grid2d.SpatialGrid, sheet_world_offset: tuple[float, float], spacing: float = 1, min_area: float = 1, curve_tolerance: float = 0.5) -> tuple[float, float] | None:
    r"""
    Find a valid position using NFP-based region subtraction.
    
    Computes No-Fit Polygons for nearby placed parts and subtracts
    them from the IFP to identify viable placement regions.
    
    :param ifp_polygons: IFP polygons (valid placement region).
    :param part_polygons: Part polygons to place.
    :param part_hulls: Convex hulls for collision (may be empty).
    :param placed_polys_list: Already-placed parts, each a list of polygons.
    :param placed_hulls_list: Hulls of already-placed parts, each a list.
    :param grid: SpatialGrid for fast neighbor lookup.
    :param sheet_world_offset: (offset_x, offset_y) for this sheet.
    :param spacing: Minimum spacing between parts.
    :param min_area: Minimum overlap area (clipper coords).
    :param curve_tolerance: Curve tolerance for distance filtering.
    :returns: (x, y) position or None.
    :complexity: O(n * m) for NFP construction per nearby placed part.
    """

def find_valid_position_scored(ifp_polygons: collections.abc.Sequence[collections.abc.Sequence[tuple[float, float]]], part_polygons: collections.abc.Sequence[collections.abc.Sequence[tuple[float, float]]], part_hulls: collections.abc.Sequence[collections.abc.Sequence[tuple[float, float]]], placed_polys_list: collections.abc.Sequence[collections.abc.Sequence[collections.abc.Sequence[tuple[float, float]]]], placed_hulls_list: collections.abc.Sequence[collections.abc.Sequence[collections.abc.Sequence[tuple[float, float]]]], grid: spatial_grid2d.SpatialGrid, sheet_world_offset: tuple[float, float], spacing: float = 1, min_area: float = 1, curve_tolerance: float = 0.5) -> tuple[float, float] | None:
    r"""
    Find a valid position using heuristic candidate search.
    
    Uses IFP vertices, bottom-left sweep, grid, placed polygon vertices,
    and perimeter candidates. Falls back to NFP-region candidates.
    Scores candidates and picks the best valid one.
    
    :param ifp_polygons: IFP polygons (valid placement region).
    :param part_polygons: Part polygons to place.
    :param part_hulls: Convex hulls for collision (may be empty).
    :param placed_polys_list: Already-placed parts, each a list of polygons.
    :param placed_hulls_list: Hulls of already-placed parts, each a list.
    :param grid: SpatialGrid for fast neighbor lookup.
    :param sheet_world_offset: (offset_x, offset_y) for this sheet.
    :param spacing: Minimum spacing between parts.
    :param min_area: Minimum overlap area (clipper coords).
    :param curve_tolerance: Curve tolerance for distance filtering.
    :returns: (x, y) position or None.
    :complexity: O(n * m) for scored candidate search.
    """

def generate_bottom_left_candidates(ifp_bounds: tuple[float, float, float, float], part_bounds: tuple[float, float, float, float], spacing: float) -> list[tuple[float, float]]:
    r"""
    Generate candidate positions scanning from bottom-left.
    
    :param ifp_bounds: IFP bounding box (min_x, min_y, max_x, max_y).
    :param part_bounds: Part bounding box (min_x, min_y, max_x, max_y).
    :param spacing: Minimum spacing between parts.
    :returns: List of (x, y) candidate positions.
    :complexity: O(n) where n = number of grid cells scanned.
    """

def generate_grid_candidates(ifp_bounds: tuple[float, float, float, float], part_bounds: tuple[float, float, float, float], spacing: float) -> list[tuple[float, float]]:
    r"""
    Generate candidate positions in a grid pattern.
    
    :param ifp_bounds: IFP bounding box (min_x, min_y, max_x, max_y).
    :param part_bounds: Part bounding box (min_x, min_y, max_x, max_y).
    :param spacing: Grid spacing.
    :returns: List of (x, y) candidate positions.
    :complexity: O(n) where n = number of grid cells.
    """

def generate_perimeter_candidates(placed_groups: collections.abc.Sequence[collections.abc.Sequence[list[tuple[float, float]]]], part_bounds: tuple[float, float, float, float], spacing: float) -> list[tuple[float, float]]:
    r"""
    Generate edge-aligned candidates around placed parts.
    
    For each placed part (group of polygons), produces 8 positions:
    right-bottom, right-top, left-bottom, left-top, above-left,
    above-right, below-left, below-right.
    
    :param placed_groups: List of placed parts, each a list of polygons.
    :param part_bounds: Part bounding box (min_x, min_y, max_x, max_y).
    :param spacing: Minimum spacing between parts.
    :returns: List of (x, y) candidate positions.
    :complexity: O(n) where n = number of placed parts.
    """

def place_parts(part_polys: collections.abc.Sequence[collections.abc.Sequence[collections.abc.Sequence[tuple[float, float]]]], part_hulls: collections.abc.Sequence[collections.abc.Sequence[collections.abc.Sequence[tuple[float, float]]]], sheet_polys: collections.abc.Sequence[collections.abc.Sequence[tuple[float, float]]], sheet_offsets: collections.abc.Sequence[tuple[float, float]], rotations: collections.abc.Sequence[float], flips_h: collections.abc.Sequence[bool], flips_v: collections.abc.Sequence[bool], spacing: float = 1, min_area: float = 1, curve_tolerance: float = 0.5) -> list[dict]:
    r"""
    Place as many parts as possible onto sheets.
    
    Supports combined IFP for multi-polygon parts, hull-based
    collision detection, world-space offsets per sheet, gravity
    post-processing, and fitness calculation.
    
    Parts are sorted by area (largest first).  For each part, the
    best sheet and position are selected.  After all parts are
    placed, gravity is applied per sheet.
    
    :param part_polys: List of parts, each a list of polygon point lists.
    :param part_hulls: List of hull groups per part (may be empty).
    :param sheet_polys: List of sheet polygons.
    :param sheet_offsets: World-space offset (x, y) for each sheet.
    :param rotations: Rotation angle (degrees) for each part.
    :param flips_h: Horizontal flip flag per part.
    :param flips_v: Vertical flip flag per part.
    :param spacing: Minimum spacing between parts.
    :param min_area: Minimum overlap area (clipper coords).
    :param curve_tolerance: Curve tolerance for distance filtering.
    :returns: List of dicts, one per sheet, with keys: placements,
              sheet_index, unused_part_indices, fitness.
    :complexity: O(p * s * n * m) where p = parts, s = sheets.
    """

