hoomd_bevy/representation/
hyperbolic_polygon.rs1use crate::PRIMARY_COLOR;
7use bevy::{
8 asset::embedded_asset,
9 prelude::*,
10 reflect::TypePath,
11 render::render_resource::AsBindGroup,
12 shader::ShaderRef,
13 sprite_render::{AlphaMode2d, Material2d, Material2dPlugin},
14};
15use hoomd_manifold::{Hyperbolic, Minkowski};
16use itertools::{
17 EitherOrBoth::{Both, Left, Right},
18 Itertools,
19};
20use std::marker::PhantomData;
21
22const SHADER_ASSET_PATH: &str = "embedded://hoomd_bevy/representation/hyperbolic_polygon.wgsl";
24
25#[derive(Component)]
27pub struct HyperbolicPolygon<T> {
28 marker: PhantomData<T>,
30}
31
32#[derive(Resource)]
34pub struct HyperbolicPolygonAssets<T> {
35 mesh: Handle<Mesh>,
37 material: Handle<HyperbolicPolygonMaterial>,
39 marker: PhantomData<T>,
41}
42
43pub(crate) fn build(app: &mut App) {
45 app.add_plugins(Material2dPlugin::<HyperbolicPolygonMaterial>::default());
46 embedded_asset!(app, "hyperbolic_polygon.wgsl");
47}
48
49impl<T: Send + Sync + 'static> HyperbolicPolygon<T> {
50 pub fn setup(
52 material: In<HyperbolicPolygonMaterialParameters>,
53 mut commands: Commands,
54 mut meshes: ResMut<Assets<Mesh>>,
58 mut materials: ResMut<Assets<HyperbolicPolygonMaterial>>,
59 asset_server: Res<AssetServer>,
60 ) {
61 let n_sides = material.0.n_sides;
63
64 let mesh = meshes.add(Rectangle::new(1.0, 1.0));
65 let material = HyperbolicPolygonMaterial {
66 n_sides,
67 background_color: material.0.background_color,
68 outline_color: material.0.outline_color,
69 outline_width: material.0.outline_width,
70 texture_scale: material.0.texture_scale,
71 texture: material.0.texture_asset.map(|t| asset_server.load(t)),
72 };
73 let material = materials.add(material);
74 commands.insert_resource(HyperbolicPolygonAssets::<T> {
75 mesh,
76 material,
77 marker: PhantomData,
78 });
79 }
80
81 pub fn sync<I>(
83 commands: &mut Commands,
84 disk_assets: Res<HyperbolicPolygonAssets<T>>,
85 query: Query<(Entity, &mut Transform), With<Self>>,
86 disks: I,
87 ) where
88 I: IntoIterator<Item = (Minkowski<3>, f64, f32)>,
89 {
90 for item in &mut query.into_iter().zip_longest(disks) {
91 match item {
92 Both((_, mut transform), (position, radius, theta)) => {
93 let (poincare_position, max_projected_radius) =
94 poincare(&position, radius, theta);
95 transform.translation = Vec3::from_array(poincare_position);
99 transform.scale = Vec3::from_array([
100 max_projected_radius * 2.0,
101 max_projected_radius * 2.0,
102 radius as f32, ]);
104 }
106 Left((entity, _)) => commands.entity(entity).despawn(),
107 Right((position, radius, theta)) => {
108 let (poincare_position, max_projected_radius) =
109 poincare(&position, radius, theta);
110 commands.spawn((
114 Mesh2d(disk_assets.mesh.clone()),
115 MeshMaterial2d(disk_assets.material.clone()),
116 Transform::from_translation(Vec3::from_array(poincare_position))
117 .with_scale(Vec3::from_array([
118 max_projected_radius * 2.0,
119 max_projected_radius * 2.0,
120 radius as f32, ])),
122 Self {
123 marker: PhantomData,
124 },
125 ));
126 }
127 }
128 }
129 }
130}
131
132fn poincare(point: &Minkowski<3>, radius: f64, angle: f32) -> ([f32; 3], f32) {
134 let pt = Hyperbolic::from_minkowski_coordinates(*point);
135 let proj = pt.to_poincare();
136 let v = radius;
137 let eta = (point.coordinates[2]).acosh();
138 let edge_proj = ((eta - v).sinh()) / (1.0 + (eta - v).cosh());
139 let rad_proj = ((eta).sinh()) / (1.0 + (eta).cosh()) - edge_proj;
140 ([proj[0] as f32, proj[1] as f32, angle], rad_proj as f32)
141}
142
143#[derive(Asset, TypePath, AsBindGroup, Debug, Clone)]
145pub struct HyperbolicPolygonMaterial {
146 #[uniform(0)]
148 pub background_color: LinearRgba,
149
150 #[uniform(1)]
152 pub outline_color: LinearRgba,
153
154 #[uniform(2)]
156 pub outline_width: f32,
157
158 #[uniform(3)]
160 pub texture_scale: f32,
161
162 #[texture(4)]
164 #[sampler(5)]
165 pub texture: Option<Handle<Image>>,
166 #[uniform(6)]
169 pub n_sides: f32,
170}
171
172pub struct HyperbolicPolygonMaterialParameters {
174 pub n_sides: f32,
176 pub background_color: LinearRgba,
178 pub outline_color: LinearRgba,
180 pub outline_width: f32,
182 pub texture_scale: f32,
184 pub texture_asset: Option<String>,
186}
187
188impl Default for HyperbolicPolygonMaterialParameters {
189 fn default() -> Self {
190 Self {
191 n_sides: 0.0_f32,
192 background_color: PRIMARY_COLOR.into(),
193 outline_color: Color::linear_rgb(0.0, 0.0, 0.0).into(),
194 outline_width: 0.005,
195 texture_asset: None,
196 texture_scale: 1000.0,
197 }
198 }
199}
200
201impl HyperbolicPolygonMaterialParameters {
202 #[must_use]
204 pub fn ghost() -> Self {
205 Self {
206 background_color: Color::linear_rgb(0.5, 0.5, 0.5).into(),
207 outline_color: Color::linear_rgb(0.0, 0.0, 0.0).into(),
208 outline_width: 0.005,
209 texture_asset: None,
210 texture_scale: 1.2,
211 n_sides: 4.0_f32,
212 }
213 }
214}
215
216impl Material2d for HyperbolicPolygonMaterial {
217 fn fragment_shader() -> ShaderRef {
218 SHADER_ASSET_PATH.into()
219 }
220
221 fn vertex_shader() -> ShaderRef {
222 SHADER_ASSET_PATH.into()
223 }
224
225 fn alpha_mode(&self) -> AlphaMode2d {
226 AlphaMode2d::Mask(0.5)
227 }
228}