Skip to main content

examples/queries/
query_group_by_callbacks.rs

1use crate::z_ignore_test_common::*;
2
3use core::ffi::c_void;
4use flecs_ecs::prelude::*;
5use flecs_ecs::sys;
6use std::sync::Mutex;
7
8#[derive(Debug, Component)]
9pub struct Position {
10    pub x: f32,
11    pub y: f32,
12}
13
14#[derive(Component)]
15pub struct Tag;
16
17static GROUP_COUNTER: Mutex<i32> = Mutex::new(0);
18
19struct GroupCtx {
20    counter: i32,
21}
22
23#[derive(Component)]
24pub struct First;
25
26#[derive(Component)]
27pub struct Second;
28
29#[derive(Component)]
30pub struct Third;
31
32#[derive(Component)]
33pub struct Group;
34
35#[extern_abi]
36fn callback_group_create(
37    world: *mut sys::ecs_world_t,
38    group_id: u64,
39    _group_by_ctx: *mut c_void,
40) -> *mut c_void {
41    let world_ref = unsafe { WorldRef::from_ptr(world) };
42    println!(
43        "Group created: {:?}",
44        world_ref.world().entity_from_id(group_id).name()
45    );
46
47    println!();
48
49    let mut counter = GROUP_COUNTER.lock().unwrap();
50    *counter += 1;
51
52    // Return data that will be associated with the group
53    let ctx = Box::new(GroupCtx { counter: *counter });
54
55    Box::into_raw(ctx) as *mut core::ffi::c_void // Cast to make sure function type matches
56}
57
58#[extern_abi]
59fn callback_group_delete(
60    world: *mut sys::ecs_world_t,
61    group_id: u64,
62    _ctx: *mut c_void,
63    _group_by_ctx: *mut c_void,
64) {
65    let world_ref = unsafe { WorldRef::from_ptr(world) };
66    println!(
67        "Group deleted: {:?}",
68        world_ref.world().entity_from_id(group_id).name()
69    );
70
71    // if you have any data associated with the group, you need to free it
72    // or use the callback group_by_ctx where you pass a context to the callback
73}
74
75fn main() {
76    let world = World::new();
77
78    // Register components in order so that id for First is lower than Third
79    world.component::<First>();
80    world.component::<Second>();
81    world.component::<Third>();
82
83    // Grouped query
84    let query = world
85        .query::<(&Position,)>()
86        .group_by(Group)
87        // Callback invoked when a new group is created
88        .on_group_create(Some(callback_group_create))
89        // Callback invoked when a group is deleted
90        .on_group_delete(Some(callback_group_delete))
91        .build();
92
93    // Create entities in 6 different tables with 3 group ids
94    world
95        .entity()
96        .add((Group, Third))
97        .set(Position { x: 1.0, y: 1.0 });
98    world
99        .entity()
100        .add((Group, Second))
101        .set(Position { x: 2.0, y: 2.0 });
102    world
103        .entity()
104        .add((Group, First))
105        .set(Position { x: 3.0, y: 3.0 });
106
107    world
108        .entity()
109        .add((Group, Third))
110        .set(Position { x: 4.0, y: 4.0 })
111        .add(Tag);
112    world
113        .entity()
114        .add((Group, Second))
115        .set(Position { x: 5.0, y: 5.0 })
116        .add(Tag);
117    world
118        .entity()
119        .add((Group, First))
120        .set(Position { x: 6.0, y: 6.0 })
121        .add(Tag);
122
123    println!();
124
125    // The query cache now looks like this:
126    //  - group First:
127    //     - table [Position, (Group, First)]
128    //     - table [Position, Tag, (Group, First)]
129    //
130    //  - group Second:
131    //     - table [Position, (Group, Second)]
132    //     - table [Position, Tag, (Group, Second)]
133    //
134    //  - group Third:
135    //     - table [Position, (Group, Third)]
136    //     - table [Position, Tag, (Group, Third)]
137    //
138
139    query.run(|mut it| {
140        while it.next() {
141            let group = world.entity_from_id(it.group_id());
142            let pos = it.field::<Position>(0);
143
144            let ctx = unsafe { &*(query.group_context(group) as *mut GroupCtx) };
145            println!(
146                "Group: {:?} - Table: [{:?}] - Counter: {}",
147                group.path().unwrap(),
148                it.archetype(),
149                ctx.counter
150            );
151
152            for i in it.iter() {
153                println!(" [{:?}]", pos[i]);
154            }
155
156            println!();
157        }
158    });
159
160    // Deleting the query will call the on_group_deleted callback
161    query.destruct();
162
163    // Output:
164    //  Group created: "Third"
165    //  Group created: "Second"
166    //  Group created: "First"
167    //
168    //  Group: "::First" - Table: [Position, (Group,First)] - Counter: 3
169    //   [Position { x: 3.0, y: 3.0 }]
170    //
171    //  Group: "::First" - Table: [Position, Tag, (Group,First)] - Counter: 3
172    //   [Position { x: 6.0, y: 6.0 }]
173    //
174    //  Group: "::Second" - Table: [Position, (Group,Second)] - Counter: 2
175    //   [Position { x: 2.0, y: 2.0 }]
176    //
177    //  Group: "::Second" - Table: [Position, Tag, (Group,Second)] - Counter: 2
178    //   [Position { x: 5.0, y: 5.0 }]
179    //
180    //  Group: "::Third" - Table: [Position, (Group,Third)] - Counter: 1
181    //   [Position { x: 1.0, y: 1.0 }]
182    //
183    //  Group: "::Third" - Table: [Position, Tag, (Group,Third)] - Counter: 1
184    //   [Position { x: 4.0, y: 4.0 }]
185    //
186    //  Group deleted: "Second"
187    //  Group deleted: "First"
188    //  Group deleted: "Third"
189}
190
191#[cfg(feature = "flecs_nightly_tests")]
192#[test]
193fn test() {
194    let output_capture = OutputCapture::capture().unwrap();
195    main();
196    output_capture.test("query_group_by_callbacks".to_string());
197}