1use serde_json::{json, Value};
17
18pub fn chemical_crystallization(cx: f64, cy: f64, num_crystals: usize,
34 max_radius: f64, t: f64, growth_rate: f64) -> Vec<Value> {
35 let num_crystals = num_crystals.clamp(5, 40);
36 let t = t.clamp(0.0, 1.0);
37 let growth_rate = growth_rate.clamp(0.3, 5.0);
38 let mut result = Vec::new();
39
40 for i in 0..num_crystals {
41 let angle = (i as f64 / num_crystals as f64) * std::f64::consts::PI * 2.0 + i as f64 * 0.5;
42 let dist_factor = i as f64 / num_crystals as f64;
44 let radius = max_radius * t.powf(growth_rate) * (1.0 - dist_factor * 0.5);
45 let x = cx + angle.cos() * radius;
46 let y = cy + angle.sin() * radius;
47 let size = 3.0 + 8.0 * t.powf(growth_rate * 0.8);
48 let alpha = (t * 2.0).min(1.0);
49
50 result.push(json!({
51 "type": "crystal",
52 "x": x, "y": y,
53 "size": size,
54 "angle": angle,
55 "alpha": alpha,
56 "color": "#AADDFF"
57 }));
58 }
59
60 result
61}
62
63pub fn cell_division(cx: f64, cy: f64, initial_radius: f64,
79 division_time: f64, max_divisions: usize, t: f64) -> Vec<Value> {
80 let max_divisions = max_divisions.clamp(1, 8);
81 let mut cells = vec![(cx, cy, initial_radius, 0usize)];
82
83 for div in 0..max_divisions {
84 let div_t = division_time * (div as f64 + 1.0);
85 if t >= div_t {
86 let progress = ((t - div_t) / division_time).min(1.0);
87 let mut new_cells = Vec::new();
88
89 for (ccx, ccy, r, _) in &cells {
90 if progress > 0.5 {
91 let offset = r * 0.3 * (progress - 0.5) * 2.0;
93 let new_r = r * 0.7;
94 new_cells.push((*ccx - offset, *ccy, new_r, div + 1));
95 new_cells.push((*ccx + offset, *ccy, new_r, div + 1));
96 } else {
97 let new_r = r * (1.0 + progress * 0.5);
99 new_cells.push((*ccx, *ccy, new_r, div));
100 }
101 }
102
103 cells = new_cells;
104 }
105 }
106
107 cells.iter().map(|(x, y, r, _)| json!({
108 "type": "cell",
109 "x": x, "y": y,
110 "radius": r,
111 "alpha": 0.8,
112 "color": "#44DD88"
113 })).collect()
114}
115
116pub fn walk_cycle(cx: f64, cy: f64, body_size: f64, num_legs: usize,
133 stride: f64, t: f64, leg_phase_offset: f64) -> Vec<Value> {
134 let num_legs = num_legs.clamp(2, 8);
135 let t = t % 1.0;
136 let mut result = Vec::new();
137
138 let body_y = cy + 3.0 * (t * std::f64::consts::PI * 4.0).sin();
140 result.push(json!({
141 "type": "body",
142 "x": cx, "y": body_y,
143 "size": body_size,
144 "color": "#CC8844"
145 }));
146
147 for i in 0..num_legs {
149 let phase = t + (i as f64 / num_legs as f64) * leg_phase_offset;
150 let leg_angle = (phase * std::f64::consts::PI * 2.0).sin() * stride;
151 let _side = if i < num_legs / 2 { 1.0 } else { -1.0 };
152 let leg_idx = i % (num_legs / 2);
153 let leg_x = cx + (leg_idx as f64 - (num_legs / 4) as f64) * body_size * 0.4;
154
155 let foot_x = leg_x + leg_angle;
156 let foot_y = body_y + body_size + leg_angle.abs() * 0.3;
157
158 result.push(json!({
159 "type": "leg",
160 "x1": leg_x, "y1": body_y,
161 "x2": foot_x, "y2": foot_y,
162 "color": "#AA6633"
163 }));
164 }
165
166 result
167}
168
169pub fn flight_pattern(cx: f64, cy: f64, wingspan: f64, flap_speed: f64, t: f64) -> Vec<Value> {
184 let flap = (t * flap_speed * std::f64::consts::PI * 2.0).sin();
185 let wing_angle = flap * 0.6; let mut result = Vec::new();
188
189 result.push(json!({
191 "type": "body",
192 "x": cx, "y": cy,
193 "size": 10.0,
194 "color": "#4488CC"
195 }));
196
197 let left_wing_x = cx - wingspan / 2.0 * wing_angle.cos();
199 let left_wing_y = cy - wingspan / 2.0 * wing_angle.sin();
200 result.push(json!({
201 "type": "wing",
202 "x1": cx, "y1": cy,
203 "x2": left_wing_x, "y2": left_wing_y,
204 "color": "#66AADD"
205 }));
206
207 let right_wing_x = cx + wingspan / 2.0 * wing_angle.cos();
209 let right_wing_y = cy - wingspan / 2.0 * wing_angle.sin();
210 result.push(json!({
211 "type": "wing",
212 "x1": cx, "y1": cy,
213 "x2": right_wing_x, "y2": right_wing_y,
214 "color": "#66AADD"
215 }));
216
217 result
218}
219
220pub fn lsystem_tree(base_x: f64, base_y: f64, trunk_length: f64,
237 branch_angle: f64, length_ratio: f64, max_depth: usize, t: f64) -> Vec<Value> {
238 let max_depth = max_depth.clamp(2, 10);
239 let t = t.clamp(0.0, 1.0);
240 let mut result = Vec::new();
241
242 fn grow_branch(x: f64, y: f64, angle: f64, length: f64,
243 depth: usize, max_depth: usize, t: f64,
244 branch_angle: f64, length_ratio: f64,
245 result: &mut Vec<Value>) {
246 if depth == 0 || t < (depth as f64 / max_depth as f64) {
247 return;
248 }
249
250 let progress = ((t - (depth as f64 / max_depth as f64)) * max_depth as f64).min(1.0);
251 let current_length = length * progress;
252
253 let end_x = x + angle.cos() * current_length;
254 let end_y = y + angle.sin() * current_length;
255
256 let color = if depth > max_depth / 2 { "#8B4513" } else { "#228B22" };
258
259 result.push(json!({
260 "type": "branch",
261 "x1": x, "y1": y,
262 "x2": end_x, "y2": end_y,
263 "depth": depth,
264 "color": color
265 }));
266
267 if depth > 1 {
268 grow_branch(end_x, end_y, angle - branch_angle,
269 length * length_ratio, depth - 1, max_depth, t,
270 branch_angle, length_ratio, result);
271 grow_branch(end_x, end_y, angle + branch_angle,
272 length * length_ratio, depth - 1, max_depth, t,
273 branch_angle, length_ratio, result);
274 }
275 }
276
277 grow_branch(base_x, base_y, -std::f64::consts::PI / 2.0,
278 trunk_length, max_depth, max_depth, t, branch_angle, length_ratio, &mut result);
279
280 result
281}
282
283pub fn tusi_couple(cx: f64, cy: f64, large_radius: f64, t: f64) -> Vec<Value> {
300 let small_radius = large_radius / 2.0;
301 let mut result = Vec::new();
302
303 result.push(json!({
305 "type": "large_circle",
306 "x": cx, "y": cy,
307 "radius": large_radius,
308 "color": "#444466"
309 }));
310
311 let small_cx = cx + small_radius * (t * 2.0).cos();
313 let small_cy = cy + small_radius * (t * 2.0).sin();
314
315 result.push(json!({
317 "type": "small_circle",
318 "x": small_cx, "y": small_cy,
319 "radius": small_radius,
320 "color": "#666688"
321 }));
322
323 let point_x = small_cx + small_radius * (-t * 2.0).cos();
325 let point_y = small_cy + small_radius * (-t * 2.0).sin();
326
327 result.push(json!({
328 "type": "point",
329 "x": point_x, "y": point_y,
330 "size": 6.0,
331 "color": "#FF4444"
332 }));
333
334 result.push(json!({
336 "type": "trace_line",
337 "x1": cx - large_radius, "y1": cy,
338 "x2": cx + large_radius, "y2": cy,
339 "color": "#FF8888"
340 }));
341
342 result
343}
344
345pub fn pendulum_waves(base_x: f64, base_y: f64, num_pendulums: usize,
361 pendulum_length: f64, freq_spread: f64, t: f64) -> Vec<Value> {
362 let num_pendulums = num_pendulums.clamp(6, 30);
363 let mut result = Vec::new();
364 let spacing = (base_x * 2.0 / num_pendulums as f64).min(40.0);
365
366 for i in 0..num_pendulums {
367 let px = base_x + i as f64 * spacing - (num_pendulums as f64 * spacing / 2.0);
368 let freq = 1.0 + i as f64 * freq_spread;
369 let angle = 0.5 * (t * freq * std::f64::consts::PI * 2.0).sin();
370
371 let bob_x = px + pendulum_length * angle.sin();
372 let bob_y = base_y + pendulum_length * angle.cos();
373
374 let hue = (i as f64 / num_pendulums as f64 + t * 0.1) % 1.0;
376 let color = format!("hsl({}, 80%, 60%)", (hue * 360.0) as usize);
377
378 result.push(json!({
379 "type": "pendulum",
380 "x1": px, "y1": base_y,
381 "x2": bob_x, "y2": bob_y,
382 "bob_x": bob_x, "bob_y": bob_y,
383 "color": color
384 }));
385 }
386
387 result
388}
389
390pub fn wave_interference(cx1: f64, cy1: f64, cx2: f64, cy2: f64,
407 wavelength: f64, amplitude: f64,
408 grid_resolution: usize, t: f64) -> Vec<Value> {
409 let grid_resolution = grid_resolution.clamp(8, 60);
410 let mut result = Vec::new();
411
412 let width = 600.0;
413 let height = 400.0;
414 let start_x = 100.0;
415 let start_y = 100.0;
416 let step_x = width / grid_resolution as f64;
417 let step_y = height / grid_resolution as f64;
418
419 for iy in 0..=grid_resolution {
420 for ix in 0..=grid_resolution {
421 let x = start_x + ix as f64 * step_x;
422 let y = start_y + iy as f64 * step_y;
423
424 let d1 = ((x - cx1) * (x - cx1) + (y - cy1) * (y - cy1)).sqrt();
426 let d2 = ((x - cx2) * (x - cx2) + (y - cy2) * (y - cy2)).sqrt();
427
428 let w1 = amplitude * (d1 * std::f64::consts::PI * 2.0 / wavelength - t * 3.0).sin();
430 let w2 = amplitude * (d2 * std::f64::consts::PI * 2.0 / wavelength - t * 3.0).sin();
431
432 let combined = (w1 + w2) / 2.0;
434 let normalized = (combined / amplitude + 1.0) / 2.0; let color = if normalized < 0.5 {
438 let t = normalized * 2.0;
439 format!("rgb({}, {}, {})", (t * 100.0) as u8, (t * 100.0) as u8, 255)
440 } else {
441 let t = (normalized - 0.5) * 2.0;
442 format!("rgb(255, {}, {})", (255.0 - t * 155.0) as u8, (255.0 - t * 200.0) as u8)
443 };
444
445 result.push(json!({
446 "type": "wave_point",
447 "x": x, "y": y,
448 "amplitude": combined,
449 "color": color
450 }));
451 }
452 }
453
454 result.push(json!({ "type": "source", "x": cx1, "y": cy1, "color": "#00FFFF" }));
456 result.push(json!({ "type": "source", "x": cx2, "y": cy2, "color": "#FF00FF" }));
457
458 result
459}
460
461#[cfg(test)]
466mod tests {
467 use super::*;
468
469 #[test]
470 fn test_chemical_crystallization() {
471 let result = chemical_crystallization(400.0, 300.0, 12, 100.0, 0.5, 1.5);
472 assert_eq!(result.len(), 12);
473 assert!(result[0].get("type").is_some());
474 }
475
476 #[test]
477 fn test_cell_division() {
478 let result = cell_division(400.0, 300.0, 30.0, 1.0, 3, 2.5);
479 assert!(result.len() >= 2); }
481
482 #[test]
483 fn test_walk_cycle() {
484 let result = walk_cycle(400.0, 300.0, 20.0, 4, 15.0, 0.3, 0.25);
485 assert!(result.len() > 4); }
487
488 #[test]
489 fn test_flight_pattern() {
490 let result = flight_pattern(400.0, 300.0, 80.0, 5.0, 0.25);
491 assert_eq!(result.len(), 3); }
493
494 #[test]
495 fn test_lsystem_tree() {
496 let result = lsystem_tree(400.0, 500.0, 80.0, 0.5, 0.7, 4, 1.0);
497 assert!(!result.is_empty());
498 assert!(result.len() >= 3); }
500
501 #[test]
502 fn test_tusi_couple() {
503 let result = tusi_couple(400.0, 300.0, 100.0, 0.5);
504 assert!(result.len() >= 4); }
506
507 #[test]
508 fn test_pendulum_waves() {
509 let result = pendulum_waves(400.0, 100.0, 12, 100.0, 0.05, 0.5);
510 assert_eq!(result.len(), 12);
511 }
512
513 #[test]
514 fn test_wave_interference() {
515 let result = wave_interference(250.0, 300.0, 550.0, 300.0, 40.0, 1.0, 15, 0.5);
516 assert!(!result.is_empty());
517 assert!(result.len() > 2);
519 }
520}