Skip to content

Commit

Permalink
Add draft collision
Browse files Browse the repository at this point in the history
Signed-off-by: Xiyu Oh <xiyu@openrobotics.org>
  • Loading branch information
xiyuoh committed Jan 17, 2025
1 parent edd54cc commit dcdf8b3
Show file tree
Hide file tree
Showing 7 changed files with 286 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*
* Copyright (C) 2025 Open Source Robotics Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

use super::CollisionKinds;
use crate::site::{CircleCollision, Collision};
use bevy::prelude::*;
use bevy_egui::egui::{DragValue, Grid, Ui};

#[derive(Default)]
pub struct InspectCircleCollisionPlugin {}

impl Plugin for InspectCircleCollisionPlugin {
fn build(&self, app: &mut App) {
app.world.resource_mut::<CollisionKinds>().0.insert(
CircleCollision::label(),
|collision, ui| {
InspectCircleCollision::new(collision).show(ui);
},
);
}
}

pub struct InspectCircleCollision<'a> {
collision: &'a mut Collision,
}

impl<'a> InspectCircleCollision<'a> {
pub fn new(collision: &'a mut Collision) -> Self {
Self { collision }
}

pub fn show(self, ui: &mut Ui) {
let mut new_circle_collision =
match serde_json::from_value::<CircleCollision>(self.collision.config.clone()) {
Ok(circle_collision) => circle_collision,
Err(_) => CircleCollision::default(),
};

ui.indent("inspect_circle_collision_properties", |ui| {
Grid::new("inspect_circle_collision")
.num_columns(3)
.show(ui, |ui| {
ui.label("Collision Radius");
if ui
.add(
DragValue::new(&mut new_circle_collision.radius)
.clamp_range(0_f32..=std::f32::INFINITY)
.speed(0.01),
)
.is_pointer_button_down_on()
{
// TODO(@xiyuoh) bring in poses and gizmos
// if let Ok(pose) = params.poses.get(selection) {
// params.gizmos.circle(
// Vec3::new(pose.trans[0], pose.trans[1], pose.trans[2] + 0.01),
// Vec3::Z,
// new_circle_collision.radius,
// Color::RED,
// );
// }
};
ui.label("m");
ui.end_row();

ui.label("Offset");
ui.label("x");
ui.label("y");
ui.end_row();

ui.label("");
ui.add(
DragValue::new(&mut new_circle_collision.offset[0])
.clamp_range(std::f32::NEG_INFINITY..=std::f32::INFINITY)
.speed(0.01),
);
ui.add(
DragValue::new(&mut new_circle_collision.offset[1])
.clamp_range(std::f32::NEG_INFINITY..=std::f32::INFINITY)
.speed(0.01),
);
ui.end_row();
});
});

if let Ok(new_value) = serde_json::to_value(new_circle_collision) {
self.collision.config = new_value;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
/*
* Copyright (C) 2025 Open Source Robotics Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

use super::get_selected_description_entity;
use crate::{
site::{Affiliation, Change, Collision, Group, ModelMarker, ModelProperty, Pose, Robot},
widgets::{prelude::*, Inspect},
};
use bevy::{ecs::system::SystemParam, prelude::*};
use bevy_egui::egui::{ComboBox, Ui};
use std::collections::HashMap;

#[derive(Resource)]
pub struct CollisionKinds(pub HashMap<String, fn(&mut Collision, &mut Ui)>);

impl FromWorld for CollisionKinds {
fn from_world(_world: &mut World) -> Self {
CollisionKinds(HashMap::new())
}
}

#[derive(Default)]
pub struct InspectCollisionPlugin {}

impl Plugin for InspectCollisionPlugin {
fn build(&self, app: &mut App) {
app.init_resource::<CollisionKinds>()
.add_plugins(InspectionPlugin::<InspectCollision>::new());
}
}

#[derive(SystemParam)]
pub struct InspectCollision<'w, 's> {
collision: ResMut<'w, CollisionKinds>,
model_instances: Query<
'w,
's,
&'static Affiliation<Entity>,
(With<ModelMarker>, Without<Group>, With<Robot>),
>,
model_descriptions:
Query<'w, 's, &'static ModelProperty<Robot>, (With<ModelMarker>, With<Group>)>,
change_robot_property: EventWriter<'w, Change<ModelProperty<Robot>>>,
poses: Query<'w, 's, &'static Pose>,
gizmos: Gizmos<'s>,
}

impl<'w, 's> WidgetSystem<Inspect> for InspectCollision<'w, 's> {
fn show(
Inspect { selection, .. }: Inspect,
ui: &mut Ui,
state: &mut SystemState<Self>,
world: &mut World,
) {
let mut params = state.get_mut(world);
let Some(description_entity) = get_selected_description_entity(
selection,
&params.model_instances,
&params.model_descriptions,
) else {
return;
};
let Ok(ModelProperty(robot)) = params.model_descriptions.get(description_entity) else {
return;
};
let collision = robot
.properties
.get(&Collision::label())
.and_then(|c| serde_json::from_value::<Collision>(c.clone()).ok());
let mut has_collision = collision.is_some();
// TODO(@xiyuoh) a lot of duplicate code with inspect_mobility, consider consolidating
// some of them to inspect_robot_properties

ui.checkbox(&mut has_collision, "Collision");

if !has_collision {
let mut new_robot = robot.clone();
new_robot.properties.remove(&Collision::label());
params
.change_robot_property
.send(Change::new(ModelProperty(new_robot), description_entity));
return;
}

let mut new_collision = match collision {
Some(ref c) => c.clone(),
None => Collision::default(),
};

let selected_collision_kind = if !new_collision.is_empty() {
new_collision.kind.clone()
} else {
"Select Kind".to_string()
};

ui.indent("configure_collision", |ui| {
ui.horizontal(|ui| {
ui.label("Collision Kind");
ComboBox::from_id_source("select_collision_kind")
.selected_text(selected_collision_kind)
.show_ui(ui, |ui| {
for (kind, _) in params.collision.0.iter() {
ui.selectable_value(
&mut new_collision.kind,
kind.clone(),
kind.clone(),
);
}
});
});
if !new_collision.is_default() {
if let Some(show_widget) = params.collision.0.get(&new_collision.kind) {
show_widget(&mut new_collision, ui);
}
}
});

if collision.is_none()
|| collision.is_some_and(|m| m != new_collision && !new_collision.is_empty())
{
if let Ok(new_value) = serde_json::to_value(new_collision) {
let mut new_robot = robot.clone();
new_robot.properties.insert(Collision::label(), new_value);
params
.change_robot_property
.send(Change::new(ModelProperty(new_robot), description_entity));
}
}
ui.add_space(10.0);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

use super::get_selected_description_entity;
use crate::{
site::{Affiliation, Change, Group, Mobility, ModelMarker, ModelProperty, Pose, Robot},
site::{Affiliation, Change, Group, Mobility, ModelMarker, ModelProperty, Robot},
widgets::{prelude::*, Inspect},
};
use bevy::{ecs::system::SystemParam, prelude::*};
Expand Down Expand Up @@ -55,8 +55,6 @@ pub struct InspectMobility<'w, 's> {
model_descriptions:
Query<'w, 's, &'static ModelProperty<Robot>, (With<ModelMarker>, With<Group>)>,
change_robot_property: EventWriter<'w, Change<ModelProperty<Robot>>>,
poses: Query<'w, 's, &'static Pose>,
gizmos: Gizmos<'s>,
}

impl<'w, 's> WidgetSystem<Inspect> for InspectMobility<'w, 's> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
use super::get_selected_description_entity;
use crate::{
site::{
update_model_instances, Affiliation, Change, ChangePlugin, Group, ModelMarker,
ModelProperty, Robot, Tasks,
update_model_instances, Affiliation, ChangePlugin, Group, ModelMarker, ModelProperty,
Robot, Tasks,
},
widgets::{prelude::*, Inspect},
ModelPropertyData,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ use crate::{
MainInspector,
};

pub mod inspect_circle_collision;
pub use inspect_circle_collision::*;

pub mod inspect_collision;
pub use inspect_collision::*;

pub mod inspect_differential_drive;
pub use inspect_differential_drive::*;

Expand Down
2 changes: 2 additions & 0 deletions rmf_site_editor/src/widgets/inspector/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,8 @@ impl Plugin for StandardInspectorPlugin {
InspectRobotPropertiesPlugin::default(),
InspectMobilityPlugin::default(),
InspectDifferentialDrivePlugin::default(),
InspectCollisionPlugin::default(),
InspectCircleCollisionPlugin::default(),
InspectTaskPlugin::default(),
InspectDefaultTasksPlugin::default(),
));
Expand Down
30 changes: 27 additions & 3 deletions rmf_site_format/src/robot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@
*
*/

// TODO(@xiyuoh) change file name to robot.rs since we are mostly dealing with Robot properties

#[cfg(feature = "bevy")]
use bevy::prelude::{Component, Reflect};
use bevy::{
math::Vec2,
prelude::{Component, Reflect},
};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

Expand Down Expand Up @@ -140,3 +141,26 @@ impl Collision {
"Collision".to_string()
}
}

// Supported kinds of Collision
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[cfg_attr(feature = "bevy", derive(Component, Reflect))]
pub struct CircleCollision {
pub radius: f32,
pub offset: Vec2,
}

impl Default for CircleCollision {
fn default() -> Self {
Self {
radius: 0.0,
offset: Vec2::default(),
}
}
}

impl CircleCollision {
pub fn label() -> String {
"Circle Collision".to_string()
}
}

0 comments on commit dcdf8b3

Please sign in to comment.