Metadata-Version: 2.4
Name: b123D-positioning
Version: 2.1.0
Summary: `build123d` ergonomic selector methods
License-File: LICENSE
Requires-Python: >=3.10
Requires-Dist: build123d>=0.11.0
Description-Content-Type: text/markdown

# b123d_positioning

An extension for `build123d` that installs ergonomic methods for face, edge and vertex selection, e.g. `object.vertices().top_front_left()`.

## Usage

Importing the module automatically monkey-patches the `build123d.ShapeList` class. Your standard objects will immediately possess the new functionality.

```python
import b123d_positioning
from build123d import Box

base = Box(10, 10, 10)

# Extract a single vertex
top_left_corner = base.vertices().top_left()

# Extract a group of edges (pass as_list=True)
top_edges = base.edges().top(as_list=True)

# Extract a face by size
biggest_face = base.faces().largest()

```

---

## Spatial Selectors

Spatial selectors filter shapes based on their bounding box centers relative to the global 3D coordinate system.

### API Grammar

The naming convention strictly follows standard CAD axes:

* **Z-Axis:** `bottom` / `top`
* **Y-Axis:** `front` / `back`
* **X-Axis:** `left` / `right`

Methods can target a single axis or chain up to three dimensions (e.g., `bottom_front_left()`).

### Singular vs. Grouped (`as_list=True`)
By default, spatial selectors return the **single** shape at the absolute extreme of the requested vector. If you want to return a **subset** (group) of all shapes that share that exact boundary, pass `as_list=True` into the method.

| Mode | Return Type | Examples |
| :--- | :--- | :--- |
| **Default** (`as_list=False`) | `Shape` | `top()`, `bottom_back()`, `top_front_right()` |
| **Grouped** (`as_list=True`) | `ShapeList` | `top(as_list=True)`, `bottom_back(as_list=True)` |

### Ambiguity Warnings
If you use a singular selector (e.g., `.left()`) on a dimension where multiple shapes share the exact same extreme coordinate (like the 4 left-most vertices of a 3D box), `b123d_positioning` will emit a `UserWarning`. It will still return a single arbitrary shape to prevent crashing your script, but alerts you that the selection is mathematically ambiguous. To resolve the warning, pass `as_list=True` to select the whole group, or use a fully constrained 2D/3D selector.

## Dimensional Selectors

Filters the `ShapeList` based on physical size properties (`.length` for Edges, `.area` for Faces, and `.volume` for Solids). Just like spatial selectors, dimensional selectors support the `as_list=True` parameter to return all shapes that tie for that extreme size (within a specified tolerance).

| Method | Description |
| :--- | :--- |
| `largest(as_list=False, tol=1e-5)` | Returns the largest shape(s) in the list. |
| `smallest(as_list=False, tol=1e-5)` | Returns the smallest shape(s) in the list. |
| `longest(as_list=False, tol=1e-5)` | An alias for `largest()`; improves readability when filtering Edges. |
| `shortest(as_list=False, tol=1e-5)` | An alias for `smallest()`; improves readability when filtering Edges. |

```python
# Grab all edges tied for the longest length
long_edges = base.edges().longest(as_list=True)

# Grab the single smallest face (will warn if multiple faces tie for smallest)
tiny_face = base.faces().smallest()
```

*Note: Dimensional selectors will raise a `ValueError` if used on `Vertex` objects, as they are dimensionless.*

---

## Orientation Selectors

Orientation selectors filter shapes based on their angle and alignment in 3D space. To align human intuition with CAD geometry, the vocabulary is specifically tailored to how we visualize lines versus surfaces:

* **Universal Selectors:** `.horizontal()` and `.vertical()` work across both Edges and Faces. Under the hood, they intelligently evaluate **Tangents** for Edges and **Normals** for Faces.
* **Edge Selectors (`along_*`):** Filters edges by the physical path of their line.
* **Face Selectors (`facing_*`):** Filters faces by the direction their surface is "looking" (their normal vector).

| Method | Supported Shapes | Description |
| :--- | :--- | :--- |
| `horizontal(as_list=False)` | Edges, Faces | Flat surfaces (XY plane) or level-running edges. |
| `vertical(as_list=False)` | Edges, Faces | Upright walls or vertical Z-running edges. |
| `along_x(as_list=False)` | **Edges only** | Edges running left-to-right parallel to the X-axis. |
| `along_y(as_list=False)` | **Edges only** | Edges running front-to-back parallel to the Y-axis. |
| `along_z(as_list=False)` | **Edges only** | Edges running up-and-down (synonym for `vertical()`). |
| `facing_x(as_list=False)` | **Faces only** | Outer side walls facing left or right (Normal $\parallel$ X). |
| `facing_y(as_list=False)` | **Faces only** | Outer front or back walls (Normal $\parallel$ Y). |
| `facing_z(as_list=False)` | **Faces only** | Top or bottom floors/ceilings (synonym for `horizontal()`). |

```python
# Extract all upright side walls of a model
side_walls = base.faces().vertical(as_list=True)

# Extract only the edges running front-to-back along the Y axis
y_lines = base.edges().along_y(as_list=True)

# Extract the outer left and right facing walls
side_faces = base.faces().facing_x(as_list=True)
```

### Type Guardrails & Tolerances

* Type Safety: Attempting to use a line-path selector (`.along_x()`) on a Face, or a surface-normal selector (`.facing_x()`) on an Edge will immediately raise a descriptive `ValueError` directing you to the correct method.

* Ambiguity: Calling an orientation selector with `as_list=False` (default) when multiple shapes match will emit a `UserWarning` and return an arbitrary matching shape. Pass `as_list=True` to safely return the entire group.

## Technical Notes

* **Evaluation Engine:** Under the hood, spatial selectors rely on standard `build123d` bounding box centers. To evaluate geometry, the engine chains `group_by` operations across all specified axes to drill down to the final geometric extreme. If a single shape is requested (`as_list=False`), it extracts the first instance from that final group. If a subset is requested (`as_list=True`), it returns the entire final group.
