1mod scene;
2mod utils;
3
4use proc_macro::TokenStream;
5use proc_macro_crate::{FoundCrate, crate_name};
6use proc_macro2::Span;
7use quote::quote;
8use syn::{Data, DeriveInput, Fields, Ident, ItemFn, parse_macro_input};
9
10use crate::scene::parse_scene_attrs;
11
12const RANIM_CRATE_NAME: &str = "ranim";
13
14fn ranim_path() -> proc_macro2::TokenStream {
15 match (
16 crate_name(RANIM_CRATE_NAME),
17 std::env::var("CARGO_CRATE_NAME").as_deref(),
18 ) {
19 (Ok(FoundCrate::Itself), Ok(RANIM_CRATE_NAME)) => quote!(crate),
20 (Ok(FoundCrate::Name(name)), _) => {
21 let ident = Ident::new(&name, Span::call_site());
22 quote!(::#ident)
23 }
24 _ => quote!(::ranim),
25 }
26}
27
28#[derive(Default)]
30struct SceneAttrs {
31 name: Option<String>, frame_height: Option<f64>, clear_color: Option<String>, wasm_demo_doc: bool, outputs: Vec<OutputDef>, }
37
38#[derive(Default)]
40struct OutputDef {
41 width: u32,
42 height: u32,
43 fps: u32,
44 save_frames: bool,
45 dir: String,
46}
47
48#[proc_macro_attribute]
50pub fn scene(args: TokenStream, input: TokenStream) -> TokenStream {
51 let ranim = ranim_path();
52 let input_fn = parse_macro_input!(input as ItemFn);
53 let attrs = parse_scene_attrs(args, input_fn.attrs.as_slice()).unwrap();
54
55 let fn_name = &input_fn.sig.ident;
56 let vis = &input_fn.vis;
57 let fn_body = &input_fn.block;
58 let doc_attrs: Vec<_> = input_fn
59 .attrs
60 .iter()
61 .filter(|attr| attr.path().is_ident("doc"))
62 .collect();
63
64 let scene_name = attrs.name.unwrap_or_else(|| fn_name.to_string());
66
67 let frame_height = attrs.frame_height.unwrap_or(8.0);
69 let clear_color = attrs.clear_color.unwrap_or("#333333ff".to_string());
70 let scene_config = quote! {
71 #ranim::SceneConfig {
72 frame_height: #frame_height,
73 clear_color: #clear_color,
74 }
75 };
76
77 let mut outputs = Vec::new();
79 for OutputDef {
80 width,
81 height,
82 fps,
83 save_frames,
84 dir,
85 } in attrs.outputs
86 {
87 outputs.push(quote! {
88 #ranim::Output {
89 width: #width,
90 height: #height,
91 fps: #fps,
92 save_frames: #save_frames,
93 dir: #dir,
94 }
95 });
96 }
97 if outputs.is_empty() {
98 outputs.push(quote! {
99 #ranim::Output::DEFAULT
100 });
101 }
102
103 let doc = if attrs.wasm_demo_doc {
104 quote! {
105 #[doc = concat!("<canvas id=\"ranim-app-", stringify!(#fn_name), "\" width=\"1280\" height=\"720\" style=\"width: 100%;\"></canvas>")]
106 #[doc = concat!("<script type=\"module\">")]
107 #[doc = concat!(" const { find_scene, preview_scene } = await ranim_examples;")]
108 #[doc = concat!(" preview_scene(find_scene(\"", stringify!(#fn_name), "\"));")]
109 #[doc = "</script>"]
110 }
111 } else {
112 quote! {}
113 };
114
115 let static_output_name = syn::Ident::new(
116 &format!("__SCENE_{}_OUTPUTS", fn_name.to_string().to_uppercase()),
117 fn_name.span(),
118 );
119 let static_name = syn::Ident::new(
120 &format!("__SCENE_{}", fn_name.to_string().to_uppercase()),
121 fn_name.span(),
122 );
123 let static_scene_name = syn::Ident::new(&format!("{fn_name}_scene"), fn_name.span());
124
125 let output_cnt = outputs.len();
126
127 let scene = quote! {
128 #ranim::Scene {
129 name: #scene_name,
130 constructor: #fn_name,
131 config: #scene_config,
132 outputs: &#static_output_name,
133 }
134 };
135
136 let expanded = quote! {
138 #doc
139 #(#doc_attrs)*
140 #vis fn #fn_name(r: &mut #ranim::RanimScene) #fn_body
141
142 static #static_output_name: [#ranim::Output; #output_cnt] = [#(#outputs),*];
143 #[doc(hidden)]
144 static #static_name: #ranim::Scene = #scene;
145 #ranim::inventory::submit!{
146 #scene
147 }
148
149 #[allow(non_upper_case_globals)]
150 #vis static #static_scene_name: &'static #ranim::Scene = &#static_name;
151 };
152
153 TokenStream::from(expanded)
154}
155
156#[proc_macro_attribute]
166pub fn output(_: TokenStream, _: TokenStream) -> TokenStream {
167 TokenStream::new()
168}
169
170#[proc_macro_attribute]
176pub fn wasm_demo_doc(_attr: TokenStream, _: TokenStream) -> TokenStream {
177 TokenStream::new()
178}
179
180#[proc_macro_derive(Fill)]
183pub fn derive_fill(input: TokenStream) -> TokenStream {
184 impl_derive(
185 input,
186 |ranim| quote! {#ranim::traits::Fill},
187 |ranim, field_positions| {
188 quote! {
189 fn set_fill_opacity(&mut self, opacity: f32) -> &mut Self {
190 #(
191 self.#field_positions.set_fill_opacity(opacity);
192 )*
193 self
194 }
195 fn fill_color(&self) -> #ranim::color::AlphaColor<#ranim::color::Srgb> {
196 [#(self.#field_positions.fill_color(), )*].first().cloned().unwrap()
197 }
198 fn set_fill_color(&mut self, color: #ranim::color::AlphaColor<#ranim::color::Srgb>) -> &mut Self {
199 #(
200 self.#field_positions.set_fill_color(color);
201 )*
202 self
203 }
204 }
205 },
206 )
207}
208
209#[proc_macro_derive(Stroke)]
210pub fn derive_stroke(input: TokenStream) -> TokenStream {
211 impl_derive(
212 input,
213 |ranim| quote! {#ranim::traits::Stroke},
214 |ranim, field_positions| {
215 quote! {
216 fn stroke_color(&self) -> #ranim::color::AlphaColor<#ranim::color::Srgb> {
217 [#(self.#field_positions.stroke_color(), )*].first().cloned().unwrap()
218 }
219 fn apply_stroke_func(&mut self, f: impl for<'a> Fn(&'a mut [#ranim::components::width::Width])) -> &mut Self {
220 #(
221 self.#field_positions.apply_stroke_func(&f);
222 )*
223 self
224 }
225 fn set_stroke_color(&mut self, color: #ranim::color::AlphaColor<#ranim::color::Srgb>) -> &mut Self {
226 #(
227 self.#field_positions.set_stroke_color(color);
228 )*
229 self
230 }
231 fn set_stroke_opacity(&mut self, opacity: f32) -> &mut Self {
232 #(
233 self.#field_positions.set_stroke_opacity(opacity);
234 )*
235 self
236 }
237 }
238 },
239 )
240}
241
242#[proc_macro_derive(Partial)]
243pub fn derive_partial(input: TokenStream) -> TokenStream {
244 impl_derive(
245 input,
246 |ranim| quote! {#ranim::traits::Partial},
247 |_ranim, field_positions| {
248 quote! {
249 fn get_partial(&self, range: std::ops::Range<f64>) -> Self {
250 Self {
251 #(
252 #field_positions: self.#field_positions.get_partial(range.clone()),
253 )*
254 }
255 }
256 fn get_partial_closed(&self, range: std::ops::Range<f64>) -> Self {
257 Self {
258 #(
259 #field_positions: self.#field_positions.get_partial(range.clone()),
260 )*
261 }
262 }
263 }
264 },
265 )
266}
267
268#[proc_macro_derive(Empty)]
269pub fn derive_empty(input: TokenStream) -> TokenStream {
270 let ranim = ranim_path();
271 let input = parse_macro_input!(input as DeriveInput);
272 let name = &input.ident;
273 let generics = &input.generics;
274 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
275
276 let fields = match &input.data {
277 Data::Struct(data) => &data.fields,
278 _ => panic!("Empty can only be derived for structs"),
279 };
280
281 let field_impls = match fields {
282 Fields::Named(fields) => {
283 let (field_names, field_types): (Vec<_>, Vec<_>) =
284 fields.named.iter().map(|f| (&f.ident, &f.ty)).unzip();
285
286 quote! {
287 Self {
288 #(
289 #field_names: #field_types::empty(),
290 )*
291 }
292 }
293 }
294 Fields::Unnamed(fields) => {
295 let field_types = fields.unnamed.iter().map(|f| &f.ty);
296 quote! {
297 Self (
298 #(
299 #field_types::empty(),
300 )*
301 )
302 }
303 }
304 Fields::Unit => quote! {},
305 };
306
307 let expanded = quote! {
308 impl #impl_generics #ranim::traits::Empty for #name #ty_generics #where_clause {
309 fn empty() -> Self {
310 #field_impls
311 }
312 }
313 };
314
315 TokenStream::from(expanded)
316}
317
318#[proc_macro_derive(Opacity)]
319pub fn derive_opacity(input: TokenStream) -> TokenStream {
320 impl_derive(
321 input,
322 |ranim| quote! {#ranim::traits::Opacity},
323 |_ranim, field_positions| {
324 quote! {
325 fn set_opacity(&mut self, opacity: f32) -> &mut Self {
326 #(
327 self.#field_positions.set_opacity(opacity);
328 )*
329 self
330 }
331 }
332 },
333 )
334}
335
336#[proc_macro_derive(Alignable)]
337pub fn derive_alignable(input: TokenStream) -> TokenStream {
338 impl_derive(
339 input,
340 |ranim| quote! {#ranim::traits::Alignable},
341 |_ranim, field_positions| {
342 quote! {
343 fn is_aligned(&self, other: &Self) -> bool {
344 #(
345 self.#field_positions.is_aligned(&other.#field_positions) &&
346 )* true
347 }
348 fn align_with(&mut self, other: &mut Self) {
349 #(
350 self.#field_positions.align_with(&mut other.#field_positions);
351 )*
352 }
353 }
354 },
355 )
356}
357
358#[proc_macro_derive(Interpolatable)]
359pub fn derive_interpolatable(input: TokenStream) -> TokenStream {
360 impl_derive(
361 input,
362 |ranim| quote! {#ranim::traits::Interpolatable},
363 |ranim, field_positions| {
364 quote! {
365 fn lerp(&self, other: &Self, t: f64) -> Self {
366 Self {
367 #(
368 #field_positions: #ranim::traits::Interpolatable::lerp(&self.#field_positions, &other.#field_positions, t),
369 )*
370 }
371 }
372 }
373 },
374 )
375}
376
377#[proc_macro_derive(BoundingBox)]
378pub fn derive_bounding_box(input: TokenStream) -> TokenStream {
379 let ranim = ranim_path();
380 let input = parse_macro_input!(input as DeriveInput);
381 let name = &input.ident;
382 let generics = &input.generics;
383 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
384
385 let fields = match &input.data {
386 Data::Struct(data) => &data.fields,
387 _ => panic!("Can only be derived for structs"),
388 };
389
390 let field_positions = get_field_positions(fields)
391 .ok_or("cannot get field from unit struct")
392 .unwrap();
393
394 let expanded = quote! {
395 impl #impl_generics #ranim::traits::BoundingBox for #name #ty_generics #where_clause {
396 fn get_bounding_box(&self) -> [DVec3; 3] {
397 let [min, max] = [#(self.#field_positions.get_bounding_box(), )*]
398 .into_iter()
399 .map(|[min, _, max]| [min, max])
400 .reduce(|[acc_min, acc_max], [min, max]| [acc_min.min(min), acc_max.max(max)])
401 .unwrap();
402 [min, (min + max) / 2.0, max]
403 }
404 }
405 };
406
407 TokenStream::from(expanded)
408}
409
410#[proc_macro_derive(Position)]
411pub fn derive_position(input: TokenStream) -> TokenStream {
412 impl_derive(
413 input,
414 |ranim| {
415 quote! {#ranim::traits::Position}
416 },
417 |ranim, field_positions| {
418 quote! {
419 fn shift(&mut self, shift: DVec3) -> &mut Self {
420 #(self.#field_positions.shift(shift);)*
421 self
422 }
423
424 fn rotate_by_anchor(&mut self, angle: f64, axis: #ranim::glam::DVec3, anchor: #ranim::components::Anchor) -> &mut Self {
425 #(self.#field_positions.rotate_by_anchor(angle, axis, anchor);)*
426 self
427 }
428
429 fn scale_by_anchor(&mut self, scale: #ranim::glam::DVec3, anchor: #ranim::components::Anchor) -> &mut Self {
430 #(self.#field_positions.scale_by_anchor(scale, anchor);)*
431 self
432 }
433 }
434 },
435 )
436}
437
438#[proc_macro_derive(PointsFunc)]
439pub fn derive_point_func(input: TokenStream) -> TokenStream {
440 impl_derive(
441 input,
442 |ranim| {
443 quote! {#ranim::traits::PointsFunc}
444 },
445 |_ranim, field_positions| {
446 quote! {
447 fn apply_points_func(&mut self, f: impl for<'a> Fn(&'a mut [DVec3])) -> &mut Self {
448 #(self.#field_positions.apply_points_func(f);)*
449 self
450 }
451 }
452 },
453 )
454}
455
456fn impl_derive(
457 input: TokenStream,
458 trait_path: impl Fn(&proc_macro2::TokenStream) -> proc_macro2::TokenStream,
459 impl_token: impl Fn(
460 &proc_macro2::TokenStream,
461 Vec<proc_macro2::TokenStream>,
462 ) -> proc_macro2::TokenStream,
463) -> TokenStream {
464 let ranim = ranim_path();
465 let input = parse_macro_input!(input as DeriveInput);
466 let name = &input.ident;
467 let generics = &input.generics;
468 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
469
470 let fields = match &input.data {
471 Data::Struct(data) => &data.fields,
472 _ => panic!("Can only be derived for structs"),
473 };
474
475 let field_positions = get_field_positions(fields)
476 .ok_or("cannot get field from unit struct")
477 .unwrap();
478
479 let trait_path = trait_path(&ranim);
480 let impl_token = impl_token(&ranim, field_positions);
481 let expanded = quote! {
482 impl #impl_generics #trait_path for #name #ty_generics #where_clause {
483 #impl_token
484 }
485 };
486
487 TokenStream::from(expanded)
488}
489
490fn get_field_positions(fields: &Fields) -> Option<Vec<proc_macro2::TokenStream>> {
491 match fields {
492 Fields::Named(fields) => Some(
493 fields
494 .named
495 .iter()
496 .map(|f| {
497 let pos = &f.ident;
498 quote! { #pos }
499 })
500 .collect::<Vec<_>>(),
501 ),
502 Fields::Unnamed(fields) => Some(
503 (0..fields.unnamed.len())
504 .map(syn::Index::from)
505 .map(|i| {
506 quote! { #i }
507 })
508 .collect::<Vec<_>>(),
509 ),
510 Fields::Unit => None,
511 }
512}