map_scatter 0.2.0

Rule-based object scattering library with field-graph evaluation and sampling
Documentation
# map_scatter

[![License: MIT or Apache 2.0](https://img.shields.io/badge/License-MIT%20or%20Apache2-blue.svg)](https://github.com/morgenthum/map_scatter#license)
[![Docs](https://docs.rs/map_scatter/badge.svg)](https://docs.rs/map_scatter)
[![Crate](https://img.shields.io/crates/v/map_scatter.svg)](https://crates.io/crates/map_scatter)
[![Build Status](https://github.com/morgenthum/map_scatter/actions/workflows/ci.yml/badge.svg)](https://github.com/morgenthum/map_scatter/actions/workflows/ci.yml)

Rule-based object scattering library with field-graph evaluation and sampling.

![logo](./logo.png)

## Overview

**map_scatter** is a fast, composable scattering library for games and tools. You define:
- Scalar fields as a directed acyclic graph (DAG), including textures and distance-field utilities.
- Candidate positions via a selection of samplers (Poisson disk, jittered grid, Halton, best-candidate, clustered, and more).
- A multi-layer plan describing which “kinds” can be placed, their probabilities, and gating rules.

At runtime, map_scatter evaluates the field graph over chunked grids with caching, selects valid placements according to your strategy, and optionally produces overlay textures for downstream layers.

### Highlights
- Field graph authoring and compilation into an efficient program
- Chunked evaluation with raster caching for speed
- Multiple sampling strategies for candidate generation
- Per-layer selection strategies (weighted random, highest probability)
- Optional overlay generation to feed subsequent layers
- Event stream for inspection, logging, and tooling

## Architecture

For a high-level architecture overview, see [ARCHITECTURE.md](./ARCHITECTURE.md).

## Status

This crate is actively developed. The core APIs are designed to be practical and composable for real projects. Feedback and contributions are welcome.

## Quick Start

Add the dependency:

```toml
[dependencies]
map_scatter = "0.2"
rand = "0.9"
glam = { version = "0.30", features = ["mint"] }
mint = "0.5"
```

Hello, scatter:

```rust
use glam::Vec2;
use rand::{SeedableRng, rngs::StdRng};

use map_scatter::prelude::*;

fn main() {
    // 1) Author a field graph for a “kind”
    //    Here, we tag a constant=1.0 as the Probability field (always placeable).
    let mut spec = FieldGraphSpec::default();
    spec.add_with_semantics(
        "probability",
        NodeSpec::constant(1.0),
        FieldSemantics::Probability,
    );
    let grass = Kind::new("grass", spec);

    // 2) Build a layer using a sampling strategy (e.g., jittered grid)
    let layer = Layer::new_with(
        "layer_grass",
        vec![grass],
        JitterGridSampling::new(0.35, 5.0), // jitter, cell_size
    )
    // Optional: produce an overlay mask to reuse in later layers (name: "mask_layer_grass")
    .with_overlay((256, 256), 3);

    // 3) Assemble a plan (one or more layers)
    let plan = Plan::new().with_layer(layer);

    // 4) Prepare runtime
    let mut cache = FieldProgramCache::new();
    let textures = TextureRegistry::new(); // Register textures as needed
    let cfg = RunConfig::new(Vec2::new(100.0, 100.0))
        .with_chunk_extent(32.0)
        .with_raster_cell_size(1.0)
        .with_grid_halo(2);

    // 5) Run
    let mut rng = StdRng::seed_from_u64(42);
    let mut runner = ScatterRunner::new(cfg, &textures, &mut cache);
    let result = runner.run(&plan, &mut rng);

    println!(
        "Placed {} instances (evaluated: {}, rejected: {}).",
        result.placements.len(),
        result.positions_evaluated,
        result.positions_rejected
    );
}
```

Observing events:

```rust
use rand::{SeedableRng, rngs::StdRng};
use map_scatter::prelude::*;

fn run_with_events(plan: &Plan) {
    let mut cache = FieldProgramCache::new();
    let textures = TextureRegistry::new();
    let cfg = RunConfig::new(glam::Vec2::new(64.0, 64.0));
    let mut rng = StdRng::seed_from_u64(7);

    let mut runner = ScatterRunner::new(cfg, &textures, &mut cache);

    // Capture events for inspection (warnings, per-position evaluations, overlays, etc.)
    let mut sink = VecSink::new();
    let result = runner.run_with_events(plan, &mut rng, &mut sink);

    for event in sink.into_inner() {
        match event {
            ScatterEvent::PlacementMade { placement, .. } => {
                println!("Placed '{}' at {:?}", placement.kind_id, placement.position);
            }
            ScatterEvent::Warning { context, message } => {
                eprintln!("[WARN] {context}: {message}");
            }
            _ => {}
        }
    }

    println!("Total placed: {}", result.placements.len());
}
```

## Performance Notes

- Chunked evaluation: Keeps working sets small and cache-friendly.
- Raster cell size and chunk extent control performance/quality trade-offs.
- Field programs are cached and reused per (Kind, Chunk).
- Overlays are generated only when configured on the layer.

## API Tips

- Bring common types into scope with:
  ```rust
  use map_scatter::prelude::*;
  ```
- Start simple: one kind with a constant Probability field, then introduce gates/overlays.
- Tune `RunConfig`:
  - `chunk_extent`: larger chunks reduce overhead but can increase evaluation cost
  - `raster_cell_size`: smaller cells improve accuracy at higher cost
  - `grid_halo`: extra cells for filters/EDT at chunk borders
- Overlays: bridge layers by enabling `with_overlay`, then refer to the registered texture `mask_<layer_id>` in subsequent field graphs.

## Compatibility

- 2D domains (Vec2 positions); usable for 3D by feeding height/slope textures and augmenting the 2D placement with a height component in your engine
- No engine lock-in; pair with your renderer/tooling of choice
- Integrates well with `tracing` for diagnostics
- Use `rand` RNGs; examples commonly use `StdRng`

## Benchmarks

Some micro-benchmarks are included:
```bash
cargo bench -p map_scatter
```

## Roadmap

- Additional field nodes and utilities
- Advanced distance-field ops and compositors
- More sampling controls and distributions
- Higher-level authoring ergonomics

Contributions and ideas are welcome—please open issues or PRs.

## License

map_scatter is dual-licensed under either:
- MIT License ([LICENSE-MIT]https://github.com/morgenthum/map_scatter/blob/main/LICENSE-MIT)
- Apache License, Version 2.0 ([LICENSE-APACHE]https://github.com/morgenthum/map_scatter/blob/main/LICENSE-APACHE)

at your option.

## Links

- Documentation: https://docs.rs/map_scatter
- Crate: https://crates.io/crates/map_scatter
- Repository: https://github.com/morgenthum/map_scatter