1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
//! # bevy_symbios_shape
//!
//! Bevy integration for [`symbios-shape`](https://crates.io/crates/symbios-shape) —
//! a CGA Shape Grammar engine for procedural architecture.
//!
//! ## Quick start
//!
//! ```ignore
//! use bevy::prelude::*;
//! use bevy_symbios_shape::prelude::*;
//! use symbios_shape::{Interpreter, Scope, grammar::parse_ops};
//!
//! fn main() {
//! App::new()
//! .add_plugins((DefaultPlugins, BevySymbiosShapePlugin))
//! .add_systems(Startup, setup)
//! .run();
//! }
//!
//! fn setup(
//! mut commands: Commands,
//! registry: Res<ShapeRegistry>,
//! mut meshes: ResMut<Assets<Mesh>>,
//! mut materials: ResMut<Assets<StandardMaterial>>,
//! mut cache: ResMut<ShapeMeshCache>,
//! ) {
//! let mut interp = Interpreter::new();
//! interp.add_rule("Lot", parse_ops("Extrude(12) Split(Y) { 3: Ground | ~1: Upper | 2: Roof }").unwrap());
//! interp.add_rule("Ground", parse_ops(r#"I("GroundFloor")"#).unwrap());
//! interp.add_rule("Upper", parse_ops(r#"I("Floor")"#).unwrap());
//! interp.add_rule("Roof", parse_ops(r#"Taper(0.8) I("Roof")"#).unwrap());
//!
//! let footprint = Scope::new(
//! symbios_shape::Vec3::ZERO,
//! symbios_shape::Quat::IDENTITY,
//! symbios_shape::Vec3::new(10.0, 0.0, 10.0),
//! );
//!
//! commands
//! .spawn_shape(&interp, footprint, "Lot", ®istry, &mut meshes, &mut materials, &mut cache)
//! .unwrap();
//! }
//! ```
//!
//! ## Modules
//!
//! | Module | Purpose |
//! |---|---|
//! | [`cache`] | [`ShapeMeshCache`] — bounded cross-spawn mesh cache (LRU, hit/miss/eviction counters) |
//! | [`events`] | [`SpawnShapeRequest`] / [`SpawnShapeSpawned`] / [`SpawnShapeFailed`] event surface |
//! | [`label`] | [`TerminalLabel`] component carrying the grammar's `Label("…")` occlusion class |
//! | [`mass`] | [`TerminalMass`] component carrying upstream `MassProperties` |
//! | [`mesh`] | `build_profiled_mesh` / `build_profiled_mesh_with` / `build_tapered_cuboid` — meshing, including round cross-sections |
//! | `mutation` | `mutation` feature only — texture-config evolution plumbing |
//! | [`query`] | [`LastDerivedModel`] resource exposing the most recent `ShapeModel` for spatial queries |
//! | [`registry`] | [`ShapeRegistry`] — asset routing table (mesh + material IDs → handles) |
//! | [`snap`] | [`SnapPlanes`] resource exposing `RegSnap("…")` recordings |
//! | [`spawner`] | [`SpawnShapeExt`] — `Commands` extension trait |
//! | [`transform`] | [`scope_to_transform`] — `Scope` → Bevy `Transform` with f64→f32 + corner-to-centroid offset |
//!
//! ## Round cross-sections
//!
//! Grammar scopes are always axis-aligned boxes, but a terminal's rendering
//! need not be. Opting an asset ID into round rendering makes the mesher
//! inscribe an elliptical prism in the scope — a cylinder for `Rectangle`
//! profiles, a frustum for `Taper(t)`, a cone at `Taper(1.0)` — which is how
//! columns, silos, chimneys, and spires are built without giving up the
//! OBB-pure derivation:
//!
//! ```ignore
//! // Grammar: Column --> Extrude(0.6) Taper(0.14) Mat("Marble") I("Column")
//! registry.register_round_mesh("Column");
//! registry.set_round_segments(32); // default 24
//! ```
//!
//! Splits, occlusion queries, and mass properties still see the box; only
//! the baked mesh changes. See [`registry::ShapeRegistry::register_round_mesh`].
//!
//! ## Cargo features
//!
//! - `egui` (off by default) — pulls in [`bevy_egui`](https://crates.io/crates/bevy_egui).
//! Reserved for inspection/editor UIs built on top of the crate; the core
//! pipeline does not depend on it.
//! - `mutation` (off by default) — enables the `mutation` module, which
//! wires [`bevy_symbios_texture`](https://crates.io/crates/bevy_symbios_texture)
//! procedural texture configs into a Bevy observer pipeline so shape-grammar
//! materials can be evolved via `symbios_genetics::Genotype`.
pub use ShapeMeshCache;
pub use ;
pub use TerminalLabel;
pub use TerminalMass;
pub use ;
pub use LastDerivedModel;
pub use ShapeRegistry;
pub use ;
pub use SpawnShapeExt;
pub use scope_to_transform;
pub use ;
/// Convenience re-exports for the most common types.
/// Bevy plugin that wires the crate's resources and observer into an [`App`].
///
/// Add this plugin to your [`App`] alongside [`DefaultPlugins`]:
///
/// ```ignore
/// App::new()
/// .add_plugins((DefaultPlugins, BevySymbiosShapePlugin))
/// // ...
/// ```
///
/// The plugin installs the following [`Resource`]s (each in their `Default`
/// state) and observer:
///
/// - [`ShapeRegistry`] — asset routing table; populate it before any
/// `spawn_shape` call so terminals can resolve registered scenes/materials.
/// - [`SnapPlanes`] — overwritten by every `spawn_shape` call with the
/// `RegSnap("…")` planes recorded during derivation.
/// - [`LastDerivedModel`] — overwritten by every `spawn_shape` call with the
/// full upstream [`ShapeModel`][symbios_shape::ShapeModel] for spatial
/// queries.
/// - [`ShapeMeshCache`] — cross-spawn procedural mesh handle cache.
/// - An observer for [`SpawnShapeRequest`] that performs the derivation +
/// spawn and triggers [`SpawnShapeSpawned`] / [`SpawnShapeFailed`].
///
/// [`App`]: bevy::prelude::App
/// [`DefaultPlugins`]: bevy::prelude::DefaultPlugins
/// [`Resource`]: bevy::prelude::Resource
;