hoomd_bevy/representation/
rectangular_boundary.rs

1// Copyright (c) 2024-2026 The Regents of the University of Michigan.
2// Part of hoomd-rs, released under the BSD 3-Clause License.
3
4//! Implement `RectangularBoundary`.
5
6use bevy::prelude::*;
7
8use crate::BOUNDARY_COLOR;
9
10/// Represent an simulation boundary with a thin lined rectangle.
11///
12/// The lines are a fixed thickness in world coordinates. A default
13/// [`RectangularBoundary`] has:
14/// `width`: 1.0
15/// `height`: 1.0
16/// `thickness`: 0.1
17/// `color`: [`BOUNDARY_COLOR`](crate::BOUNDARY_COLOR)
18/// `z`: 1.0
19///
20/// To use:
21/// * Add [`setup`](Self::setup) to the `Startup` schedule.
22/// * (if needed) Call [`sync`](Self::sync) in an `Update` schedule that runs after `AdvanceSet`.
23#[derive(Component)]
24pub struct RectangularBoundary {
25    /// Extent of the rectangle's open space in the x direction.
26    pub width: f32,
27
28    /// Extent of the rectangle's open space in the y direction.
29    pub height: f32,
30
31    /// Width of the rectangle edges.
32    pub thickness: f32,
33
34    /// Color of the rectangle.
35    pub color: Color,
36
37    /// Draw the rectangle on this z plane.
38    pub z: f32,
39}
40
41impl Default for RectangularBoundary {
42    fn default() -> Self {
43        Self {
44            width: 1.0,
45            height: 1.0,
46            thickness: 0.1,
47            color: BOUNDARY_COLOR,
48            z: 1.0,
49        }
50    }
51}
52
53impl RectangularBoundary {
54    /// Create entities that render rectangular boundaries.
55    pub fn setup(
56        rectangular_boundary: In<Self>,
57        mut commands: Commands,
58        mut meshes: ResMut<Assets<Mesh>>,
59        mut materials: ResMut<Assets<ColorMaterial>>,
60    ) {
61        let mesh = meshes.add(Rectangle::new(1.0, 1.0));
62        let material = materials.add(ColorMaterial::from_color(rectangular_boundary.color));
63
64        let height = rectangular_boundary.height;
65        let width = rectangular_boundary.width;
66        let thickness = rectangular_boundary.thickness;
67        let z = rectangular_boundary.z;
68        let half_thickness = thickness / 2.0;
69        let double_thickness = thickness * 2.0;
70
71        let left = (
72            Transform::from_xyz(-width / 2.0 - half_thickness, 0.0, z).with_scale(Vec3::new(
73                thickness,
74                height + double_thickness,
75                1.0,
76            )),
77            Mesh2d(mesh.clone()),
78            MeshMaterial2d(material.clone()),
79        );
80        let right = (
81            Transform::from_xyz(width / 2.0 + half_thickness, 0.0, z).with_scale(Vec3::new(
82                thickness,
83                height + double_thickness,
84                1.0,
85            )),
86            Mesh2d(mesh.clone()),
87            MeshMaterial2d(material.clone()),
88        );
89        let bottom = (
90            Transform::from_xyz(0.0, -height / 2.0 - half_thickness, z).with_scale(Vec3::new(
91                width + double_thickness,
92                thickness,
93                1.0,
94            )),
95            Mesh2d(mesh.clone()),
96            MeshMaterial2d(material.clone()),
97        );
98        let top = (
99            Transform::from_xyz(0.0, height / 2.0 + half_thickness, z).with_scale(Vec3::new(
100                width + double_thickness,
101                thickness,
102                1.0,
103            )),
104            Mesh2d(mesh.clone()),
105            MeshMaterial2d(material.clone()),
106        );
107
108        commands.spawn((
109            rectangular_boundary.0,
110            Transform::default(),
111            Visibility::Visible,
112            children![left, right, bottom, top],
113        ));
114    }
115
116    /// Give the rectangular boundary a new width and height.
117    #[expect(
118        clippy::missing_panics_doc,
119        reason = "Would only panic due to a bug in this module."
120    )]
121    pub fn sync(
122        entity_rectangle: Single<(Entity, &RectangularBoundary)>,
123        children: Query<&Children>,
124        mut transforms: Query<&mut Transform>,
125        new_width: f32,
126        new_height: f32,
127    ) {
128        let (entity, rectangle) = *entity_rectangle;
129
130        let height = new_height;
131        let width = new_width;
132        let z = rectangle.z;
133        let thickness = rectangle.thickness;
134        let half_thickness = thickness / 2.0;
135        let double_thickness = thickness * 2.0;
136
137        let mut child_iter = children.iter_descendants(entity);
138
139        // left
140        let child_entity = child_iter
141            .next()
142            .expect("RectangularBoundary should have 4 children");
143        if let Ok(mut transform) = transforms.get_mut(child_entity) {
144            transform.translation = Vec3::new(-width / 2.0 - half_thickness, 0.0, z);
145            transform.scale = Vec3::new(thickness, height + double_thickness, 1.0);
146        }
147
148        // right
149        let child_entity = child_iter
150            .next()
151            .expect("RectangularBoundary should have 4 children");
152        if let Ok(mut transform) = transforms.get_mut(child_entity) {
153            transform.translation = Vec3::new(width / 2.0 + half_thickness, 0.0, z);
154            transform.scale = Vec3::new(thickness, height + double_thickness, 1.0);
155        }
156
157        // bottom
158        let child_entity = child_iter
159            .next()
160            .expect("RectangularBoundary should have 4 children");
161        if let Ok(mut transform) = transforms.get_mut(child_entity) {
162            transform.translation = Vec3::new(0.0, -height / 2.0 - half_thickness, z);
163            transform.scale = Vec3::new(width + double_thickness, thickness, 1.0);
164        }
165
166        // top
167        let child_entity = child_iter
168            .next()
169            .expect("RectangularBoundary should have 4 children");
170        if let Ok(mut transform) = transforms.get_mut(child_entity) {
171            transform.translation = Vec3::new(0.0, height / 2.0 + half_thickness, z);
172            transform.scale = Vec3::new(width + double_thickness, thickness, 1.0);
173        }
174    }
175}