test some laser linedrawing

This commit is contained in:
Ruben van de Ven 2025-04-08 11:39:23 +02:00
commit 655f64c913
8 changed files with 6070 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/target

5694
Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

20
Cargo.toml Normal file
View file

@ -0,0 +1,20 @@
[package]
name = "trap_rust"
version = "0.1.0"
edition = "2024"
[dependencies]
bevy = "0.15.3"
bevy_nannou = { git = "https://github.com/nannou-org/nannou", branch = "bevy-refactor", version = "0.1.0", features = ["wayland"] }
iyes_perf_ui = "0.4.0"
nannou_laser = { git = "https://github.com/nannou-org/nannou", branch = "bevy-refactor", version = "0.19.0" }
serde = "1.0.219"
serde_json = "1.0.140"
zmq = "0.10.0"
# Enable max optimizations for dependencies, but not for our code:
# (tip from bevy examples for fast compilation/performance trade-off)
[profile.dev.package."*"]
opt-level = 3

153
src/main.rs Normal file
View file

@ -0,0 +1,153 @@
use bevy::prelude::*;
use bevy::render::view::RenderLayers;
use bevy::window::WindowResolution;
use bevy_nannou::prelude::*;
use bevy_nannou::NannouPlugin;
use iyes_perf_ui::prelude::*;
use trap::zmqplugin::ZmqPlugin;
// use iyes_perf_ui::PerfUiPlugin;
use nannou_laser as laser;
use std::fs;
mod trap;
fn main() {
let mut app = App::new();
// app.add_plugins((DefaultPlugins, NannouPlugin))
app.add_plugins((DefaultPlugins, NannouPlugin))
// .add_plugins(bevy::diagnostic::FrameTimeDiagnosticsPlugin)
// .add_plugins(bevy::diagnostic::EntityCountDiagnosticsPlugin)
// .add_plugins(bevy::diagnostic::SystemInformationDiagnosticsPlugin)
// .add_plugins(PerfUiPlugin)
.add_systems(Startup, setup)
.add_systems(Update, update)
.add_plugins(ZmqPlugin {
// url: "tcp://localhost:5558".to_string(),
url: "tcp://100.109.175.82:99173".to_string(),
// url: "tcp://127.0.0.1:99173".to_string(),
filter: "".to_string()
})
.run();
}
struct LaserModel{
}
#[derive(Component)]
struct LaserApi{
_laser_api: laser::Api,
laser_stream: laser::FrameStream<LaserModel>,
}
fn setup(mut commands: Commands) {
// Spawn a camera for our main window
commands.spawn((render::NannouCamera, RenderLayers::none()));
let path = "your_future_points_test.json";
// let file = File::open(path)?;
let data = fs::read_to_string(path).expect("Unable to read file");
// let reader = BufReader::new(file);
// let res: serde_json::Value = serde_json::from_str(&data).expect("Unable to parse");
let res: serde_json::Value = serde_json::from_str(&data).expect("Unable to parse");
// let max = res[0].iter().cloned().fold(0./0., f64::max);
println!("{}",res);
// let u = serde_json::from_reader(reader)?;
// Initialise the state that we want to live on the laser thread and spawn the stream.
let laser_model = LaserModel {
};
let _laser_api = laser::Api::new();
// dacs = _laser_api.detect_dacs()
let laser_stream = _laser_api
.new_frame_stream(laser_model, laser_frame_producer)
.build()
.unwrap();
let api = LaserApi {
_laser_api,
laser_stream,
};
commands.spawn(api);
// create a simple Perf UI with default settings
// and all entries provided by the crate:
// commands.spawn(PerfUiDefaultEntries::default());
}
pub fn laser_frame_producer(_laser: &mut LaserModel, frame: &mut laser::Frame){
// Simple constructors for white or blank points.
let lit_p = |position| laser::Point::new(position, [1.0; 3]);
let positions = trap::shapes::YOU;
// let tl = [-1.0, 1.0];
// let tr = [1.0, 1.0];
// let br = [1.0, -1.0];
// let bl = [-1.0, -1.0];
// let positions = [tl, tr, br, bl, tl];
let points = positions.iter().cloned().map(lit_p);
frame.add_lines(points);
}
#[derive(Component)]
pub struct WindowColor(Color);
// fn receive_tracks(mut commands: Commands){
// }
fn update(
mut commands: Commands,
keys: Res<ButtonInput<KeyCode>>,
draws: Query<(&Draw, Option<&WindowColor>)>,
mut count: Local<usize>,
) {
if keys.just_pressed(KeyCode::Space) {
// Increment the count to track the number of windows
*count += 1;
// We need a render layer to make sure we only render the camera for this window
let layer = RenderLayers::layer(*count);
// Spawn a new window with a unique title, resolution, and background color
let entity = commands
.spawn((
Window {
title: "Nannou".to_string(),
resolution: WindowResolution::new(400.0, 400.0),
..Default::default()
},
layer.clone(),
WindowColor(match *count {
1 => RED.into(),
2 => GREEN.into(),
3 => BLUE.into(),
_ => PURPLE.into(),
}),
))
.id();
// Spawn a camera for our new window with the matching render layer
commands.spawn((render::NannouCamera::for_window(entity), layer.clone()));
}
for (draw, window_color) in draws.iter() {
if let Some(window_color) = window_color {
draw.background().color(window_color.0);
} else {
draw.background().color(DIM_GRAY);
}
draw.ellipse().color(LIGHT_GRAY).w_h(100.0, 100.0);
}
}

10
src/trap/mod.rs Normal file
View file

@ -0,0 +1,10 @@
pub mod zmqplugin;
// pub use zmqplugin::ZmqPlugin;
// mod grid;
// pub use grid::GridPlugin;
pub mod tracks;
// pub use tracks::Frame;
// pub use grid::GridPlugin;
pub mod shapes;

1
src/trap/shapes.rs Normal file
View file

@ -0,0 +1 @@
pub static YOU: [[f32; 2]; 107] = [[0.33800762191421707, 0.5879238779522781], [0.3268223414326286, 0.613215363578254], [0.3226602260682412, 0.6362631583205897], [0.3246304342549215, 0.656942657636883], [0.33184212442653593, 0.6751284478643268], [0.3434052641373563, 0.6906975427013294], [0.3584298209416543, 0.7035253376054891], [0.37602576239370183, 0.7134856097935934], [0.3953022469273653, 0.7204553729640508], [0.41536843297651127, 0.7243100225744593], [0.4353350972158167, 0.7249249540824171], [0.45431139807914817, 0.7221747538251168], [0.4714073031207774, 0.715936435500967], [0.485731970774571, 0.7060837763267552], [0.4963953685948006, 0.6924937900008901], [0.5025074641357381, 0.6750410628605643], [0.5031782249516551, 0.6536017994837813], [0.4979367429667209, 0.6156370609510401], [0.495441415637061, 0.6098526591742117], [0.49274380820609914, 0.6109951371863647], [0.48588165804953437, 0.6284511008083113], [0.47563576635839183, 0.6567800244354363], [0.4483724543049251, 0.7310726509211836], [0.4321592996253773, 0.7735563269170086], [0.41319918117015, 0.8153910883478571], [0.39072262543389086, 0.8534197473926095], [0.3639585406704372, 0.8844899709525775], [0.34872846728321644, 0.8964325881334402], [0.3321382624948419, 0.9054461894474517], [0.3140916409770938, 0.9111504883041646], [0.2944915082813474, 0.913132833296923], [0.2750572452686684, 0.9099529901044575], [0.2660768178912704, 0.9053814598150352], [0.25953427029476256, 0.8983016562694696], [0.2557823789758154, 0.8880420095314384], [0.25624276848637845, 0.8783568382811047], [0.26040892945279187, 0.8692380513144162], [0.26777516162180093, 0.8606856486313729], [0.2900842294341821, 0.8452880873202742], [0.31912275165667403, 0.8321479719397045], [0.3811944235421673, 0.812583440541787], [0.42160998778228187, 0.8019070967950742], [0.49081243779886885, 0.7746211293702616], [0.5572031944073599, 0.7415256774360592], [0.6047414455745159, 0.7169089982280263], [0.6265949785987653, 0.7023529221383434], [0.6461862109700545, 0.6832625352978777], [0.6747886172941396, 0.6504235745321262], [0.6790462088663415, 0.6442863962586273], [0.6805236627262503, 0.6418986819428599], [0.6800851194666279, 0.6419496565283881], [0.6619365487778236, 0.6567581781844957], [0.6446132809023312, 0.6681611120550849], [0.6283426786740135, 0.6820836468674905], [0.6133472502043029, 0.704449353108236], [0.6046807615441253, 0.7304221181153968], [0.6058523678908658, 0.7516235000930489], [0.6150544942592907, 0.7681109465899622], [0.6304771383029509, 0.7799386686732853], [0.650313534157018, 0.7871633047713831], [0.6727552977158532, 0.7898414933126199], [0.6959940448738178, 0.7880282544845498], [0.7182205824048677, 0.7817794175951324], [0.7281687177868938, 0.7736663672921168], [0.736318987628549, 0.7592688788018546], [0.7423380343228877, 0.7406186534618217], [0.7458933093833694, 0.719746583489089], [0.746652264323454, 0.6986835611007274], [0.7442799232953856, 0.6794612876342129], [0.7384453560534343, 0.664109846186211], [0.7288135867498442, 0.6546609380941979], [0.7050189738735022, 0.6490747708166453], [0.6933522667508152, 0.6453706176016053], [0.6853816216391161, 0.6377122929663164], [0.6858387746680583, 0.6366288807437436], [0.6884870257543025, 0.6371839373417159], [0.6949154873736761, 0.6398305701871495], [0.724901489590666, 0.6511493555355974], [0.7563560453431076, 0.6588983016562695], [0.8062269906384769, 0.6641600116513339], [0.8559361118528048, 0.6620765266079246], [0.8748695293346603, 0.6517707600067966], [0.8919500610885905, 0.6430087951388046], [0.8884870257543025, 0.6461789288864077], [0.8834785704460681, 0.6483052973112929], [0.8578132711928863, 0.6987588092984117], [0.8479743670655632, 0.7289422368942723], [0.8479500934534069, 0.7415418598441635], [0.8527562686603394, 0.7510595431706193], [0.8640677719251402, 0.7602398232881035], [0.875913294657378, 0.7659926693691288], [0.8881067391638551, 0.7686069373983543], [0.9004862813635298, 0.7683706742400337], [0.9250997240899419, 0.7605044056606064], [0.9483538445356053, 0.7447006659060936], [0.9688650468076154, 0.7232703028537677], [0.9852335526049633, 0.6985209278992808], [0.9960757660347437, 0.6727633889199052], [1.0, 0.6483052973112929], [0.9940691474298291, 0.6771059381346539], [0.9864795980289828, 0.7059518897007064], [0.9756373845992022, 0.7351692275327492], [0.969253424602115, 0.7562063580681442], [0.9650459984950361, 0.7754237768122275], [0.9650459984950361, 0.7754237768122275], [0.9650459984950361, 0.7754237768122275], [0.9650459984950361, 0.7754237768122275]];

92
src/trap/tracks.rs Normal file
View file

@ -0,0 +1,92 @@
use bevy::prelude::*;
use serde::{Serialize,Deserialize};
#[derive(Serialize,Deserialize)]
pub struct Frame {
pub tracks: std::collections::HashMap<String, Track>
}
#[derive(Serialize, Deserialize, Component, Debug)]
pub struct Detection{
track_id: u64,
l: f32,
t: f32,
w: f32,
h: f32,
conf: f32,
state: u8,
frame_nr: u64,
det_class: u8,
}
impl Detection {
fn to_point(&self) -> Vec2 {
let x = self.l + self.w/2.;
let y = self.t + self.h;
Vec2::new(x, y)
}
}
#[derive(Serialize, Deserialize, Component, Debug)]
pub struct Track {
pub track_id: u64,
// nr: u32,
pub history: Vec<Vec2>, // projected foot coordintes //Vec<Detection>, // history
pub predictor_history: Option<Vec<Vec2>>, // history
pub predictions: Option<Vec<Vec<Vec2>>>,
}
// check: https://www.reddit.com/r/bevy/comments/y1km8n/how_to_link_components_in_bevy_or_am_i_too_oop/
#[derive(Serialize, Deserialize, Component)]
pub struct PredictedTrajectory{
person: Entity,
points: Vec<Vec2>,
}
// #[derive(Bundle)]
// pub struct TrackBundle {
// track: Track,
// // label: TextBundle,
// }
// impl From<Track> for TrackBundle{
// fn from(track: Track) -> Self {
// let name = track.track_id.to_string();
// TrackBundle{
// track: track,
// // label: TextBundle{
// // text: Text::from_section(name, TextStyle::default()),
// // transform: Transform::from_translation(250. * Vec3::Y),
// // ..default()
// // }
// }
// }
// }
// #[derive(Bundle)]
// pub struct TrackBundle {
// pub history: TrajectoryBundle,
// pub time: Time,
// }
// #[derive(Bundle)]
// pub struct TrajectoryBundle {
// pub line: PolylineBundle
// // pub trajectory: Trajectory,
// }
#[derive(Component)]
pub struct Trajectory {
pub points: Vec<Vec3>,
}
fn spawn_track() {
}
fn draw_tracks() {
// evyr ææææááá
}

99
src/trap/zmqplugin.rs Normal file
View file

@ -0,0 +1,99 @@
use zmq::Socket;
use serde_json::Result;
use bevy::{ecs::system::SystemState, prelude::*};
use super::tracks::{Frame, Track};
// use trap::{Frame, Track, TrackBundle};
// use tracks::Frame;
// Because of world.insert_non_send_resource, this is an exclusive system
// see: https://bevy-cheatbook.github.io/programming/exclusive.html
fn setup_zmq(world: &mut World, params: &mut SystemState<Res<ZmqSettings>>) {
let settings = params.get(world);
let url = &settings.url;
let context = zmq::Context::new();
let subscriber = context.socket(zmq::SUB).unwrap();
assert!(subscriber.connect(url).is_ok());
// let filter = "10001";
let filter = &settings.filter; //"msgs";
assert!(subscriber.set_subscribe(filter.as_bytes()).is_ok());
world.insert_non_send_resource(subscriber);
}
fn receive_zmq_messsages(mut commands: Commands, subscriber: NonSend<Socket>, mut tracks_q: Query<&mut Track>) {
let mut items = [
subscriber.as_poll_item(zmq::POLLIN)
];
let _nr = zmq::poll(&mut items, 0).unwrap();
if items[0].is_readable() {
let json = subscriber.recv_string(0).unwrap().unwrap();
// dbg!(&json[4..]);
// let msg: Frame = serde_json::from_str(&json[4..]).expect("No valid json?");
let res: Result<Frame> = serde_json::from_str(&json);
let msg = match res {
Ok(msg) => msg, // if Ok(255), set x to 255
Err(_e) => {
println!("No valid json? {json}");
return
}, // if Err("some message"), panic with error message "some message"
};
for (_track_id, new_track) in msg.tracks.into_iter() {
let mut done = false;
for mut track in tracks_q.iter_mut() {
if track.track_id == new_track.track_id {
// track.nr += 1;
track.history = new_track.history.clone();
track.predictor_history = new_track.predictor_history.clone();
track.predictions = new_track.predictions.clone();
// TODO match lenghts of points and drawn_points
done = true;
// dbg!(&track);
break
}
}
if !done {
// CREATE
// let track = Track{
// id: json.clone(),
// nr: 0,
// points: Vec::new()
// };
dbg!(&new_track.predictor_history);
// commands.spawn(TrackBundle::from(new_track));
commands.spawn(new_track);
}
}
}
}
#[derive(Resource)]
struct ZmqSettings {
url: String,
filter: String
}
pub struct ZmqPlugin {
pub url: String,
pub filter: String
}
impl Plugin for ZmqPlugin {
fn build(&self, app: &mut App) {
let settings = ZmqSettings{
url: self.url.clone(),
filter: self.filter.clone()
};
app
.insert_resource(settings)
.add_systems(Startup, setup_zmq)
.add_systems(Update, receive_zmq_messsages);
}
}