Skip to main content

proof_engine/curves/
templates.rs

1//! Pre-built curve entity templates.
2
3use glam::{Vec2, Vec3, Vec4};
4use super::entity_curves::{CurveEntity, EntityCurve, CurveType};
5use crate::math::MathFunction;
6use std::f32::consts::{PI, TAU, FRAC_PI_2};
7
8pub struct CurveTemplates;
9
10impl CurveTemplates {
11    /// Humanoid mage: torso loop, arms as Lissajous, head as circle, staff as line.
12    pub fn mage(position: Vec3) -> CurveEntity {
13        let mut ent = CurveEntity::new("Mage", position);
14        let blue = Vec4::new(0.3, 0.5, 1.0, 0.9);
15        let light_blue = Vec4::new(0.5, 0.7, 1.0, 0.8);
16        let gold = Vec4::new(1.0, 0.8, 0.3, 0.9);
17
18        // Torso: closed Bezier loop
19        ent.add_curve(EntityCurve::new(CurveType::Bezier { degree: 3 }, vec![
20            Vec2::new(0.0, 0.8), Vec2::new(-0.4, 0.5), Vec2::new(-0.3, -0.2),
21            Vec2::new(0.0, -0.4), Vec2::new(0.3, -0.2), Vec2::new(0.4, 0.5), Vec2::new(0.0, 0.8),
22        ]).with_color(blue).with_emission(1.2).with_thickness(0.04).with_closed(true).with_stiffness(2.0));
23
24        // Head: distorted circle
25        ent.add_curve(EntityCurve::new(
26            CurveType::Circle { radius: 0.25, distortion: Some(MathFunction::Sine { amplitude: 0.05, frequency: 3.0, phase: 0.0 }) },
27            vec![Vec2::new(0.0, 1.1)],
28        ).with_color(light_blue).with_emission(1.5).with_thickness(0.035).with_stiffness(2.5));
29
30        // Left arm: Lissajous
31        ent.add_curve(EntityCurve::new(
32            CurveType::Lissajous { a: 2.0, b: 3.0, delta: FRAC_PI_2 },
33            vec![Vec2::new(-0.5, 0.5), Vec2::new(0.3, 0.4)],
34        ).with_color(light_blue).with_emission(0.8).with_thickness(0.025).with_segments(48));
35
36        // Right arm: Lissajous (mirror)
37        ent.add_curve(EntityCurve::new(
38            CurveType::Lissajous { a: 2.0, b: 3.0, delta: -FRAC_PI_2 },
39            vec![Vec2::new(0.5, 0.5), Vec2::new(0.3, 0.4)],
40        ).with_color(light_blue).with_emission(0.8).with_thickness(0.025).with_segments(48));
41
42        // Staff: parametric line
43        ent.add_curve(EntityCurve::new(CurveType::Bezier { degree: 1 }, vec![
44            Vec2::new(0.6, 0.3), Vec2::new(0.7, 1.3),
45        ]).with_color(gold).with_emission(0.6).with_thickness(0.02).with_stiffness(3.0));
46
47        // Staff orb: small circle at top
48        ent.add_curve(EntityCurve::new(
49            CurveType::Circle { radius: 0.1, distortion: Some(MathFunction::Breathing { rate: 2.0, depth: 0.3 }) },
50            vec![Vec2::new(0.7, 1.4)],
51        ).with_color(Vec4::new(0.8, 0.9, 1.0, 1.0)).with_emission(2.5).with_thickness(0.03).with_stiffness(1.5));
52
53        // Legs: two Bezier curves
54        ent.add_curve(EntityCurve::new(CurveType::Bezier { degree: 2 }, vec![
55            Vec2::new(-0.1, -0.4), Vec2::new(-0.2, -0.8), Vec2::new(-0.3, -1.2),
56        ]).with_color(blue).with_emission(0.6).with_thickness(0.025));
57        ent.add_curve(EntityCurve::new(CurveType::Bezier { degree: 2 }, vec![
58            Vec2::new(0.1, -0.4), Vec2::new(0.2, -0.8), Vec2::new(0.3, -1.2),
59        ]).with_color(blue).with_emission(0.6).with_thickness(0.025));
60
61        ent
62    }
63
64    /// Beast: hypotrochoid body, Bezier legs, spiral tail.
65    pub fn beast(position: Vec3) -> CurveEntity {
66        let mut ent = CurveEntity::new("Beast", position);
67        let red = Vec4::new(0.9, 0.2, 0.3, 0.9);
68        let dark_red = Vec4::new(0.7, 0.1, 0.2, 0.8);
69
70        // Body: hypotrochoid
71        ent.add_curve(EntityCurve::new(
72            CurveType::Hypotrochoid { big_r: 1.0, small_r: 0.4, d: 0.6 },
73            vec![Vec2::ZERO],
74        ).with_color(red).with_emission(1.0).with_thickness(0.04).with_stiffness(1.5));
75
76        // Head: superellipse (squarish)
77        ent.add_curve(EntityCurve::new(
78            CurveType::Superellipse { a: 0.3, b: 0.25, n: 3.0 },
79            vec![Vec2::new(0.0, 0.8)],
80        ).with_color(red).with_emission(1.3).with_thickness(0.035));
81
82        // 4 legs
83        for i in 0..4 {
84            let x = (i as f32 - 1.5) * 0.4;
85            ent.add_curve(EntityCurve::new(CurveType::Bezier { degree: 2 }, vec![
86                Vec2::new(x, -0.3), Vec2::new(x - 0.1, -0.7), Vec2::new(x, -1.0),
87            ]).with_color(dark_red).with_emission(0.5).with_thickness(0.025));
88        }
89
90        // Tail: spiral
91        ent.add_curve(EntityCurve::new(
92            CurveType::Spiral { rate: 0.15, decay: 0.3 },
93            vec![Vec2::new(-0.8, 0.0)],
94        ).with_color(dark_red).with_emission(0.7).with_thickness(0.02).with_segments(80));
95
96        ent
97    }
98
99    /// Elemental: rose curves forming a radial mandala pattern.
100    pub fn elemental(position: Vec3) -> CurveEntity {
101        let mut ent = CurveEntity::new("Elemental", position);
102        ent.breath_amplitude = 0.06; // breathes heavily
103
104        let colors = [
105            Vec4::new(1.0, 0.4, 0.1, 0.85), // fire
106            Vec4::new(1.0, 0.6, 0.2, 0.8),
107            Vec4::new(1.0, 0.8, 0.3, 0.7),
108        ];
109
110        // Multiple rose curves with different k values
111        for i in 0..3 {
112            let k = (i + 3) as f32;
113            ent.add_curve(EntityCurve::new(
114                CurveType::Rose { k, amplitude: 0.8 - i as f32 * 0.15 },
115                vec![Vec2::ZERO],
116            ).with_color(colors[i]).with_emission(1.5 - i as f32 * 0.3)
117             .with_thickness(0.035 - i as f32 * 0.005).with_segments(96).with_stiffness(1.0));
118        }
119
120        // Inner glow circle
121        ent.add_curve(EntityCurve::new(
122            CurveType::Circle { radius: 0.15, distortion: Some(MathFunction::Breathing { rate: 3.0, depth: 0.5 }) },
123            vec![Vec2::ZERO],
124        ).with_color(Vec4::new(1.0, 0.9, 0.5, 1.0)).with_emission(3.0).with_thickness(0.04));
125
126        ent
127    }
128
129    /// Boss: multiple overlapping Lissajous figures forming a complex mandala.
130    pub fn boss(position: Vec3) -> CurveEntity {
131        let mut ent = CurveEntity::new("Boss", position);
132        ent.breath_rate = 0.25;
133        ent.breath_amplitude = 0.04;
134
135        let purple = Vec4::new(0.7, 0.2, 1.0, 0.9);
136        let dark_purple = Vec4::new(0.5, 0.1, 0.8, 0.8);
137        let gold = Vec4::new(1.0, 0.8, 0.2, 0.85);
138
139        // Outer mandala: 4 Lissajous figures rotated
140        for i in 0..4 {
141            let phase = i as f32 * PI * 0.5;
142            ent.add_curve(EntityCurve::new(
143                CurveType::Lissajous { a: 3.0 + i as f32 * 0.5, b: 2.0 + i as f32 * 0.3, delta: phase },
144                vec![Vec2::ZERO, Vec2::splat(1.2 - i as f32 * 0.15)],
145            ).with_color(if i % 2 == 0 { purple } else { dark_purple })
146             .with_emission(1.2).with_thickness(0.03).with_segments(128));
147        }
148
149        // Inner hypotrochoid
150        ent.add_curve(EntityCurve::new(
151            CurveType::Hypotrochoid { big_r: 0.8, small_r: 0.3, d: 0.5 },
152            vec![Vec2::ZERO],
153        ).with_color(gold).with_emission(2.0).with_thickness(0.04).with_segments(96));
154
155        // Crown: rose curve
156        ent.add_curve(EntityCurve::new(
157            CurveType::Rose { k: 6.0, amplitude: 1.5 },
158            vec![Vec2::ZERO],
159        ).with_color(purple).with_emission(0.8).with_thickness(0.02).with_segments(128)
160         .with_dash(0.1, 0.05));
161
162        // Eyes: two small circles
163        ent.add_curve(EntityCurve::new(
164            CurveType::Circle { radius: 0.08, distortion: None },
165            vec![Vec2::new(-0.2, 0.3)],
166        ).with_color(Vec4::new(1.0, 0.9, 0.1, 1.0)).with_emission(2.5).with_thickness(0.03));
167        ent.add_curve(EntityCurve::new(
168            CurveType::Circle { radius: 0.08, distortion: None },
169            vec![Vec2::new(0.2, 0.3)],
170        ).with_color(Vec4::new(1.0, 0.9, 0.1, 1.0)).with_emission(2.5).with_thickness(0.03));
171
172        ent
173    }
174
175    /// Simple circle entity for testing.
176    pub fn simple_circle(position: Vec3, radius: f32, color: Vec4) -> CurveEntity {
177        let mut ent = CurveEntity::new("Circle", position);
178        ent.add_curve(EntityCurve::new(
179            CurveType::Circle { radius, distortion: None },
180            vec![Vec2::ZERO],
181        ).with_color(color).with_emission(1.0).with_thickness(0.03));
182        ent
183    }
184}
185
186#[cfg(test)]
187mod tests {
188    use super::*;
189
190    #[test]
191    fn test_mage_template() {
192        let mage = CurveTemplates::mage(Vec3::ZERO);
193        assert!(mage.curves.len() >= 7, "mage should have multiple curves");
194        assert!(mage.alive);
195    }
196
197    #[test]
198    fn test_boss_template() {
199        let boss = CurveTemplates::boss(Vec3::ZERO);
200        assert!(boss.curves.len() >= 7, "boss should be complex");
201    }
202
203    #[test]
204    fn test_elemental_template() {
205        let elem = CurveTemplates::elemental(Vec3::ZERO);
206        assert!(elem.curves.len() >= 3);
207    }
208}