diff --git a/Form1.cs b/Form1.cs deleted file mode 100644 index c1f138b..0000000 --- a/Form1.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace ParsonsLoveheartAnimation -{ - public partial class Form1 : Form - { - public Form1() - { - InitializeComponent(); - } - } -} diff --git a/Form1.Designer.cs b/ParticleCanvas.Designer.cs similarity index 97% rename from Form1.Designer.cs rename to ParticleCanvas.Designer.cs index 22387fe..2d23bee 100644 --- a/Form1.Designer.cs +++ b/ParticleCanvas.Designer.cs @@ -1,6 +1,6 @@ namespace ParsonsLoveheartAnimation { - partial class Form1 + partial class ParticleCanvas { /// /// Required designer variable. diff --git a/ParticleCanvas.cs b/ParticleCanvas.cs new file mode 100644 index 0000000..af38102 --- /dev/null +++ b/ParticleCanvas.cs @@ -0,0 +1,146 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Numerics; +using System.Windows.Forms; +using System.Diagnostics; + +namespace ParsonsLoveheartAnimation +{ + public partial class ParticleCanvas : Form + { + private List _particles = new List(); + private System.Windows.Forms.Timer _renderTimer; + private Random _random = new Random(); + private DateTime _startTime; + + public ParticleCanvas() + { + this.Text = "WinForms Particle Tweening"; + this.Size = new Size(800, 600); + this.BackColor = Color.FromArgb(20, 20, 20); + + // Step 1: Enable Double Buffering to prevent tearing/flickering + this.DoubleBuffered = true; + + InitializeParticles(); + + // Step 4: The Render Loop + _startTime = DateTime.Now; + _renderTimer = new System.Windows.Forms.Timer(); + _renderTimer.Interval = 16; // ~60 FPS + _renderTimer.Tick += (s, e) => this.Invalidate(); // Triggers the Paint event + _renderTimer.Start(); + } + + private void InitializeParticles() + { + // Step 2: Mocking the SVG Path using GraphicsPath + GraphicsPath path = new GraphicsPath(); + // Adding a simple curve to represent the SVG path + path.AddBezier(100, 300, 300, 100, 500, 500, 700, 300); + path.Flatten(null, 0.25f); // Flattens curve into points + + PointF[] pathPoints = path.PathPoints; + + // Center points based on the JS code (600/2, 552/2) + Vector3 startPos = new Vector3(300f, 276f, 0f); + + // Step 3: Iterate through points (Simulating the JS for-loop) + for (int i = 0; i < pathPoints.Length; i++) + { + PointF p = pathPoints[i]; + + // Construct final vector with random noise + Vector3 targetPos = new Vector3(p.X, p.Y, 0); + targetPos.X += ((float)_random.NextDouble() - 0.5f) * 30f; + targetPos.Y += ((float)_random.NextDouble() - 0.5f) * 30f; + targetPos.Z += ((float)_random.NextDouble() - 0.5f) * 70f; + + float delay = i * 0.05f; // Scaled up slightly from 0.002 for visibility + float duration = (float)(_random.NextDouble() * 3.0 + 2.0); // Random 2 to 5 seconds + + _particles.Add(new Particle + { + StartPosition = startPos, + TargetPosition = targetPos, + CurrentPosition = startPos, // Starts at the origin + Delay = delay, + Duration = duration + }); + } + } + + protected override void OnPaint(PaintEventArgs e) + { + base.OnPaint(e); + Graphics g = e.Graphics; + g.SmoothingMode = SmoothingMode.AntiAlias; + + float elapsedTotalTime = (float)(DateTime.Now - _startTime).TotalSeconds; + + // Simple Painter's Algorithm: Sort by Z-axis so items in front draw last + _particles.Sort((a, b) => a.CurrentPosition.Z.CompareTo(b.CurrentPosition.Z)); + + foreach (var p in _particles) + { + // Calculate how far along the animation timeline we are (0.0 to 1.0) + float timePassed = elapsedTotalTime - p.Delay; + float progress = 0f; + + if (timePassed > 0) + { + progress = Math.Min(timePassed / p.Duration, 1f); + } + + // Apply power2.inOut Easing + float easeProgress = EaseInOutQuad(progress); + + // Lerp Current Position + p.CurrentPosition = Vector3.Lerp(p.StartPosition, p.TargetPosition, easeProgress); + + // Simulate 3D Depth via projection scaling + // Z goes roughly from -35 to +35 based on the noise logic + float scale = 1.0f + (p.CurrentPosition.Z / 100f); + float size = Math.Max(1f, 4f * scale); // Base size of 4 pixels + + // Draw the point + using (SolidBrush brush = new SolidBrush(Color.DeepSkyBlue)) + { + g.FillEllipse(brush, + p.CurrentPosition.X - (size / 2), + p.CurrentPosition.Y - (size / 2), + size, size); + } + } + } + + // Mathematical equivalent of GSAP's "power2.inOut" + private float EaseInOutQuad(float x) + { + return x < 0.5f ? 2f * x * x : 1f - (float)Math.Pow(-2f * x + 2f, 2f) / 2f; + } + } + + public class Particle + { + public Vector3 StartPosition; + public Vector3 TargetPosition; + public Vector3 CurrentPosition; + public float Delay; + public float Duration; + } + + // Standard WinForms entry point + //static class Program + //{ + // [STAThread] + // static void Main() + // { + // Application.EnableVisualStyles(); + // Application.SetCompatibleTextRenderingDefault(false); + // Application.Run(new ParticleCanvas()); + // } + //} +} \ No newline at end of file diff --git a/Form1.resx b/ParticleCanvas.resx similarity index 100% rename from Form1.resx rename to ParticleCanvas.resx diff --git a/Program.cs b/Program.cs index 4126752..fa35e62 100644 --- a/Program.cs +++ b/Program.cs @@ -8,10 +8,10 @@ namespace ParsonsLoveheartAnimation [STAThread] static void Main() { - // To customize application configuration such as set high DPI settings or default font, - // see https://aka.ms/applicationconfiguration. + //Application.EnableVisualStyles(); + //Application.SetCompatibleTextRenderingDefault(false); ApplicationConfiguration.Initialize(); - Application.Run(new Form1()); + Application.Run(new ParticleCanvas()); } } } \ No newline at end of file