├── .gitattributes
├── .gitignore
├── Core
├── Atom.cs
├── EventArgs.cs
├── Exceptions.cs
├── Extensions.cs
├── Force.cs
├── Particle.cs
└── Universe.cs
├── FrmAtom.Designer.cs
├── FrmAtom.cs
├── FrmAtom.resx
├── FrmSettings.Designer.cs
├── FrmSettings.cs
├── FrmSettings.resx
├── FrmSimulation.Designer.cs
├── FrmSimulation.cs
├── FrmSimulation.resx
├── ParticleLifeSimulation.csproj
├── ParticleLifeSimulation.sln
├── Program.cs
├── Properties
├── Resources.Designer.cs
├── Resources.resx
└── launchSettings.json
├── README.md
├── Resources
├── arrow_bottom.ico
└── arrow_right.ico
├── Settings.cs
├── UserControls
├── Accordion.cs
├── Slider.Designer.cs
└── Slider.cs
└── images
└── screenshot.png
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Mono auto generated files
17 | mono_crash.*
18 |
19 | # Build results
20 | [Dd]ebug/
21 | [Dd]ebugPublic/
22 | [Rr]elease/
23 | [Rr]eleases/
24 | x64/
25 | x86/
26 | [Ww][Ii][Nn]32/
27 | [Aa][Rr][Mm]/
28 | [Aa][Rr][Mm]64/
29 | bld/
30 | [Bb]in/
31 | [Oo]bj/
32 | [Oo]ut/
33 | [Ll]og/
34 | [Ll]ogs/
35 |
36 | # Visual Studio 2015/2017 cache/options directory
37 | .vs/
38 | # Uncomment if you have tasks that create the project's static files in wwwroot
39 | #wwwroot/
40 |
41 | # Visual Studio 2017 auto generated files
42 | Generated\ Files/
43 |
44 | # MSTest test Results
45 | [Tt]est[Rr]esult*/
46 | [Bb]uild[Ll]og.*
47 |
48 | # NUnit
49 | *.VisualState.xml
50 | TestResult.xml
51 | nunit-*.xml
52 |
53 | # Build Results of an ATL Project
54 | [Dd]ebugPS/
55 | [Rr]eleasePS/
56 | dlldata.c
57 |
58 | # Benchmark Results
59 | BenchmarkDotNet.Artifacts/
60 |
61 | # .NET Core
62 | project.lock.json
63 | project.fragment.lock.json
64 | artifacts/
65 |
66 | # ASP.NET Scaffolding
67 | ScaffoldingReadMe.txt
68 |
69 | # StyleCop
70 | StyleCopReport.xml
71 |
72 | # Files built by Visual Studio
73 | *_i.c
74 | *_p.c
75 | *_h.h
76 | *.ilk
77 | *.meta
78 | *.obj
79 | *.iobj
80 | *.pch
81 | *.pdb
82 | *.ipdb
83 | *.pgc
84 | *.pgd
85 | *.rsp
86 | *.sbr
87 | *.tlb
88 | *.tli
89 | *.tlh
90 | *.tmp
91 | *.tmp_proj
92 | *_wpftmp.csproj
93 | *.log
94 | *.vspscc
95 | *.vssscc
96 | .builds
97 | *.pidb
98 | *.svclog
99 | *.scc
100 |
101 | # Chutzpah Test files
102 | _Chutzpah*
103 |
104 | # Visual C++ cache files
105 | ipch/
106 | *.aps
107 | *.ncb
108 | *.opendb
109 | *.opensdf
110 | *.sdf
111 | *.cachefile
112 | *.VC.db
113 | *.VC.VC.opendb
114 |
115 | # Visual Studio profiler
116 | *.psess
117 | *.vsp
118 | *.vspx
119 | *.sap
120 |
121 | # Visual Studio Trace Files
122 | *.e2e
123 |
124 | # TFS 2012 Local Workspace
125 | $tf/
126 |
127 | # Guidance Automation Toolkit
128 | *.gpState
129 |
130 | # ReSharper is a .NET coding add-in
131 | _ReSharper*/
132 | *.[Rr]e[Ss]harper
133 | *.DotSettings.user
134 |
135 | # TeamCity is a build add-in
136 | _TeamCity*
137 |
138 | # DotCover is a Code Coverage Tool
139 | *.dotCover
140 |
141 | # AxoCover is a Code Coverage Tool
142 | .axoCover/*
143 | !.axoCover/settings.json
144 |
145 | # Coverlet is a free, cross platform Code Coverage Tool
146 | coverage*.json
147 | coverage*.xml
148 | coverage*.info
149 |
150 | # Visual Studio code coverage results
151 | *.coverage
152 | *.coveragexml
153 |
154 | # NCrunch
155 | _NCrunch_*
156 | .*crunch*.local.xml
157 | nCrunchTemp_*
158 |
159 | # MightyMoose
160 | *.mm.*
161 | AutoTest.Net/
162 |
163 | # Web workbench (sass)
164 | .sass-cache/
165 |
166 | # Installshield output folder
167 | [Ee]xpress/
168 |
169 | # DocProject is a documentation generator add-in
170 | DocProject/buildhelp/
171 | DocProject/Help/*.HxT
172 | DocProject/Help/*.HxC
173 | DocProject/Help/*.hhc
174 | DocProject/Help/*.hhk
175 | DocProject/Help/*.hhp
176 | DocProject/Help/Html2
177 | DocProject/Help/html
178 |
179 | # Click-Once directory
180 | publish/
181 |
182 | # Publish Web Output
183 | *.[Pp]ublish.xml
184 | *.azurePubxml
185 | # Note: Comment the next line if you want to checkin your web deploy settings,
186 | # but database connection strings (with potential passwords) will be unencrypted
187 | *.pubxml
188 | *.publishproj
189 |
190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
191 | # checkin your Azure Web App publish settings, but sensitive information contained
192 | # in these scripts will be unencrypted
193 | PublishScripts/
194 |
195 | # NuGet Packages
196 | *.nupkg
197 | # NuGet Symbol Packages
198 | *.snupkg
199 | # The packages folder can be ignored because of Package Restore
200 | **/[Pp]ackages/*
201 | # except build/, which is used as an MSBuild target.
202 | !**/[Pp]ackages/build/
203 | # Uncomment if necessary however generally it will be regenerated when needed
204 | #!**/[Pp]ackages/repositories.config
205 | # NuGet v3's project.json files produces more ignorable files
206 | *.nuget.props
207 | *.nuget.targets
208 |
209 | # Microsoft Azure Build Output
210 | csx/
211 | *.build.csdef
212 |
213 | # Microsoft Azure Emulator
214 | ecf/
215 | rcf/
216 |
217 | # Windows Store app package directories and files
218 | AppPackages/
219 | BundleArtifacts/
220 | Package.StoreAssociation.xml
221 | _pkginfo.txt
222 | *.appx
223 | *.appxbundle
224 | *.appxupload
225 |
226 | # Visual Studio cache files
227 | # files ending in .cache can be ignored
228 | *.[Cc]ache
229 | # but keep track of directories ending in .cache
230 | !?*.[Cc]ache/
231 |
232 | # Others
233 | ClientBin/
234 | ~$*
235 | *~
236 | *.dbmdl
237 | *.dbproj.schemaview
238 | *.jfm
239 | *.pfx
240 | *.publishsettings
241 | orleans.codegen.cs
242 |
243 | # Including strong name files can present a security risk
244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
245 | #*.snk
246 |
247 | # Since there are multiple workflows, uncomment next line to ignore bower_components
248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
249 | #bower_components/
250 |
251 | # RIA/Silverlight projects
252 | Generated_Code/
253 |
254 | # Backup & report files from converting an old project file
255 | # to a newer Visual Studio version. Backup files are not needed,
256 | # because we have git ;-)
257 | _UpgradeReport_Files/
258 | Backup*/
259 | UpgradeLog*.XML
260 | UpgradeLog*.htm
261 | ServiceFabricBackup/
262 | *.rptproj.bak
263 |
264 | # SQL Server files
265 | *.mdf
266 | *.ldf
267 | *.ndf
268 |
269 | # Business Intelligence projects
270 | *.rdl.data
271 | *.bim.layout
272 | *.bim_*.settings
273 | *.rptproj.rsuser
274 | *- [Bb]ackup.rdl
275 | *- [Bb]ackup ([0-9]).rdl
276 | *- [Bb]ackup ([0-9][0-9]).rdl
277 |
278 | # Microsoft Fakes
279 | FakesAssemblies/
280 |
281 | # GhostDoc plugin setting file
282 | *.GhostDoc.xml
283 |
284 | # Node.js Tools for Visual Studio
285 | .ntvs_analysis.dat
286 | node_modules/
287 |
288 | # Visual Studio 6 build log
289 | *.plg
290 |
291 | # Visual Studio 6 workspace options file
292 | *.opt
293 |
294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
295 | *.vbw
296 |
297 | # Visual Studio LightSwitch build output
298 | **/*.HTMLClient/GeneratedArtifacts
299 | **/*.DesktopClient/GeneratedArtifacts
300 | **/*.DesktopClient/ModelManifest.xml
301 | **/*.Server/GeneratedArtifacts
302 | **/*.Server/ModelManifest.xml
303 | _Pvt_Extensions
304 |
305 | # Paket dependency manager
306 | .paket/paket.exe
307 | paket-files/
308 |
309 | # FAKE - F# Make
310 | .fake/
311 |
312 | # CodeRush personal settings
313 | .cr/personal
314 |
315 | # Python Tools for Visual Studio (PTVS)
316 | __pycache__/
317 | *.pyc
318 |
319 | # Cake - Uncomment if you are using it
320 | # tools/**
321 | # !tools/packages.config
322 |
323 | # Tabs Studio
324 | *.tss
325 |
326 | # Telerik's JustMock configuration file
327 | *.jmconfig
328 |
329 | # BizTalk build output
330 | *.btp.cs
331 | *.btm.cs
332 | *.odx.cs
333 | *.xsd.cs
334 |
335 | # OpenCover UI analysis results
336 | OpenCover/
337 |
338 | # Azure Stream Analytics local run output
339 | ASALocalRun/
340 |
341 | # MSBuild Binary and Structured Log
342 | *.binlog
343 |
344 | # NVidia Nsight GPU debugger configuration file
345 | *.nvuser
346 |
347 | # MFractors (Xamarin productivity tool) working folder
348 | .mfractor/
349 |
350 | # Local History for Visual Studio
351 | .localhistory/
352 |
353 | # BeatPulse healthcheck temp database
354 | healthchecksdb
355 |
356 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
357 | MigrationBackup/
358 |
359 | # Ionide (cross platform F# VS Code tools) working folder
360 | .ionide/
361 |
362 | # Fody - auto-generated XML schema
363 | FodyWeavers.xsd
--------------------------------------------------------------------------------
/Core/Atom.cs:
--------------------------------------------------------------------------------
1 | namespace ParticleLifeSimulation.Core
2 | {
3 | public class Atom : IDisposable
4 | {
5 | #region Events
6 | ///
7 | /// Occurs when the name of the atom has been changed.
8 | ///
9 | public event EventHandler? AtomNameChanged;
10 | ///
11 | /// Occurs when the color of the atom has been changed.
12 | ///
13 | public event EventHandler? AtomColorChanged;
14 | ///
15 | /// Occurs when the radius of the atom has been changed.
16 | ///
17 | public event EventHandler? AtomRadiusChanged;
18 | ///
19 | /// Occurs when the forces of the atom have been cleared.
20 | ///
21 | public event EventHandler? AtomForcesCleared;
22 | ///
23 | /// Occurs when force of the atom have been added.
24 | ///
25 | public event EventHandler? AtomForceAdded;
26 | ///
27 | /// Occurs when force(s) of the atom have been removed.
28 | ///
29 | public event EventHandler? AtomForceRemoved;
30 | ///
31 | /// Occurs when particle of the atom have been reset.
32 | ///
33 | public event EventHandler? AtomParticlesReset;
34 | ///
35 | /// Occurs when particle of the atom have been added.
36 | ///
37 | public event EventHandler? AtomParticlesAdded;
38 | ///
39 | /// Occurs when particle of the atom have been removed.
40 | ///
41 | public event EventHandler? AtomParticlesRemoved;
42 | ///
43 | /// Occurs when particle of the atom have been changed.
44 | ///
45 | public event EventHandler? AtomParticlesChanged;
46 | #endregion
47 |
48 | #region Properties
49 | public List Forces { get; private set; }
50 | public List Particles { get; private set; }
51 | public string Name
52 | {
53 | get => name;
54 | set
55 | {
56 | if (name != value)
57 | {
58 | name = value;
59 | AtomNameChanged?.Invoke(this, EventArgs.Empty);
60 | }
61 | }
62 | }
63 | private string name;
64 | public Color Color
65 | {
66 | get => color;
67 | set
68 | {
69 | if (color != value)
70 | {
71 | color = value;
72 | AtomColorChanged?.Invoke(this, EventArgs.Empty);
73 | }
74 | }
75 | }
76 | private Color color;
77 | public double Radius
78 | {
79 | get => radius;
80 | set
81 | {
82 | if (radius != value)
83 | {
84 | radius = value;
85 | AtomRadiusChanged?.Invoke(this, EventArgs.Empty);
86 | }
87 | }
88 | }
89 | private double radius;
90 | public double Diameter
91 | {
92 | get => radius * 2.0;
93 | set
94 | {
95 | if (radius != value / 2.0)
96 | {
97 | radius = value / 2.0;
98 | AtomRadiusChanged?.Invoke(this, EventArgs.Empty);
99 | }
100 | }
101 | }
102 | public double MaxWidth { get; private set; }
103 | public double MaxHeight { get; private set; }
104 | #endregion
105 |
106 | #region Constructors
107 | public Atom(string name, Color color, double maxWidth, double maxHeight, double radius)
108 | {
109 | Forces = new();
110 | Particles = new();
111 | this.name = name;
112 | this.color = color;
113 | this.radius = radius;
114 | MaxWidth = maxWidth;
115 | MaxHeight = maxHeight;
116 | }
117 | public Atom(string name, Color color, double maxWidth, double maxHeight, double radius, int particleNumber) : this(name, color, maxWidth, maxHeight, radius) => AddParticles(particleNumber);
118 | #endregion
119 |
120 | #region Atom
121 | public void SetMaxSize(double maxWidth, double maxHeight)
122 | {
123 | MaxWidth = maxWidth;
124 | MaxHeight = maxHeight;
125 | }
126 | #endregion
127 |
128 | #region Particles
129 | public void AddParticles(IEnumerable particles)
130 | {
131 | foreach (Particle particle in particles)
132 | Particles.Add(particle);
133 | AtomParticlesAdded?.Invoke(this, new(particles));
134 | }
135 | public void AddParticles(int number)
136 | {
137 | double xOffSet = MaxWidth * 0.1;
138 | double yOffSet = MaxHeight * 0.1;
139 | List particlesAdded = new();
140 | for (int i = 0; i < number; i++)
141 | {
142 | double x = (Random.Shared.NextDoubleInclusive() * (MaxWidth - (2 * xOffSet))) + xOffSet;
143 | double y = (Random.Shared.NextDoubleInclusive() * (MaxHeight - (2 * yOffSet))) + yOffSet;
144 | Particle particle = new(x, y);
145 | particlesAdded.Add(particle);
146 | }
147 | AddParticles(particlesAdded);
148 | }
149 |
150 | public void RemoveParticles(int number)
151 | {
152 | if (number < 0 || number > Particles.Count)
153 | number = Math.Clamp(number, 0, Particles.Count);
154 | List particlesRemoved = new();
155 | for (int i = 0; i < number; i++)
156 | {
157 | int index = Random.Shared.Next(Particles.Count);
158 | Particle particleToRemove = Particles[index];
159 | while (particlesRemoved.Contains(particleToRemove))
160 | {
161 | index = Random.Shared.Next(Particles.Count);
162 | particleToRemove = Particles[index];
163 | }
164 | particlesRemoved.Add(particleToRemove);
165 | }
166 | RemoveParticles(particlesRemoved);
167 | }
168 | public void RemoveParticles(IEnumerable particles)
169 | {
170 | foreach (Particle particle in particles)
171 | Particles.Remove(particle);
172 | AtomParticlesRemoved?.Invoke(this, new(particles));
173 | }
174 |
175 | public void ClearParticles()
176 | {
177 | Particles.Clear();
178 | AtomParticlesChanged?.Invoke(this, EventArgs.Empty);
179 | }
180 | public void UpdateParticles(int newCount)
181 | {
182 | int diffCount = newCount - Particles.Count;
183 | if (diffCount > 0)
184 | AddParticles(diffCount);
185 | else if (diffCount < 0)
186 | RemoveParticles(Math.Abs(diffCount));
187 | AtomParticlesChanged?.Invoke(this, EventArgs.Empty);
188 | }
189 |
190 | public void ResetParticle(Particle particle)
191 | {
192 | double xOffSet = MaxWidth * 0.1;
193 | double yOffSet = MaxHeight * 0.1;
194 | double x = (Random.Shared.NextDoubleInclusive() * (MaxWidth - (2 * xOffSet))) + xOffSet;
195 | double y = (Random.Shared.NextDoubleInclusive() * (MaxHeight - (2 * yOffSet))) + yOffSet;
196 | particle.X = x;
197 | particle.Y = y;
198 | particle.VX = 0;
199 | particle.VY = 0;
200 | }
201 | public void ResetParticles(IEnumerable particles)
202 | {
203 | foreach (Particle particle in particles)
204 | ResetParticle(particle);
205 | AtomParticlesReset?.Invoke(this, new(particles));
206 | }
207 | public void ResetParticles() => ResetParticles(Particles);
208 | #endregion
209 |
210 | #region Forces
211 | public bool HasForceWith(Atom target) => Forces.FirstOrDefault(force => force.Target == target) is not null;
212 | public Force GetForceWith(Atom target) => Forces.Single(force => force.Target == target);
213 | public void AddForce(Force force)
214 | {
215 | if (!Forces.Contains(force))
216 | {
217 | Forces.Add(force);
218 | AtomForceAdded?.Invoke(this, new(force));
219 | }
220 | }
221 | public void AddForces(IEnumerable forces)
222 | {
223 | foreach (Force force in forces) AddForce(force);
224 | }
225 | public void RemoveForce(Force force)
226 | {
227 | Forces.Remove(force);
228 | force.Dispose();
229 | AtomForceRemoved?.Invoke(this, new(force));
230 | }
231 | public void RemoveForceWith(Atom target)
232 | {
233 | if (HasForceWith(target)) RemoveForce(GetForceWith(target));
234 | }
235 | public void RemoveForces(IEnumerable forces)
236 | {
237 | foreach (Force force in forces) RemoveForce(force);
238 | }
239 | public void ClearForces()
240 | {
241 | foreach (Force force in Forces) force.Dispose();
242 | Forces.Clear();
243 | AtomForcesCleared?.Invoke(this, EventArgs.Empty);
244 | }
245 | #endregion
246 |
247 | #region Finalize
248 | public void Dispose()
249 | {
250 | ClearForces();
251 | ClearParticles();
252 |
253 | AtomNameChanged = null;
254 | AtomColorChanged = null;
255 | AtomRadiusChanged = null;
256 |
257 | AtomForcesCleared = null;
258 | AtomForceAdded = null;
259 | AtomForceRemoved = null;
260 |
261 | AtomParticlesReset = null;
262 | AtomParticlesAdded = null;
263 | AtomParticlesRemoved = null;
264 | AtomParticlesChanged = null;
265 |
266 | GC.SuppressFinalize(this);
267 | }
268 | #endregion
269 | }
270 | }
271 |
--------------------------------------------------------------------------------
/Core/EventArgs.cs:
--------------------------------------------------------------------------------
1 | namespace ParticleLifeSimulation.Core
2 | {
3 | #region EventArgs
4 | ///
5 | /// Contain Universe event data.
6 | ///
7 | ///
8 | public class UniverseEventArgs : EventArgs
9 | {
10 | ///
11 | /// Gets the particle affected.
12 | ///
13 | ///
14 | /// The particle affected.
15 | ///
16 | public Particle? Particle { get; private set; }
17 | ///
18 | /// Gets the atom affected.
19 | ///
20 | ///
21 | /// The atom affected.
22 | ///
23 | public Atom? Atom { get; private set; }
24 | ///
25 | /// Gets the force affected.
26 | ///
27 | ///
28 | /// The force affected.
29 | ///
30 | public Force? Force { get; private set; }
31 | ///
32 | /// Initializes a new instance of the class.
33 | ///
34 | /// The particle affected.
35 | public UniverseEventArgs(Particle particle) => Particle = particle;
36 | ///
37 | /// Initializes a new instance of the class.
38 | ///
39 | /// The atom affected.
40 | public UniverseEventArgs(Atom atom) => Atom = atom;
41 | ///
42 | /// Initializes a new instance of the class.
43 | ///
44 | /// The force affected.
45 | public UniverseEventArgs(Force force) => Force = force;
46 | }
47 | ///
48 | /// Contain Particle event data.
49 | ///
50 | ///
51 | public class ParticleEventArgs : EventArgs
52 | {
53 | ///
54 | /// Gets the atom affected.
55 | ///
56 | ///
57 | /// The atom affected.
58 | ///
59 | public Atom Atom { get; private set; }
60 | ///
61 | /// Initializes a new instance of the class.
62 | ///
63 | /// The atom affected.
64 | public ParticleEventArgs(Atom atom) => Atom = atom;
65 | }
66 | ///
67 | /// Contain Atom event data.
68 | ///
69 | ///
70 | public class AtomEventArgs : EventArgs
71 | {
72 | ///
73 | /// Gets the force affected.
74 | ///
75 | ///
76 | /// The force affected.
77 | ///
78 | public Force? Force { get; private set; }
79 | ///
80 | /// Gets the particles affected.
81 | ///
82 | ///
83 | /// The particles affected.
84 | ///
85 | public IEnumerable? Particles { get; private set; }
86 | ///
87 | /// Initializes a new instance of the class.
88 | ///
89 | /// The force affected.
90 | public AtomEventArgs() { }
91 | ///
92 | /// Initializes a new instance of the class.
93 | ///
94 | /// The force affected.
95 | public AtomEventArgs(Force force) => Force = force;
96 | ///
97 | /// Initializes a new instance of the class.
98 | ///
99 | /// The particles affected.
100 | public AtomEventArgs(IEnumerable particles) => Particles = particles;
101 | }
102 | ///
103 | /// Contain Force event data.
104 | ///
105 | ///
106 | public class ForceEventArgs : EventArgs
107 | {
108 | ///
109 | /// Initializes a new instance of the class.
110 | ///
111 | public ForceEventArgs() { }
112 | }
113 | #endregion
114 | }
115 |
--------------------------------------------------------------------------------
/Core/Exceptions.cs:
--------------------------------------------------------------------------------
1 | namespace ParticleLifeSimulation.Core
2 | {
3 | #region Exceptions
4 | ///
5 | /// The exception that is thrown when a method call is invalid for the object's current state.
6 | ///
7 | ///
8 | public class InvalidUniverseException : InvalidOperationException
9 | {
10 | ///
11 | /// Initializes a new instance of the class.
12 | ///
13 | /// The message exception.
14 | public InvalidUniverseException(string messageException) : base(messageException) { }
15 | }
16 | ///
17 | /// The exception that is thrown when a method call is invalid for the object's current state.
18 | ///
19 | ///
20 | public class InvalidParticleException : InvalidOperationException
21 | {
22 | ///
23 | /// Initializes a new instance of the class.
24 | ///
25 | /// The message exception.
26 | public InvalidParticleException(string messageException) : base(messageException) { }
27 | }
28 | ///
29 | /// The exception that is thrown when a method call is invalid for the object's current state.
30 | ///
31 | ///
32 | public class InvalidAtomException : InvalidOperationException
33 | {
34 | ///
35 | /// Initializes a new instance of the class.
36 | ///
37 | /// The message exception.
38 | public InvalidAtomException(string messageException) : base(messageException) { }
39 | }
40 | ///
41 | /// The exception that is thrown when a method call is invalid for the object's current state.
42 | ///
43 | ///
44 | public class InvalidForceException : InvalidOperationException
45 | {
46 | ///
47 | /// Initializes a new instance of the class.
48 | ///
49 | /// The message exception.
50 | public InvalidForceException(string messageException) : base(messageException) { }
51 | }
52 | #endregion
53 | }
54 |
--------------------------------------------------------------------------------
/Core/Extensions.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 |
3 | namespace ParticleLifeSimulation.Core
4 | {
5 | public static class Extensions
6 | {
7 | #region Particle
8 | ///
9 | /// Converts to clone particle.
10 | ///
11 | /// The particle to clone.
12 | /// Clone of particle.
13 | public static Particle ToClone(this Particle particle) => new(particle.X, particle.Y);
14 | #endregion
15 |
16 | #region Atom
17 | ///
18 | /// Converts to clone atom.
19 | ///
20 | /// The atom to clone.
21 | /// Clone of atom.
22 | public static Atom ToClone(this Atom atom) => new(atom.Name, atom.Color, atom.MaxWidth, atom.MaxHeight, atom.Radius);
23 | #endregion
24 |
25 | #region Force
26 | ///
27 | /// Converts to clone force.
28 | ///
29 | /// The force to clone.
30 | /// Clone of force.
31 | public static Force ToClone(this Force force) => new(force.Target, force.Radiation, force.Attraction);
32 | #endregion
33 |
34 | #region Random
35 | ///
36 | /// Gets random double with range [0.0, 1.0] (inclusive).
37 | ///
38 | /// Pseudo-random number generator.
39 | /// double [0.0, 1.0] (inclusive).
40 | public static double NextDoubleInclusive(this Random? random) => (random ?? Random.Shared).Next() * (1.0 / (int.MaxValue - 1.0));
41 | ///
42 | /// Gets random double with range [min, max] (inclusive).
43 | ///
44 | /// Pseudo-random number generator.
45 | /// Minimum double.
46 | /// Maximum double.
47 | /// double [min, max] (inclusive).
48 | public static double NextDoubleInclusive(this Random? random, double min, double max) => (random.NextDoubleInclusive() * (max - min)) + min;
49 | #endregion
50 |
51 | #region Colors
52 | public static Color GetRandomKnownColor(bool systemColorsIncluded = false, bool compoundColorNamesIncluded = false) => GetKnownColors(systemColorsIncluded, compoundColorNamesIncluded).ElementAt(Random.Shared.Next(GetKnownColors(systemColorsIncluded, compoundColorNamesIncluded).Count()));
53 | public static IEnumerable GetKnownColors(bool systemColorsIncluded = false, bool compoundColorNamesIncluded = false)
54 | {
55 | IEnumerable knownsColors = Enum.GetValues(typeof(KnownColor)).Cast().Select(knownColor => Color.FromKnownColor(knownColor));
56 | IEnumerable systemColorNames = typeof(SystemColors).GetRuntimeProperties().Select(systemColor => systemColor.Name);
57 | if (!systemColorsIncluded) // SystemColors
58 | knownsColors = knownsColors.Where(knownColor => !systemColorNames.Contains(knownColor.Name));
59 | if (!compoundColorNamesIncluded) // Compound Color Name
60 | knownsColors = knownsColors.Where(knownColor => knownColor.Name.Count(character => char.IsUpper(character)) == 1);
61 | return knownsColors.Where(color => color != Color.Transparent); // Of cours !
62 | }
63 | #endregion
64 | }
65 | }
--------------------------------------------------------------------------------
/Core/Force.cs:
--------------------------------------------------------------------------------
1 | namespace ParticleLifeSimulation.Core
2 | {
3 | public class Force : IDisposable
4 | {
5 | #region Events
6 | ///
7 | /// Occurs when the force name have changed.
8 | ///
9 | public event EventHandler? ForceNameChanged;
10 | ///
11 | /// Occurs when the attraction value have changed.
12 | ///
13 | public event EventHandler? ForceAttractionChanged;
14 | ///
15 | /// Occurs when the radiation value have changed.
16 | ///
17 | public event EventHandler? ForceRadiationChanged;
18 | #endregion
19 |
20 | #region Constants
21 | private const double MINVALUE = -1.0;
22 | private const double MAXVALUE = 1.0;
23 | #endregion
24 |
25 | #region Properties
26 | public Atom Target { get; private set; }
27 | public string Name { get => Target.Name; }
28 | public double Radiation
29 | {
30 | get => radiation;
31 | set
32 | {
33 | if (radiation != Math.Clamp(value, 0.0, MAXVALUE))
34 | {
35 | radiation = Math.Clamp(value, 0.0, MAXVALUE);
36 | ForceRadiationChanged?.Invoke(this, new());
37 | }
38 | }
39 | }
40 | private double radiation;
41 | public double Attraction
42 | {
43 | get => attraction;
44 | set
45 | {
46 | if (attraction != Math.Clamp(value, MINVALUE, MAXVALUE))
47 | {
48 | attraction = Math.Clamp(value, MINVALUE, MAXVALUE);
49 | ForceAttractionChanged?.Invoke(this, new());
50 | }
51 | }
52 | }
53 | private double attraction;
54 | #endregion
55 |
56 | #region Constructors
57 | public Force(Atom target, double radiation, double attraction)
58 | {
59 | this.radiation = radiation;
60 | this.attraction = attraction;
61 | Target = target;
62 | target.AtomNameChanged += Force_AtomNameChanged;
63 | }
64 | #endregion
65 |
66 | #region Force
67 | private void Force_AtomNameChanged(object? sender, EventArgs e)
68 | {
69 | ForceNameChanged?.Invoke(this, new());
70 | }
71 | #endregion
72 |
73 | #region Finalize
74 | public void Dispose()
75 | {
76 | Target.AtomNameChanged -= Force_AtomNameChanged;
77 |
78 | ForceNameChanged = null;
79 | ForceRadiationChanged = null;
80 | ForceAttractionChanged = null;
81 |
82 | GC.SuppressFinalize(this);
83 | }
84 | #endregion
85 | }
86 | }
--------------------------------------------------------------------------------
/Core/Particle.cs:
--------------------------------------------------------------------------------
1 | namespace ParticleLifeSimulation.Core
2 | {
3 | public class Particle
4 | {
5 | public double VX { get; set; }
6 | public double VY { get; set; }
7 | public double X { get; set; }
8 | public double Y { get; set; }
9 |
10 | public Particle(double x, double y)
11 | {
12 | X = x;
13 | Y = y;
14 | VX = 0.0;
15 | VY = 0.0;
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/Core/Universe.cs:
--------------------------------------------------------------------------------
1 | namespace ParticleLifeSimulation.Core
2 | {
3 | public class Universe
4 | {
5 | #region Events
6 | //Universe's Events
7 | public event EventHandler? UniverseSizeChanged;
8 | public event EventHandler? UniverseWrapChanged;
9 | public event EventHandler? UniverseFrictionChanged;
10 | public event EventHandler? UniverseFlatForceChanged;
11 | //Particle's Events
12 | public event EventHandler? UniverseParticleAdded;
13 | public event EventHandler? UniverseParticleChanged;
14 | public event EventHandler? UniverseParticleRemoved;
15 | //Atom's Events
16 | public event EventHandler? UniverseAtomAdded;
17 | public event EventHandler? UniverseAtomChanged;
18 | public event EventHandler? UniverseAtomRemoved;
19 | //Force's Events
20 | public event EventHandler? UniverseForceAdded;
21 | public event EventHandler? UniverseForceChanged;
22 | public event EventHandler? UniverseForceRemoved;
23 | #endregion
24 |
25 | #region Properties
26 | public SizeF Size
27 | {
28 | get => new((float)Width, (float)Height);
29 | set
30 | {
31 | if (Width != value.Width || Height != value.Height)
32 | {
33 | Width = value.Width;
34 | Height = value.Height;
35 | if (Atoms != null) foreach (Atom atom in Atoms) atom.SetMaxSize(Width, Height);
36 | UniverseSizeChanged?.Invoke(this, EventArgs.Empty);
37 | }
38 | }
39 | }
40 | public double Width { get; private set; }
41 | public double Height { get; private set; }
42 | public bool Wrap
43 | {
44 | get => wrap;
45 | set
46 | {
47 | if (wrap != value)
48 | {
49 | wrap = value;
50 | UniverseWrapChanged?.Invoke(this, EventArgs.Empty);
51 | }
52 | }
53 | }
54 | private bool wrap;
55 | public bool FlatForce
56 | {
57 | get => flatForce;
58 | set
59 | {
60 | if (flatForce != value)
61 | {
62 | flatForce = value;
63 | UniverseFlatForceChanged?.Invoke(this, EventArgs.Empty);
64 | }
65 | }
66 | }
67 | private bool flatForce;
68 | public double Friction
69 | {
70 | get => friction;
71 | set
72 | {
73 | if (value != friction)
74 | {
75 | friction = value;
76 | UniverseFrictionChanged?.Invoke(this, EventArgs.Empty);
77 | }
78 | }
79 | }
80 | private double friction = 0.5;
81 | public List Atoms { get; private set; }
82 | #endregion
83 |
84 | #region Constructors
85 | public Universe(double width, double height)
86 | {
87 | Atoms = new();
88 |
89 | Width = width;
90 | Height = height;
91 | }
92 | #endregion
93 |
94 | #region Universe
95 | public void Step()
96 | {
97 | //For each Atom
98 | Parallel.ForEach(Atoms, (Atom atomSource) =>
99 | //foreach (Atom atomSource in Atoms)
100 | {
101 | //With each Atom
102 | Parallel.ForEach(Atoms, (Atom atomTarget) =>
103 | //foreach (Atom atomTarget in Atoms)
104 | {
105 | //Get forces between AtomSource and AtomTarget
106 | double g = atomSource.Forces.Find(force => force.Target == atomTarget)?.Attraction ?? double.NaN;
107 | if (double.IsNaN(g)) return; //continue;
108 |
109 | //For every Particles of AtomSource
110 | //Parallel.For(0, atomSource.Particles.Count, (int i) =>
111 | for (int i = 0; i < atomSource.Particles.Count; i++)
112 | {
113 | Particle a = atomSource.Particles[i];
114 | double fx = 0;
115 | double fy = 0;
116 | //With every Particles of Atom2
117 | //Parallel.For(0, atomTarget.Particles.Count, (int j) =>
118 | for (int j = 0; j < atomTarget.Particles.Count; j++)
119 | {
120 | Particle b = atomTarget.Particles[j];
121 |
122 | //Calculate delta (distance)
123 | double dx = a.X - b.X;
124 | double dy = a.Y - b.Y;
125 |
126 | //If Wrapping
127 | if (Wrap)
128 | {
129 | if (dx > Width * 0.5)
130 | {
131 | dx -= Width;
132 | }
133 | else if (dx < -Width * 0.5)
134 | {
135 | dx += Width;
136 | }
137 |
138 | if (dy > Height * 0.5)
139 | {
140 | dy -= Height;
141 | }
142 | else if (dy < -Height * 0.5)
143 | {
144 | dy += Height;
145 | }
146 | }
147 |
148 | // Get distance squared
149 | double r2 = (dx * dx) + (dy * dy);
150 |
151 | //// Normalize displacement
152 | //double r = Math.Sqrt(r2);
153 | //dx /= r;
154 | //dy /= r;
155 |
156 | //Calculate the intensity of force
157 | double d = Math.Sqrt(r2);
158 | if (d > 0 && d < 80)
159 | {
160 | double F = g * 1 / d;
161 | fx += F * dx;
162 | fy += F * dy;
163 | }
164 | }
165 |
166 | //Apply force to Particles velocity
167 | a.VX = (a.VX + fx) * (1.0 - friction);// * 0.5;
168 | a.VY = (a.VY + fy) * (1.0 - friction);// * 0.5;
169 | a.X += a.VX;
170 | a.Y += a.VY;
171 | //a.VX *= 1.0 - friction;
172 | //a.VY *= 1.0 - friction;
173 |
174 | //Apply Edge Collision or Wrapping to Particles position
175 | //If Wrapping,
176 | if (Wrap)
177 | {
178 | if (a.X < 0)
179 | {
180 | a.X += Width;
181 | //a.Position = new((float)(a.X + Width), (float)a.Y);
182 | }
183 | else if (a.X >= Width)
184 | {
185 | a.X -= Width;
186 | //a.Position = new((float)(a.X - Width), (float)a.Y);
187 | }
188 |
189 | if (a.Y < 0)
190 | {
191 | //a.Position = new((float)a.X, (float)(a.Y + Height));
192 | a.Y += Height;
193 | }
194 | else if (a.Y >= Height)
195 | {
196 | //a.Position = new((float)a.X, (float)(a.Y - Height));
197 | a.Y -= Height;
198 | }
199 | }
200 | else
201 | {
202 | if (a.X < atomSource.Diameter)
203 | {
204 | //a.Velocity = new((float)-a.VX, (float)a.VY);
205 | //a.Position = new((float)atomSource.Diameter, (float)a.Y);
206 | a.VX = -a.VX;
207 | a.X = atomSource.Diameter;
208 | }
209 | else if (a.X >= Width - atomSource.Diameter)
210 | {
211 | //a.Velocity = new((float)-a.VX, (float)a.VY);
212 | //a.Position = new((float)(Width - atomSource.Diameter), (float)a.Y);
213 | a.VX = -a.VX;
214 | a.X = Width - atomSource.Diameter;
215 | }
216 |
217 | if (a.Y < atomSource.Diameter)
218 | {
219 | //a.Velocity = new((float)a.VX, (float)-a.VY);
220 | //a.Position = new((float)a.X, (float)atomSource.Diameter);
221 | a.VY = -a.VY;
222 | a.Y = atomSource.Diameter;
223 | }
224 | else if (a.Y >= Height - atomSource.Diameter)
225 | {
226 | //a.Velocity = new((float)a.VX, (float)-a.VY);
227 | //a.Position = new((float)a.X, (float)(Height - atomSource.Diameter));
228 | a.VY = -a.VY;
229 | a.Y = Height - atomSource.Diameter;
230 | }
231 | }
232 | }
233 | });
234 | });
235 | }
236 | #endregion
237 |
238 | #region Atoms
239 | public void AddAtom(Atom atom)
240 | {
241 | if (!Atoms.Contains(atom))
242 | {
243 | Atoms.Add(atom);
244 | UniverseAtomAdded?.Invoke(this, new UniverseEventArgs(atom));
245 | }
246 | }
247 | public void RemoveAtom(Atom atom)
248 | {
249 | if (Atoms.Contains(atom))
250 | {
251 | atom.Dispose();
252 | Atoms.Remove(atom);
253 | UniverseAtomRemoved?.Invoke(this, new(atom));
254 | }
255 | }
256 | #endregion
257 |
258 | #region Particles
259 | public void ResetParticles() => Atoms.ForEach(atom => atom.ResetParticles());
260 | public int ParticlesCount() => Atoms.Sum(atom => atom.Particles.Count);
261 | #endregion
262 | }
263 | }
--------------------------------------------------------------------------------
/FrmAtom.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace ParticleLifeSimulation
2 | {
3 | partial class FrmAtom
4 | {
5 | ///
6 | /// Required designer variable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Clean up any resources being used.
12 | ///
13 | /// true if managed resources should be disposed; otherwise, false.
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Windows Form Designer generated code
24 |
25 | ///
26 | /// Required method for Designer support - do not modify
27 | /// the contents of this method with the code editor.
28 | ///
29 | private void InitializeComponent()
30 | {
31 | this.BtnOk = new System.Windows.Forms.Button();
32 | this.BtnCancel = new System.Windows.Forms.Button();
33 | this.GrpAtomSettings = new System.Windows.Forms.GroupBox();
34 | this.LblParticles = new System.Windows.Forms.Label();
35 | this.NumParticles = new System.Windows.Forms.NumericUpDown();
36 | this.LblRadius = new System.Windows.Forms.Label();
37 | this.BtnPicker = new System.Windows.Forms.Button();
38 | this.NumRadius = new System.Windows.Forms.NumericUpDown();
39 | this.LblColor = new System.Windows.Forms.Label();
40 | this.LblName = new System.Windows.Forms.Label();
41 | this.TxtName = new System.Windows.Forms.TextBox();
42 | this.PicColor = new System.Windows.Forms.PictureBox();
43 | this.GrpAtomForces = new System.Windows.Forms.GroupBox();
44 | this.GrpForce = new System.Windows.Forms.GroupBox();
45 | this.BtnRandomRadiation = new System.Windows.Forms.Button();
46 | this.NumRadiation = new System.Windows.Forms.NumericUpDown();
47 | this.LblRadiation = new System.Windows.Forms.Label();
48 | this.LblTargetName = new System.Windows.Forms.Label();
49 | this.BtnRandomAttraction = new System.Windows.Forms.Button();
50 | this.LblAttraction = new System.Windows.Forms.Label();
51 | this.NumAttraction = new System.Windows.Forms.NumericUpDown();
52 | this.BtnSave = new System.Windows.Forms.Button();
53 | this.LblTarget = new System.Windows.Forms.Label();
54 | this.LstForces = new System.Windows.Forms.ListBox();
55 | this.GrpAtomSettings.SuspendLayout();
56 | ((System.ComponentModel.ISupportInitialize)(this.NumParticles)).BeginInit();
57 | ((System.ComponentModel.ISupportInitialize)(this.NumRadius)).BeginInit();
58 | ((System.ComponentModel.ISupportInitialize)(this.PicColor)).BeginInit();
59 | this.GrpAtomForces.SuspendLayout();
60 | this.GrpForce.SuspendLayout();
61 | ((System.ComponentModel.ISupportInitialize)(this.NumRadiation)).BeginInit();
62 | ((System.ComponentModel.ISupportInitialize)(this.NumAttraction)).BeginInit();
63 | this.SuspendLayout();
64 | //
65 | // BtnOk
66 | //
67 | this.BtnOk.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
68 | this.BtnOk.Location = new System.Drawing.Point(423, 330);
69 | this.BtnOk.Name = "BtnOk";
70 | this.BtnOk.Size = new System.Drawing.Size(47, 30);
71 | this.BtnOk.TabIndex = 14;
72 | this.BtnOk.Text = "OK";
73 | this.BtnOk.UseVisualStyleBackColor = true;
74 | this.BtnOk.Click += new System.EventHandler(this.BtnOk_Click);
75 | //
76 | // BtnCancel
77 | //
78 | this.BtnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
79 | this.BtnCancel.Location = new System.Drawing.Point(337, 330);
80 | this.BtnCancel.Name = "BtnCancel";
81 | this.BtnCancel.Size = new System.Drawing.Size(80, 30);
82 | this.BtnCancel.TabIndex = 13;
83 | this.BtnCancel.Text = "CANCEL";
84 | this.BtnCancel.UseVisualStyleBackColor = true;
85 | //
86 | // GrpAtomSettings
87 | //
88 | this.GrpAtomSettings.Controls.Add(this.LblParticles);
89 | this.GrpAtomSettings.Controls.Add(this.NumParticles);
90 | this.GrpAtomSettings.Controls.Add(this.LblRadius);
91 | this.GrpAtomSettings.Controls.Add(this.BtnPicker);
92 | this.GrpAtomSettings.Controls.Add(this.NumRadius);
93 | this.GrpAtomSettings.Controls.Add(this.LblColor);
94 | this.GrpAtomSettings.Controls.Add(this.LblName);
95 | this.GrpAtomSettings.Controls.Add(this.TxtName);
96 | this.GrpAtomSettings.Controls.Add(this.PicColor);
97 | this.GrpAtomSettings.Location = new System.Drawing.Point(12, 12);
98 | this.GrpAtomSettings.Name = "GrpAtomSettings";
99 | this.GrpAtomSettings.Size = new System.Drawing.Size(458, 109);
100 | this.GrpAtomSettings.TabIndex = 1;
101 | this.GrpAtomSettings.TabStop = false;
102 | this.GrpAtomSettings.Text = "Atom Settings";
103 | //
104 | // LblParticles
105 | //
106 | this.LblParticles.AutoSize = true;
107 | this.LblParticles.Location = new System.Drawing.Point(248, 69);
108 | this.LblParticles.Name = "LblParticles";
109 | this.LblParticles.Size = new System.Drawing.Size(57, 15);
110 | this.LblParticles.TabIndex = 8;
111 | this.LblParticles.Text = "Particles :";
112 | //
113 | // NumParticles
114 | //
115 | this.NumParticles.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
116 | this.NumParticles.Location = new System.Drawing.Point(310, 67);
117 | this.NumParticles.Maximum = new decimal(new int[] {
118 | 500,
119 | 0,
120 | 0,
121 | 0});
122 | this.NumParticles.Name = "NumParticles";
123 | this.NumParticles.Size = new System.Drawing.Size(131, 23);
124 | this.NumParticles.TabIndex = 3;
125 | //
126 | // LblRadius
127 | //
128 | this.LblRadius.AutoSize = true;
129 | this.LblRadius.Location = new System.Drawing.Point(19, 69);
130 | this.LblRadius.Name = "LblRadius";
131 | this.LblRadius.Size = new System.Drawing.Size(48, 15);
132 | this.LblRadius.TabIndex = 2;
133 | this.LblRadius.Text = "Radius :";
134 | //
135 | // BtnPicker
136 | //
137 | this.BtnPicker.Location = new System.Drawing.Point(343, 24);
138 | this.BtnPicker.Name = "BtnPicker";
139 | this.BtnPicker.Size = new System.Drawing.Size(98, 23);
140 | this.BtnPicker.TabIndex = 1;
141 | this.BtnPicker.Text = "Color Picker";
142 | this.BtnPicker.UseVisualStyleBackColor = true;
143 | //
144 | // NumRadius
145 | //
146 | this.NumRadius.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
147 | this.NumRadius.DecimalPlaces = 1;
148 | this.NumRadius.Location = new System.Drawing.Point(81, 67);
149 | this.NumRadius.Maximum = new decimal(new int[] {
150 | 10,
151 | 0,
152 | 0,
153 | 0});
154 | this.NumRadius.Minimum = new decimal(new int[] {
155 | 1,
156 | 0,
157 | 0,
158 | 65536});
159 | this.NumRadius.Name = "NumRadius";
160 | this.NumRadius.Size = new System.Drawing.Size(131, 23);
161 | this.NumRadius.TabIndex = 2;
162 | this.NumRadius.Value = new decimal(new int[] {
163 | 1,
164 | 0,
165 | 0,
166 | 65536});
167 | //
168 | // LblColor
169 | //
170 | this.LblColor.AutoSize = true;
171 | this.LblColor.Location = new System.Drawing.Point(248, 28);
172 | this.LblColor.Name = "LblColor";
173 | this.LblColor.Size = new System.Drawing.Size(42, 15);
174 | this.LblColor.TabIndex = 5;
175 | this.LblColor.Text = "Color :";
176 | //
177 | // LblName
178 | //
179 | this.LblName.AutoSize = true;
180 | this.LblName.Location = new System.Drawing.Point(19, 28);
181 | this.LblName.Name = "LblName";
182 | this.LblName.Size = new System.Drawing.Size(45, 15);
183 | this.LblName.TabIndex = 4;
184 | this.LblName.Text = "Name :";
185 | //
186 | // TxtName
187 | //
188 | this.TxtName.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
189 | this.TxtName.Location = new System.Drawing.Point(81, 25);
190 | this.TxtName.Name = "TxtName";
191 | this.TxtName.Size = new System.Drawing.Size(131, 23);
192 | this.TxtName.TabIndex = 0;
193 | //
194 | // PicColor
195 | //
196 | this.PicColor.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
197 | this.PicColor.Location = new System.Drawing.Point(310, 22);
198 | this.PicColor.Name = "PicColor";
199 | this.PicColor.Size = new System.Drawing.Size(27, 27);
200 | this.PicColor.TabIndex = 1;
201 | this.PicColor.TabStop = false;
202 | //
203 | // GrpAtomForces
204 | //
205 | this.GrpAtomForces.Controls.Add(this.GrpForce);
206 | this.GrpAtomForces.Controls.Add(this.LstForces);
207 | this.GrpAtomForces.Location = new System.Drawing.Point(12, 127);
208 | this.GrpAtomForces.Name = "GrpAtomForces";
209 | this.GrpAtomForces.Size = new System.Drawing.Size(458, 192);
210 | this.GrpAtomForces.TabIndex = 2;
211 | this.GrpAtomForces.TabStop = false;
212 | this.GrpAtomForces.Text = "Atom Forces";
213 | //
214 | // GrpForce
215 | //
216 | this.GrpForce.Controls.Add(this.BtnRandomRadiation);
217 | this.GrpForce.Controls.Add(this.NumRadiation);
218 | this.GrpForce.Controls.Add(this.LblRadiation);
219 | this.GrpForce.Controls.Add(this.LblTargetName);
220 | this.GrpForce.Controls.Add(this.BtnRandomAttraction);
221 | this.GrpForce.Controls.Add(this.LblAttraction);
222 | this.GrpForce.Controls.Add(this.NumAttraction);
223 | this.GrpForce.Controls.Add(this.BtnSave);
224 | this.GrpForce.Controls.Add(this.LblTarget);
225 | this.GrpForce.Location = new System.Drawing.Point(167, 22);
226 | this.GrpForce.Name = "GrpForce";
227 | this.GrpForce.Size = new System.Drawing.Size(274, 155);
228 | this.GrpForce.TabIndex = 1;
229 | this.GrpForce.TabStop = false;
230 | this.GrpForce.Text = "Force";
231 | //
232 | // BtnRandomRadiation
233 | //
234 | this.BtnRandomRadiation.Location = new System.Drawing.Point(195, 50);
235 | this.BtnRandomRadiation.Name = "BtnRandomRadiation";
236 | this.BtnRandomRadiation.Size = new System.Drawing.Size(61, 23);
237 | this.BtnRandomRadiation.TabIndex = 16;
238 | this.BtnRandomRadiation.Text = "Random";
239 | this.BtnRandomRadiation.UseVisualStyleBackColor = true;
240 | //
241 | // NumRadiation
242 | //
243 | this.NumRadiation.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
244 | this.NumRadiation.DecimalPlaces = 10;
245 | this.NumRadiation.Increment = new decimal(new int[] {
246 | 1,
247 | 0,
248 | 0,
249 | 65536});
250 | this.NumRadiation.Location = new System.Drawing.Point(88, 50);
251 | this.NumRadiation.Maximum = new decimal(new int[] {
252 | 1,
253 | 0,
254 | 0,
255 | 0});
256 | this.NumRadiation.Name = "NumRadiation";
257 | this.NumRadiation.Size = new System.Drawing.Size(101, 23);
258 | this.NumRadiation.TabIndex = 15;
259 | //
260 | // LblRadiation
261 | //
262 | this.LblRadiation.AutoSize = true;
263 | this.LblRadiation.Location = new System.Drawing.Point(13, 52);
264 | this.LblRadiation.Name = "LblRadiation";
265 | this.LblRadiation.Size = new System.Drawing.Size(63, 15);
266 | this.LblRadiation.TabIndex = 14;
267 | this.LblRadiation.Text = "Radiation :";
268 | //
269 | // LblTargetName
270 | //
271 | this.LblTargetName.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
272 | this.LblTargetName.Location = new System.Drawing.Point(88, 18);
273 | this.LblTargetName.Name = "LblTargetName";
274 | this.LblTargetName.Size = new System.Drawing.Size(168, 23);
275 | this.LblTargetName.TabIndex = 13;
276 | this.LblTargetName.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
277 | //
278 | // BtnRandomAttraction
279 | //
280 | this.BtnRandomAttraction.Location = new System.Drawing.Point(195, 81);
281 | this.BtnRandomAttraction.Name = "BtnRandomAttraction";
282 | this.BtnRandomAttraction.Size = new System.Drawing.Size(61, 23);
283 | this.BtnRandomAttraction.TabIndex = 12;
284 | this.BtnRandomAttraction.Text = "Random";
285 | this.BtnRandomAttraction.UseVisualStyleBackColor = true;
286 | //
287 | // LblAttraction
288 | //
289 | this.LblAttraction.AutoSize = true;
290 | this.LblAttraction.Location = new System.Drawing.Point(13, 83);
291 | this.LblAttraction.Name = "LblAttraction";
292 | this.LblAttraction.Size = new System.Drawing.Size(66, 15);
293 | this.LblAttraction.TabIndex = 4;
294 | this.LblAttraction.Text = "Attraction :";
295 | //
296 | // NumAttraction
297 | //
298 | this.NumAttraction.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
299 | this.NumAttraction.DecimalPlaces = 10;
300 | this.NumAttraction.Increment = new decimal(new int[] {
301 | 1,
302 | 0,
303 | 0,
304 | 65536});
305 | this.NumAttraction.Location = new System.Drawing.Point(88, 81);
306 | this.NumAttraction.Maximum = new decimal(new int[] {
307 | 1,
308 | 0,
309 | 0,
310 | 0});
311 | this.NumAttraction.Minimum = new decimal(new int[] {
312 | 1,
313 | 0,
314 | 0,
315 | -2147483648});
316 | this.NumAttraction.Name = "NumAttraction";
317 | this.NumAttraction.Size = new System.Drawing.Size(101, 23);
318 | this.NumAttraction.TabIndex = 7;
319 | //
320 | // BtnSave
321 | //
322 | this.BtnSave.Location = new System.Drawing.Point(195, 117);
323 | this.BtnSave.Name = "BtnSave";
324 | this.BtnSave.Size = new System.Drawing.Size(61, 24);
325 | this.BtnSave.TabIndex = 11;
326 | this.BtnSave.Text = "Save";
327 | this.BtnSave.UseVisualStyleBackColor = true;
328 | //
329 | // LblTarget
330 | //
331 | this.LblTarget.AutoSize = true;
332 | this.LblTarget.Location = new System.Drawing.Point(13, 22);
333 | this.LblTarget.Name = "LblTarget";
334 | this.LblTarget.Size = new System.Drawing.Size(45, 15);
335 | this.LblTarget.TabIndex = 4;
336 | this.LblTarget.Text = "Target :";
337 | //
338 | // LstForces
339 | //
340 | this.LstForces.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
341 | this.LstForces.FormattingEnabled = true;
342 | this.LstForces.ItemHeight = 15;
343 | this.LstForces.Location = new System.Drawing.Point(19, 25);
344 | this.LstForces.Name = "LstForces";
345 | this.LstForces.ScrollAlwaysVisible = true;
346 | this.LstForces.Size = new System.Drawing.Size(142, 152);
347 | this.LstForces.TabIndex = 5;
348 | //
349 | // FrmAtom
350 | //
351 | this.AcceptButton = this.BtnOk;
352 | this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
353 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
354 | this.CancelButton = this.BtnCancel;
355 | this.ClientSize = new System.Drawing.Size(482, 372);
356 | this.Controls.Add(this.GrpAtomForces);
357 | this.Controls.Add(this.GrpAtomSettings);
358 | this.Controls.Add(this.BtnCancel);
359 | this.Controls.Add(this.BtnOk);
360 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow;
361 | this.Name = "FrmAtom";
362 | this.Text = "Atom";
363 | this.GrpAtomSettings.ResumeLayout(false);
364 | this.GrpAtomSettings.PerformLayout();
365 | ((System.ComponentModel.ISupportInitialize)(this.NumParticles)).EndInit();
366 | ((System.ComponentModel.ISupportInitialize)(this.NumRadius)).EndInit();
367 | ((System.ComponentModel.ISupportInitialize)(this.PicColor)).EndInit();
368 | this.GrpAtomForces.ResumeLayout(false);
369 | this.GrpForce.ResumeLayout(false);
370 | this.GrpForce.PerformLayout();
371 | ((System.ComponentModel.ISupportInitialize)(this.NumRadiation)).EndInit();
372 | ((System.ComponentModel.ISupportInitialize)(this.NumAttraction)).EndInit();
373 | this.ResumeLayout(false);
374 |
375 | }
376 |
377 | #endregion
378 |
379 | private Button BtnOk;
380 | private Button BtnCancel;
381 | private GroupBox GrpAtomSettings;
382 | private TextBox TxtName;
383 | private Label LblRadius;
384 | private NumericUpDown NumRadius;
385 | private Label LblColor;
386 | private Button BtnPicker;
387 | private Label LblName;
388 | private PictureBox PicColor;
389 | private Label LblParticles;
390 | private NumericUpDown NumParticles;
391 | private GroupBox GrpAtomForces;
392 | private ListBox LstForces;
393 | private GroupBox GrpForce;
394 | private Label LblAttraction;
395 | private NumericUpDown NumAttraction;
396 | private Label LblTarget;
397 | private Button BtnSave;
398 | private Button BtnRandomAttraction;
399 | private Label LblTargetName;
400 | private Button BtnRandomRadiation;
401 | private NumericUpDown NumRadiation;
402 | private Label LblRadiation;
403 | }
404 | }
--------------------------------------------------------------------------------
/FrmAtom.cs:
--------------------------------------------------------------------------------
1 | using ParticleLifeSimulation.Core;
2 |
3 | namespace ParticleLifeSimulation
4 | {
5 | public partial class FrmAtom : Form
6 | {
7 | public Atom Atom { get; private set; }
8 | public FrmAtom(Atom atom)
9 | {
10 | InitializeComponent();
11 |
12 | //Clone Atom
13 | Atom = atom.ToClone();
14 | foreach (Particle particle in atom.Particles)
15 | Atom.Particles.Add(particle.ToClone());
16 | foreach (Force force in atom.Forces)
17 | Atom.AddForce(force.Target == atom ? new(Atom, force.Radiation, force.Attraction) : force.ToClone());
18 |
19 | //Initialize Components
20 | TxtName.Text = Atom.Name;
21 | TxtName.TextChanged += new((s, ev) =>
22 | {
23 | Atom.Name = TxtName.Text;
24 | int index = LstForces.Items.IndexOf(Atom.GetForceWith(Atom)!);
25 | LstForces.Items[index] = LstForces.Items[index];
26 | });
27 | void PicColorUpdate(object? sender, EventArgs e)
28 | {
29 | Bitmap bitmap = new(PicColor.ClientSize.Height, PicColor.ClientSize.Height);
30 | using (Graphics graphics = Graphics.FromImage(bitmap))
31 | {
32 | graphics.Clear(Color.Transparent);
33 | float x = bitmap.Width * 0.25f;
34 | float y = bitmap.Height * 0.25f;
35 | float width = bitmap.Width / 2.0f;
36 | float height = bitmap.Height / 2.0f;
37 | ((FrmSimulation)Owner).ParticleDraw(graphics, 255, Atom.Color, x, y, width, height);
38 | }
39 | PicColor.Image = bitmap;
40 | }
41 | Atom.AtomColorChanged += new(PicColorUpdate);
42 | void ColorPicker(object? sender, EventArgs e)
43 | {
44 | ColorDialog colorDialog = new()
45 | {
46 | SolidColorOnly = true,
47 | Color = Atom.Color
48 | };
49 | if (colorDialog.ShowDialog() == DialogResult.OK)
50 | {
51 | Atom.Color = colorDialog.Color;
52 | }
53 | }
54 | PicColor.Click += new(ColorPicker);
55 | BtnPicker.Click += new(ColorPicker);
56 | NumRadius.Value = (decimal)Atom.Radius;
57 | NumRadius.ValueChanged += new((s, ev) => Atom.Radius = (double)NumRadius.Value);
58 | NumParticles.Value = Atom.Particles.Count;
59 | NumParticles.ValueChanged += new((s, ev) => Atom.UpdateParticles((int)NumParticles.Value));
60 | LstForces.DisplayMember = "Name";
61 | LstForces.Items.AddRange(Atom.Forces.ToArray());
62 | LstForces.SelectedIndexChanged += new((s, ev) =>
63 | {
64 | if (LstForces.SelectedIndex == -1)
65 | {
66 | GrpForce.Enabled = false;
67 | LblTargetName.Text = string.Empty;
68 | NumRadiation.Value = 0;
69 | NumAttraction.Value = 0;
70 | }
71 | else
72 | {
73 | GrpForce.Enabled = true;
74 | BtnSave.Enabled = false;
75 |
76 | Force forceSelected = (Force)LstForces.SelectedItem;
77 | LblTargetName.Text = forceSelected.Target.Name;
78 | NumRadiation.Value = (decimal)forceSelected.Radiation;
79 | NumAttraction.Value = (decimal)forceSelected.Attraction;
80 | }
81 | });
82 | NumRadiation.ValueChanged += new((s, ev) =>
83 | {
84 | if (LstForces.SelectedItem is not null && (
85 | (decimal)((Force)LstForces.SelectedItem).Radiation != NumRadiation.Value ||
86 | (decimal)((Force)LstForces.SelectedItem).Attraction != NumAttraction.Value))
87 | BtnSave.Enabled = true;
88 | else
89 | BtnSave.Enabled = false;
90 | });
91 | NumAttraction.ValueChanged += new((s, ev) =>
92 | {
93 | if (LstForces.SelectedItem is not null && (
94 | (decimal)((Force)LstForces.SelectedItem).Radiation != NumRadiation.Value ||
95 | (decimal)((Force)LstForces.SelectedItem).Attraction != NumAttraction.Value))
96 | BtnSave.Enabled = true;
97 | else
98 | BtnSave.Enabled = false;
99 | });
100 | LstForces.SelectedIndex = -1;
101 | GrpForce.Enabled = false;
102 | LblTargetName.Text = string.Empty;
103 | NumRadiation.Value = 0;
104 | NumAttraction.Value = 0;
105 | BtnSave.Click += new((s, ev) =>
106 | {
107 | ((Force)LstForces.SelectedItem).Attraction = (double)NumAttraction.Value;
108 | BtnSave.Enabled = false;
109 | });
110 | BtnRandomRadiation.Click += new((s, ev) => NumRadiation.Value = (decimal)Random.Shared.NextDoubleInclusive(0.0, 1.0));
111 | BtnRandomAttraction.Click += new((s, ev) => NumAttraction.Value = (decimal)Random.Shared.NextDoubleInclusive(-1.0, 1.0));
112 | Load += new(PicColorUpdate);
113 | }
114 |
115 | private void BtnOk_Click(object sender, EventArgs e)
116 | {
117 | DialogResult = DialogResult.OK;
118 | Close();
119 | }
120 | }
121 | }
--------------------------------------------------------------------------------
/FrmAtom.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | text/microsoft-resx
50 |
51 |
52 | 2.0
53 |
54 |
55 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
56 |
57 |
58 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
59 |
60 |
--------------------------------------------------------------------------------
/FrmSettings.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace ParticleLifeSimulation
2 | {
3 | partial class FrmSettings
4 | {
5 | ///
6 | /// Required designer variable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Clean up any resources being used.
12 | ///
13 | /// true if managed resources should be disposed; otherwise, false.
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Windows Form Designer generated code
24 |
25 | ///
26 | /// Required method for Designer support - do not modify
27 | /// the contents of this method with the code editor.
28 | ///
29 | private void InitializeComponent()
30 | {
31 | this.BtnOk = new System.Windows.Forms.Button();
32 | this.BtnCancel = new System.Windows.Forms.Button();
33 | this.GrpSimulation = new System.Windows.Forms.GroupBox();
34 | this.LblStepsByFrame = new System.Windows.Forms.Label();
35 | this.BtnPicker = new System.Windows.Forms.Button();
36 | this.LblSteps = new System.Windows.Forms.Label();
37 | this.LblBackColor = new System.Windows.Forms.Label();
38 | this.NumStepsByFrame = new System.Windows.Forms.NumericUpDown();
39 | this.ChkAnimated = new System.Windows.Forms.CheckBox();
40 | this.PicColor = new System.Windows.Forms.PictureBox();
41 | this.GrpParticles = new System.Windows.Forms.GroupBox();
42 | this.ChkBorderless = new System.Windows.Forms.CheckBox();
43 | this.ChkContraste = new System.Windows.Forms.CheckBox();
44 | this.BtnResetDefault = new System.Windows.Forms.Button();
45 | this.ChkCompoundColorNamesIncluded = new System.Windows.Forms.CheckBox();
46 | this.ChkSystemColorsIncluded = new System.Windows.Forms.CheckBox();
47 | this.GrpSimulation.SuspendLayout();
48 | ((System.ComponentModel.ISupportInitialize)(this.NumStepsByFrame)).BeginInit();
49 | ((System.ComponentModel.ISupportInitialize)(this.PicColor)).BeginInit();
50 | this.GrpParticles.SuspendLayout();
51 | this.SuspendLayout();
52 | //
53 | // BtnOk
54 | //
55 | this.BtnOk.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
56 | this.BtnOk.Location = new System.Drawing.Point(406, 229);
57 | this.BtnOk.Name = "BtnOk";
58 | this.BtnOk.Size = new System.Drawing.Size(47, 30);
59 | this.BtnOk.TabIndex = 0;
60 | this.BtnOk.Text = "OK";
61 | this.BtnOk.UseVisualStyleBackColor = true;
62 | this.BtnOk.Click += new System.EventHandler(this.BtnOk_Click);
63 | //
64 | // BtnCancel
65 | //
66 | this.BtnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
67 | this.BtnCancel.Location = new System.Drawing.Point(320, 229);
68 | this.BtnCancel.Name = "BtnCancel";
69 | this.BtnCancel.Size = new System.Drawing.Size(80, 30);
70 | this.BtnCancel.TabIndex = 0;
71 | this.BtnCancel.Text = "CANCEL";
72 | this.BtnCancel.UseVisualStyleBackColor = true;
73 | //
74 | // GrpSimulation
75 | //
76 | this.GrpSimulation.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
77 | | System.Windows.Forms.AnchorStyles.Right)));
78 | this.GrpSimulation.Controls.Add(this.LblStepsByFrame);
79 | this.GrpSimulation.Controls.Add(this.BtnPicker);
80 | this.GrpSimulation.Controls.Add(this.LblSteps);
81 | this.GrpSimulation.Controls.Add(this.LblBackColor);
82 | this.GrpSimulation.Controls.Add(this.NumStepsByFrame);
83 | this.GrpSimulation.Controls.Add(this.ChkAnimated);
84 | this.GrpSimulation.Controls.Add(this.PicColor);
85 | this.GrpSimulation.Location = new System.Drawing.Point(12, 12);
86 | this.GrpSimulation.Name = "GrpSimulation";
87 | this.GrpSimulation.Size = new System.Drawing.Size(441, 99);
88 | this.GrpSimulation.TabIndex = 1;
89 | this.GrpSimulation.TabStop = false;
90 | this.GrpSimulation.Text = "Simulation";
91 | //
92 | // LblStepsByFrame
93 | //
94 | this.LblStepsByFrame.AutoSize = true;
95 | this.LblStepsByFrame.Location = new System.Drawing.Point(381, 28);
96 | this.LblStepsByFrame.Name = "LblStepsByFrame";
97 | this.LblStepsByFrame.Size = new System.Drawing.Size(49, 15);
98 | this.LblStepsByFrame.TabIndex = 3;
99 | this.LblStepsByFrame.Text = " / frame";
100 | //
101 | // BtnPicker
102 | //
103 | this.BtnPicker.Location = new System.Drawing.Point(164, 24);
104 | this.BtnPicker.Name = "BtnPicker";
105 | this.BtnPicker.Size = new System.Drawing.Size(59, 23);
106 | this.BtnPicker.TabIndex = 6;
107 | this.BtnPicker.Text = "Picker";
108 | this.BtnPicker.UseVisualStyleBackColor = true;
109 | this.BtnPicker.Click += new System.EventHandler(this.PicColor_Click);
110 | //
111 | // LblSteps
112 | //
113 | this.LblSteps.AutoSize = true;
114 | this.LblSteps.Location = new System.Drawing.Point(273, 28);
115 | this.LblSteps.Name = "LblSteps";
116 | this.LblSteps.Size = new System.Drawing.Size(41, 15);
117 | this.LblSteps.TabIndex = 2;
118 | this.LblSteps.Text = "Steps :";
119 | //
120 | // LblBackColor
121 | //
122 | this.LblBackColor.AutoSize = true;
123 | this.LblBackColor.Location = new System.Drawing.Point(19, 28);
124 | this.LblBackColor.Name = "LblBackColor";
125 | this.LblBackColor.Size = new System.Drawing.Size(67, 15);
126 | this.LblBackColor.TabIndex = 5;
127 | this.LblBackColor.Text = "BackColor :";
128 | //
129 | // NumStepsByFrame
130 | //
131 | this.NumStepsByFrame.Location = new System.Drawing.Point(320, 24);
132 | this.NumStepsByFrame.Maximum = new decimal(new int[] {
133 | 1000,
134 | 0,
135 | 0,
136 | 0});
137 | this.NumStepsByFrame.Minimum = new decimal(new int[] {
138 | 1,
139 | 0,
140 | 0,
141 | 0});
142 | this.NumStepsByFrame.Name = "NumStepsByFrame";
143 | this.NumStepsByFrame.Size = new System.Drawing.Size(58, 23);
144 | this.NumStepsByFrame.TabIndex = 1;
145 | this.NumStepsByFrame.Value = new decimal(new int[] {
146 | 1,
147 | 0,
148 | 0,
149 | 0});
150 | //
151 | // ChkAnimated
152 | //
153 | this.ChkAnimated.AutoSize = true;
154 | this.ChkAnimated.Location = new System.Drawing.Point(19, 64);
155 | this.ChkAnimated.Name = "ChkAnimated";
156 | this.ChkAnimated.Size = new System.Drawing.Size(78, 19);
157 | this.ChkAnimated.TabIndex = 0;
158 | this.ChkAnimated.Text = "Animated";
159 | this.ChkAnimated.UseVisualStyleBackColor = true;
160 | //
161 | // PicColor
162 | //
163 | this.PicColor.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
164 | this.PicColor.Location = new System.Drawing.Point(92, 22);
165 | this.PicColor.Name = "PicColor";
166 | this.PicColor.Size = new System.Drawing.Size(66, 27);
167 | this.PicColor.TabIndex = 1;
168 | this.PicColor.TabStop = false;
169 | this.PicColor.Click += new System.EventHandler(this.PicColor_Click);
170 | //
171 | // GrpParticles
172 | //
173 | this.GrpParticles.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
174 | | System.Windows.Forms.AnchorStyles.Right)));
175 | this.GrpParticles.Controls.Add(this.ChkCompoundColorNamesIncluded);
176 | this.GrpParticles.Controls.Add(this.ChkSystemColorsIncluded);
177 | this.GrpParticles.Controls.Add(this.ChkBorderless);
178 | this.GrpParticles.Controls.Add(this.ChkContraste);
179 | this.GrpParticles.Location = new System.Drawing.Point(12, 117);
180 | this.GrpParticles.Name = "GrpParticles";
181 | this.GrpParticles.Size = new System.Drawing.Size(441, 92);
182 | this.GrpParticles.TabIndex = 2;
183 | this.GrpParticles.TabStop = false;
184 | this.GrpParticles.Text = "Particles";
185 | //
186 | // ChkBorderless
187 | //
188 | this.ChkBorderless.AutoSize = true;
189 | this.ChkBorderless.Location = new System.Drawing.Point(19, 57);
190 | this.ChkBorderless.Name = "ChkBorderless";
191 | this.ChkBorderless.Size = new System.Drawing.Size(80, 19);
192 | this.ChkBorderless.TabIndex = 2;
193 | this.ChkBorderless.Text = "Borderless";
194 | this.ChkBorderless.UseVisualStyleBackColor = true;
195 | //
196 | // ChkContraste
197 | //
198 | this.ChkContraste.AutoSize = true;
199 | this.ChkContraste.Location = new System.Drawing.Point(19, 22);
200 | this.ChkContraste.Name = "ChkContraste";
201 | this.ChkContraste.Size = new System.Drawing.Size(77, 19);
202 | this.ChkContraste.TabIndex = 1;
203 | this.ChkContraste.Text = "Contraste";
204 | this.ChkContraste.UseVisualStyleBackColor = true;
205 | //
206 | // BtnResetDefault
207 | //
208 | this.BtnResetDefault.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
209 | this.BtnResetDefault.Location = new System.Drawing.Point(12, 229);
210 | this.BtnResetDefault.Name = "BtnResetDefault";
211 | this.BtnResetDefault.Size = new System.Drawing.Size(86, 30);
212 | this.BtnResetDefault.TabIndex = 3;
213 | this.BtnResetDefault.Text = "Reset Default";
214 | this.BtnResetDefault.UseVisualStyleBackColor = true;
215 | this.BtnResetDefault.Click += new System.EventHandler(this.BtnResetDefault_Click);
216 | //
217 | // ChkCompoundColorNamesIncluded
218 | //
219 | this.ChkCompoundColorNamesIncluded.AutoSize = true;
220 | this.ChkCompoundColorNamesIncluded.Location = new System.Drawing.Point(174, 57);
221 | this.ChkCompoundColorNamesIncluded.Name = "ChkCompoundColorNamesIncluded";
222 | this.ChkCompoundColorNamesIncluded.Size = new System.Drawing.Size(256, 19);
223 | this.ChkCompoundColorNamesIncluded.TabIndex = 4;
224 | this.ChkCompoundColorNamesIncluded.Text = "Random Compound Color Names Included";
225 | this.ChkCompoundColorNamesIncluded.UseVisualStyleBackColor = true;
226 | //
227 | // ChkSystemColorsIncluded
228 | //
229 | this.ChkSystemColorsIncluded.AutoSize = true;
230 | this.ChkSystemColorsIncluded.Location = new System.Drawing.Point(174, 22);
231 | this.ChkSystemColorsIncluded.Name = "ChkSystemColorsIncluded";
232 | this.ChkSystemColorsIncluded.Size = new System.Drawing.Size(198, 19);
233 | this.ChkSystemColorsIncluded.TabIndex = 3;
234 | this.ChkSystemColorsIncluded.Text = "Random System Colors Included";
235 | this.ChkSystemColorsIncluded.UseVisualStyleBackColor = true;
236 | //
237 | // FrmSettings
238 | //
239 | this.AcceptButton = this.BtnOk;
240 | this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
241 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
242 | this.CancelButton = this.BtnCancel;
243 | this.ClientSize = new System.Drawing.Size(465, 271);
244 | this.Controls.Add(this.BtnResetDefault);
245 | this.Controls.Add(this.GrpParticles);
246 | this.Controls.Add(this.GrpSimulation);
247 | this.Controls.Add(this.BtnCancel);
248 | this.Controls.Add(this.BtnOk);
249 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow;
250 | this.Name = "FrmSettings";
251 | this.Text = "Settings";
252 | this.GrpSimulation.ResumeLayout(false);
253 | this.GrpSimulation.PerformLayout();
254 | ((System.ComponentModel.ISupportInitialize)(this.NumStepsByFrame)).EndInit();
255 | ((System.ComponentModel.ISupportInitialize)(this.PicColor)).EndInit();
256 | this.GrpParticles.ResumeLayout(false);
257 | this.GrpParticles.PerformLayout();
258 | this.ResumeLayout(false);
259 |
260 | }
261 |
262 | #endregion
263 |
264 | private Button BtnOk;
265 | private Button BtnCancel;
266 | private GroupBox GrpSimulation;
267 | private PictureBox PicColor;
268 | private CheckBox ChkAnimated;
269 | private Label LblSteps;
270 | private NumericUpDown NumStepsByFrame;
271 | private Label LblBackColor;
272 | private Label LblStepsByFrame;
273 | private Button BtnPicker;
274 | private GroupBox GrpParticles;
275 | private CheckBox ChkBorderless;
276 | private CheckBox ChkContraste;
277 | private Button BtnResetDefault;
278 | private CheckBox ChkCompoundColorNamesIncluded;
279 | private CheckBox ChkSystemColorsIncluded;
280 | }
281 | }
--------------------------------------------------------------------------------
/FrmSettings.cs:
--------------------------------------------------------------------------------
1 | namespace ParticleLifeSimulation
2 | {
3 | public partial class FrmSettings : Form
4 | {
5 | public FrmSettings(Settings settings)
6 | {
7 | InitializeComponent();
8 | PicColor.DataBindings.Add("BackColor", settings, "SimulationBackColor", true, DataSourceUpdateMode.OnPropertyChanged);
9 | ChkAnimated.DataBindings.Add("Checked", settings, "SimulationAnimated", true, DataSourceUpdateMode.OnPropertyChanged);
10 | NumStepsByFrame.DataBindings.Add("Value", settings, "SimulationStepsPerFrame", true, DataSourceUpdateMode.OnPropertyChanged);
11 | ChkContraste.DataBindings.Add("Checked", settings, "ParticlesContraste", true, DataSourceUpdateMode.OnPropertyChanged);
12 | ChkBorderless.DataBindings.Add("Checked", settings, "ParticlesBorderless", true, DataSourceUpdateMode.OnPropertyChanged);
13 | ChkSystemColorsIncluded.DataBindings.Add("Checked", settings, "ParticlesSystemColorsIncluded", true, DataSourceUpdateMode.OnPropertyChanged);
14 | ChkCompoundColorNamesIncluded.DataBindings.Add("Checked", settings, "ParticlesCompoundColorNamesIncluded", true, DataSourceUpdateMode.OnPropertyChanged);
15 | }
16 |
17 | private void BtnOk_Click(object sender, EventArgs e)
18 | {
19 | DialogResult = DialogResult.OK;
20 | Close();
21 | }
22 |
23 | private void PicColor_Click(object sender, EventArgs e)
24 | {
25 | ColorDialog colorDialog = new()
26 | {
27 | SolidColorOnly = true,
28 | Color = PicColor.BackColor
29 | };
30 | if (colorDialog.ShowDialog() == DialogResult.OK)
31 | {
32 | PicColor.BackColor = colorDialog.Color;
33 | }
34 | }
35 |
36 | private void BtnResetDefault_Click(object sender, EventArgs e)
37 | {
38 | DialogResult = MessageBox.Show(this, "Are you sure to reset settings to default ?", "Warning", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
39 | Close();
40 | }
41 | }
42 | }
--------------------------------------------------------------------------------
/FrmSettings.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | text/microsoft-resx
50 |
51 |
52 | 2.0
53 |
54 |
55 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
56 |
57 |
58 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
59 |
60 |
--------------------------------------------------------------------------------
/FrmSimulation.Designer.cs:
--------------------------------------------------------------------------------
1 | using ParticleLifeSimulation.UserControls;
2 |
3 | namespace ParticleLifeSimulation;
4 |
5 | partial class FrmSimulation
6 | {
7 | ///
8 | /// Required designer variable.
9 | ///
10 | private System.ComponentModel.IContainer components = null;
11 |
12 | ///
13 | /// Clean up any resources being used.
14 | ///
15 | /// true if managed resources should be disposed; otherwise, false.
16 | protected override void Dispose(bool disposing)
17 | {
18 | if (disposing && (components != null))
19 | {
20 | components.Dispose();
21 | }
22 | base.Dispose(disposing);
23 | }
24 |
25 | #region Windows Form Designer generated code
26 |
27 | ///
28 | /// Required method for Designer support - do not modify
29 | /// the contents of this method with the code editor.
30 | ///
31 | private void InitializeComponent()
32 | {
33 | this.PanelSettings = new System.Windows.Forms.FlowLayoutPanel();
34 | this.RightDocker = new System.Windows.Forms.Panel();
35 | this.TogglePanel = new System.Windows.Forms.CheckBox();
36 | this.StatusStripStatus = new System.Windows.Forms.StatusStrip();
37 | this.ToolStripStatusLabelTotalParticles = new System.Windows.Forms.ToolStripStatusLabel();
38 | this.ToolStripStatusLabelTotalParticlesValue = new System.Windows.Forms.ToolStripStatusLabel();
39 | this.ToolStripStatusLabelSpring = new System.Windows.Forms.ToolStripStatusLabel();
40 | this.ToolStripStatusLabelFps = new System.Windows.Forms.ToolStripStatusLabel();
41 | this.ToolStripStatusLabelFpsValue = new System.Windows.Forms.ToolStripStatusLabel();
42 | this.RightDocker.SuspendLayout();
43 | this.StatusStripStatus.SuspendLayout();
44 | this.SuspendLayout();
45 | //
46 | // PanelSettings
47 | //
48 | this.PanelSettings.AllowDrop = true;
49 | this.PanelSettings.AutoSize = true;
50 | this.PanelSettings.BackColor = System.Drawing.Color.Transparent;
51 | this.PanelSettings.Dock = System.Windows.Forms.DockStyle.Top;
52 | this.PanelSettings.Location = new System.Drawing.Point(0, 0);
53 | this.PanelSettings.Name = "PanelSettings";
54 | this.PanelSettings.Size = new System.Drawing.Size(200, 0);
55 | this.PanelSettings.TabIndex = 0;
56 | //
57 | // RightDocker
58 | //
59 | this.RightDocker.AutoScroll = true;
60 | this.RightDocker.BackColor = System.Drawing.Color.Transparent;
61 | this.RightDocker.Controls.Add(this.PanelSettings);
62 | this.RightDocker.Dock = System.Windows.Forms.DockStyle.Right;
63 | this.RightDocker.Location = new System.Drawing.Point(600, 0);
64 | this.RightDocker.Name = "RightDocker";
65 | this.RightDocker.Size = new System.Drawing.Size(200, 476);
66 | this.RightDocker.TabIndex = 1;
67 | //
68 | // TogglePanel
69 | //
70 | this.TogglePanel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
71 | this.TogglePanel.Appearance = System.Windows.Forms.Appearance.Button;
72 | this.TogglePanel.AutoSize = true;
73 | this.TogglePanel.Location = new System.Drawing.Point(575, 3);
74 | this.TogglePanel.Name = "TogglePanel";
75 | this.TogglePanel.Size = new System.Drawing.Size(25, 25);
76 | this.TogglePanel.TabIndex = 0;
77 | this.TogglePanel.Text = ">";
78 | this.TogglePanel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
79 | this.TogglePanel.UseVisualStyleBackColor = true;
80 | //
81 | // StatusStripStatus
82 | //
83 | this.StatusStripStatus.BackColor = System.Drawing.SystemColors.Control;
84 | this.StatusStripStatus.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
85 | this.ToolStripStatusLabelTotalParticles,
86 | this.ToolStripStatusLabelTotalParticlesValue,
87 | this.ToolStripStatusLabelSpring,
88 | this.ToolStripStatusLabelFps,
89 | this.ToolStripStatusLabelFpsValue});
90 | this.StatusStripStatus.Location = new System.Drawing.Point(0, 476);
91 | this.StatusStripStatus.Name = "StatusStripStatus";
92 | this.StatusStripStatus.Size = new System.Drawing.Size(800, 24);
93 | this.StatusStripStatus.TabIndex = 2;
94 | //
95 | // ToolStripStatusLabelTotalParticles
96 | //
97 | this.ToolStripStatusLabelTotalParticles.Name = "ToolStripStatusLabelTotalParticles";
98 | this.ToolStripStatusLabelTotalParticles.Size = new System.Drawing.Size(85, 19);
99 | this.ToolStripStatusLabelTotalParticles.Text = "Total Particles :";
100 | //
101 | // ToolStripStatusLabelTotalParticlesValue
102 | //
103 | this.ToolStripStatusLabelTotalParticlesValue.BorderSides = System.Windows.Forms.ToolStripStatusLabelBorderSides.Right;
104 | this.ToolStripStatusLabelTotalParticlesValue.Name = "ToolStripStatusLabelTotalParticlesValue";
105 | this.ToolStripStatusLabelTotalParticlesValue.Size = new System.Drawing.Size(17, 19);
106 | this.ToolStripStatusLabelTotalParticlesValue.Text = "0";
107 | //
108 | // ToolStripStatusLabelSpring
109 | //
110 | this.ToolStripStatusLabelSpring.BorderSides = ((System.Windows.Forms.ToolStripStatusLabelBorderSides)((System.Windows.Forms.ToolStripStatusLabelBorderSides.Left | System.Windows.Forms.ToolStripStatusLabelBorderSides.Right)));
111 | this.ToolStripStatusLabelSpring.Name = "ToolStripStatusLabelSpring";
112 | this.ToolStripStatusLabelSpring.Size = new System.Drawing.Size(634, 19);
113 | this.ToolStripStatusLabelSpring.Spring = true;
114 | //
115 | // ToolStripStatusLabelFps
116 | //
117 | this.ToolStripStatusLabelFps.BorderSides = System.Windows.Forms.ToolStripStatusLabelBorderSides.Left;
118 | this.ToolStripStatusLabelFps.Name = "ToolStripStatusLabelFps";
119 | this.ToolStripStatusLabelFps.Size = new System.Drawing.Size(36, 19);
120 | this.ToolStripStatusLabelFps.Text = "FPS :";
121 | //
122 | // ToolStripStatusLabelFpsValue
123 | //
124 | this.ToolStripStatusLabelFpsValue.Name = "ToolStripStatusLabelFpsValue";
125 | this.ToolStripStatusLabelFpsValue.Size = new System.Drawing.Size(13, 19);
126 | this.ToolStripStatusLabelFpsValue.Text = "0";
127 | //
128 | // FrmSimulation
129 | //
130 | this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
131 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
132 | this.BackgroundImageLayout = System.Windows.Forms.ImageLayout.None;
133 | this.ClientSize = new System.Drawing.Size(800, 500);
134 | this.Controls.Add(this.TogglePanel);
135 | this.Controls.Add(this.RightDocker);
136 | this.Controls.Add(this.StatusStripStatus);
137 | this.DoubleBuffered = true;
138 | this.Margin = new System.Windows.Forms.Padding(4);
139 | this.Name = "FrmSimulation";
140 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
141 | this.Text = "Simulation";
142 | this.RightDocker.ResumeLayout(false);
143 | this.RightDocker.PerformLayout();
144 | this.StatusStripStatus.ResumeLayout(false);
145 | this.StatusStripStatus.PerformLayout();
146 | this.ResumeLayout(false);
147 | this.PerformLayout();
148 |
149 | }
150 |
151 | #endregion
152 | private FlowLayoutPanel PanelSettings;
153 | private Panel RightDocker;
154 | private CheckBox TogglePanel;
155 | private StatusStrip StatusStripStatus;
156 | private ToolStripStatusLabel ToolStripStatusLabelTotalParticles;
157 | private ToolStripStatusLabel ToolStripStatusLabelTotalParticlesValue;
158 | private ToolStripStatusLabel ToolStripStatusLabelSpring;
159 | private ToolStripStatusLabel ToolStripStatusLabelFps;
160 | private ToolStripStatusLabel ToolStripStatusLabelFpsValue;
161 | }
--------------------------------------------------------------------------------
/FrmSimulation.cs:
--------------------------------------------------------------------------------
1 | using ParticleLifeSimulation.Core;
2 | using ParticleLifeSimulation.Properties;
3 | using ParticleLifeSimulation.UserControls;
4 | using static ParticleLifeSimulation.Core.Extensions;
5 |
6 | namespace ParticleLifeSimulation;
7 |
8 | public partial class FrmSimulation : Form, IFormLoop
9 | {
10 | #region Properties
11 | private Bitmap BackgroundImageBuffer;
12 | private Settings Settings;
13 | private Universe Universe;
14 |
15 | private long ElapsedTime;
16 | private int FrameCounter;
17 | private int FramesPerSecond;
18 | private int LastRightDockerWidth;
19 | #endregion
20 |
21 | #region Constructor
22 | public FrmSimulation()
23 | {
24 | InitializeComponent();
25 |
26 | //How to brute force a protected property ;) (Avoid flicking, BackColor = Transparent)
27 | System.Reflection.PropertyInfo setDoubleBuffered = typeof(Control).GetProperty("DoubleBuffered", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)!;
28 | setDoubleBuffered.SetValue(RightDocker, true, null);
29 | setDoubleBuffered.SetValue(PanelSettings, true, null);
30 |
31 | BackgroundImageBuffer = new(ClientSize.Width, ClientSize.Height);
32 |
33 | Universe = new(ClientSize.Width, ClientSize.Height);
34 |
35 | Settings = new();
36 |
37 | InitializeSettingsUI();
38 | InitializeParticlesUI();
39 | InitializeUniverseUI();
40 | }
41 | #endregion
42 |
43 | #region SettingsUI
44 | private void InitializeSettingsUI()
45 | {
46 | void resizePanelSettingsControls()
47 | {
48 | foreach (Control control in PanelSettings.Controls)
49 | {
50 | control.Width = PanelSettings.ClientSize.Width - (PanelSettings.Margin.Left + PanelSettings.Margin.Right);
51 | if (control.Tag is string tag) switch (tag)
52 | {
53 | case "FIRST":
54 | PanelSettings.Controls.SetChildIndex(control, 0);
55 | break;
56 | case "LAST":
57 | PanelSettings.Controls.SetChildIndex(control, PanelSettings.Controls.Count - 1);
58 | break;
59 | }
60 | }
61 | }
62 | PanelSettings.DragOver += new((s, dev) =>
63 | {
64 | if (dev.Data!.GetDataPresent(typeof(Accordion)))
65 | {
66 | Point mousePosition = PanelSettings.PointToClient(new Point(dev.X, dev.Y));
67 | Accordion accSource = (Accordion)dev.Data!.GetData(typeof(Accordion));
68 | if (PanelSettings.GetChildAtPoint(mousePosition) is Accordion accTarget && accTarget != accSource && accTarget.IsDraggable)
69 | dev.Effect = DragDropEffects.Move;
70 | else
71 | dev.Effect = DragDropEffects.None;
72 | }
73 | });
74 | PanelSettings.DragDrop += new((s, dev) =>
75 | {
76 | Accordion accSource = (Accordion)dev.Data!.GetData(typeof(Accordion));
77 | Point mousePosition = PanelSettings.PointToClient(new Point(dev.X, dev.Y));
78 | Accordion? accTarget = PanelSettings.GetChildAtPoint(mousePosition) as Accordion;
79 | int accIndexTarget = PanelSettings.Controls.IndexOf(accTarget);
80 | if (accIndexTarget != -1) PanelSettings.Controls.SetChildIndex(accSource, accIndexTarget);
81 | });
82 | TogglePanel.CheckedChanged += new((s, ev) =>
83 | {
84 | if (TogglePanel.Checked)
85 | {
86 | LastRightDockerWidth = RightDocker.Width;
87 | RightDocker.Width = 0;
88 | TogglePanel.Left = RightDocker.Left - TogglePanel.Width;
89 | TogglePanel.Text = "<";
90 | }
91 | else
92 | {
93 | RightDocker.Width = LastRightDockerWidth;
94 | TogglePanel.Left = RightDocker.Left - TogglePanel.Width;
95 | TogglePanel.Text = ">";
96 | }
97 | });
98 | PanelSettings.ControlAdded += new((s, cev) => resizePanelSettingsControls());
99 | PanelSettings.ClientSizeChanged += new((s, ev) => resizePanelSettingsControls());
100 |
101 | ContextMenuStrip = new();
102 | ToolStripMenuItem toolStripSettings = new() { Text = "Settings" };
103 | toolStripSettings.Click += (s, ev) =>
104 | {
105 | FrmSettings frmSettings = new(Settings) { Owner = this, StartPosition = FormStartPosition.CenterParent };
106 | DialogResult dialogResult = frmSettings.ShowDialog(this);
107 | switch (dialogResult)
108 | {
109 | case DialogResult.OK:
110 | Settings.Save();
111 | break;
112 | case DialogResult.Yes:
113 | Settings.Reset();
114 | break;
115 | default:
116 | Settings.Reload();
117 | break;
118 | }
119 | };
120 | ContextMenuStrip.Items.Add(toolStripSettings);
121 | }
122 | #endregion
123 |
124 | #region ParticlesUI
125 | private void InitializeParticlesUI()
126 | {
127 | // Particles AccordionUI
128 | Accordion accordionParticlesUI = new() { Title = "Particles" };
129 |
130 | // Particles Contraste
131 | CheckBox chkContraste = new() { Text = "Contraste", Checked = Settings.ParticlesContraste };
132 | chkContraste.CheckedChanged += new((s, ev) =>
133 | {
134 | Settings.ParticlesContraste = chkContraste.Checked;
135 | Settings.Save();
136 | PanelSettings.Invalidate(true);
137 | });
138 |
139 | // Particles Borderless
140 | CheckBox chkBorderless = new() { Text = "Borderless", Checked = Settings.ParticlesBorderless };
141 | chkBorderless.CheckedChanged += new((s, ev) =>
142 | {
143 | Settings.ParticlesBorderless = chkBorderless.Checked;
144 | Settings.Save();
145 | PanelSettings.Invalidate(true);
146 | });
147 |
148 | accordionParticlesUI.Controls.AddRange(new Control[] { chkContraste, chkBorderless});
149 | PanelSettings.Controls.Add(accordionParticlesUI);
150 | }
151 | #endregion
152 |
153 | #region UniverseUI
154 | private void InitializeUniverseUI()
155 | {
156 | ClientSizeChanged += new((s, ev) =>
157 | {
158 | if (WindowState == FormWindowState.Minimized) return;
159 | BackgroundImageBuffer = new(BackgroundImageBuffer, ClientSize);
160 | Universe.Size = ClientSize;
161 | });
162 | Universe.UniverseAtomAdded += new((s, uev) =>
163 | {
164 | Atom atom = uev.Atom!;
165 | InitializeAtomUI(atom);
166 | foreach (Atom target in Universe.Atoms)
167 | {
168 | atom.AddForce(new(target, Random.Shared.NextDoubleInclusive(), Random.Shared.NextDoubleInclusive(-1.0, 1.0)));
169 | if (atom != target)
170 | if (!target.HasForceWith(atom))
171 | target.AddForce(new(atom, Random.Shared.NextDoubleInclusive(), Random.Shared.NextDoubleInclusive(-1.0, 1.0)));
172 | }
173 | });
174 |
175 | // Universe Settings Accordion
176 | Accordion accordionUniverseUI = new() { Title = "Universe Settings" };
177 |
178 | // Reset All Particles Button
179 | Button btnResetAllParticles = new()
180 | {
181 | FlatStyle = FlatStyle.Flat,
182 | Text = "Reset All Particles Position",
183 | UseVisualStyleBackColor = true
184 | };
185 | btnResetAllParticles.Click += new((s, ev) => Universe.ResetParticles());
186 |
187 | // Wrap CheckBox
188 | CheckBox chkWrap = new()
189 | {
190 | Text = "Wrap",
191 | Checked = Universe.Wrap,
192 | UseVisualStyleBackColor = true
193 | };
194 | chkWrap.CheckedChanged += new((s, ev) => Universe.Wrap = chkWrap.Checked);
195 |
196 | // Friction Slider
197 | Slider sldFriction = new() { MinValue = 0.0, MaxValue = 1.0, Value = Universe.Friction, ValueFormat = "F2", Text = "Friction" };
198 | sldFriction.OnValueChanged += new((s, ev) => Universe.Friction = sldFriction.Value);
199 |
200 | // Add Atom Button
201 | Label lblAddAtom = new()
202 | {
203 | BorderStyle = BorderStyle.FixedSingle,
204 | Tag = "LAST",
205 | Text = "+ Add new Atom",
206 | TextAlign = ContentAlignment.MiddleCenter,
207 | BackColor = SystemColors.Control
208 | };
209 | lblAddAtom.Click += new((s, ev) =>
210 | {
211 | Color randomKnownColor = GetRandomKnownColor(Settings.ParticlesSystemColorsIncluded, Settings.ParticlesCompoundColorNamesIncluded);
212 | while (Universe.Atoms.Any(atom => atom.Color == randomKnownColor))
213 | randomKnownColor = GetRandomKnownColor(Settings.ParticlesSystemColorsIncluded, Settings.ParticlesCompoundColorNamesIncluded);
214 | Universe.AddAtom(new(randomKnownColor.Name, randomKnownColor, Universe.Width, Universe.Height, 2.5, 150));
215 | });
216 |
217 | accordionUniverseUI.Controls.AddRange(new Control[] { btnResetAllParticles, chkWrap, sldFriction });
218 | PanelSettings.Controls.AddRange(new Control[] { accordionUniverseUI, lblAddAtom });
219 | }
220 | #endregion
221 |
222 | #region AtomUI
223 | private void InitializeAtomUI(Atom atom)
224 | {
225 | // Atom Settings AccordionUI
226 | Accordion accordionAtomUI = new() { Title = atom.Name, IsDraggable = true, Atom = atom, ParticleDraw = ParticleDraw, ContextMenuStrip = new() };
227 |
228 | // Atom AccordionUI ContextMenuStrip
229 | ToolStripMenuItem editAtomMenuItem = new() { Text = "Edit Atom" };
230 | editAtomMenuItem.Click += new((s, ev) =>
231 | {
232 | FrmAtom frmAtom = new(atom) { Owner = this, StartPosition = FormStartPosition.CenterParent }; ;
233 | DialogResult dialogResult = frmAtom.ShowDialog();
234 | if (dialogResult == DialogResult.OK)
235 | {
236 | Atom atomUpdated = frmAtom.Atom;
237 | atom.Name = atomUpdated.Name;
238 | atom.Color = atomUpdated.Color;
239 | atom.Radius = atomUpdated.Radius;
240 | atom.UpdateParticles(atomUpdated.Particles.Count);
241 |
242 | foreach (Force force in atomUpdated.Forces)
243 | if (atom.HasForceWith(force.Target))
244 | atom.GetForceWith(force.Target)!.Attraction = force.Attraction;
245 | else
246 | {
247 | if (atom.Name == force.Target.Name)
248 | atom.Forces.SingleOrDefault(f => f.Name == force.Name)!.Attraction = force.Attraction;
249 | }
250 | frmAtom.Atom.Dispose();
251 | frmAtom.Dispose();
252 | accordionAtomUI.Invalidate(true);
253 | }
254 | });
255 | ToolStripMenuItem removeAtomMenuItem = new() { Text = "Remove Atom" };
256 | removeAtomMenuItem.Click += new((s, ev) => Universe.RemoveAtom(atom));
257 | accordionAtomUI.ContextMenuStrip.Items.AddRange(new[] { editAtomMenuItem, removeAtomMenuItem });
258 |
259 | atom.AtomNameChanged += new((s, ev) => accordionAtomUI.Title = atom.Name);
260 |
261 | Universe.UniverseAtomRemoved += new((s, ev) =>
262 | {
263 | Atom atomRemoved = ev.Atom!;
264 | if (atomRemoved == atom)
265 | accordionAtomUI.Dispose();
266 | else
267 | atom.RemoveForceWith(atomRemoved);
268 | });
269 |
270 | Slider sldNumber = new() { MinValue = 0.0, MaxValue = 500.0, Value = atom.Particles.Count, Text = "Number" };
271 | sldNumber.OnValueChanged += new((s, ev) => atom.UpdateParticles(Convert.ToInt32(sldNumber.Value)));
272 | atom.AtomParticlesChanged += new((s, ev) => sldNumber.Value = atom.Particles.Count);
273 |
274 | Button btnResetParticles = new() { Text = "Reset Particles Position", UseVisualStyleBackColor = true, TextAlign = ContentAlignment.MiddleCenter };
275 | btnResetParticles.Click += new((s, e) => atom.ResetParticles());
276 |
277 | Slider sldRadius = new() { MinValue = 1.0, MaxValue = 10.0, Value = atom.Radius, ValueFormat = "F1", Text = "Radius" };
278 | sldRadius.OnValueChanged += new((s, ev) => atom.Radius = sldRadius.Value);
279 | atom.AtomRadiusChanged += new((s, ev) => sldRadius.Value = atom.Radius);
280 |
281 | Accordion accRadiationsUI = new() { Title = "Radiations..." };
282 | accRadiationsUI.ClientSizeChanged += new((s, ev) => accordionAtomUI.ResizeAccordionControls());
283 |
284 | Accordion accAttractionsUI = new() { Title = "Attractions..." };
285 | accAttractionsUI.ClientSizeChanged += new((s, ev) => accordionAtomUI.ResizeAccordionControls());
286 |
287 | atom.AtomForceAdded += new((s, ev) =>
288 | {
289 | Force forceAdded = ev.Force!;
290 |
291 | Slider sldRadiation = new() { Text = forceAdded.Name, MinValue = 0.0, MaxValue = 1.0, Value = forceAdded.Radiation, ValueFormat = "F2" };
292 | sldRadiation.OnValueChanged += new((s, ev) => forceAdded.Radiation = sldRadiation.Value);
293 | accRadiationsUI.Controls.Add(sldRadiation);
294 |
295 | Slider sldAttraction = new() { Text = forceAdded.Name, MinValue = -1.0, MaxValue = 1.0, Value = forceAdded.Attraction, ValueFormat = "F2" };
296 | sldAttraction.OnValueChanged += new((s, ev) => forceAdded.Attraction = sldAttraction.Value);
297 | accAttractionsUI.Controls.Add(sldAttraction);
298 |
299 | forceAdded.ForceNameChanged += new((s, ev) =>
300 | {
301 | sldRadiation.Text = forceAdded.Name;
302 | sldAttraction.Text = forceAdded.Name;
303 | });
304 | forceAdded.ForceRadiationChanged += new((s, ev) =>
305 | {
306 | sldAttraction.Value = forceAdded.Radiation;
307 | });
308 | forceAdded.ForceAttractionChanged += new((s, ev) =>
309 | {
310 | sldAttraction.Value = forceAdded.Attraction;
311 | });
312 |
313 | atom.AtomForceRemoved += new((s, ev) =>
314 | {
315 | if (ev.Force! == forceAdded)
316 | {
317 | sldRadiation.Dispose();
318 | sldAttraction.Dispose();
319 | }
320 | });
321 | atom.AtomForcesCleared += new((s, ev) =>
322 | {
323 | sldRadiation.Dispose();
324 | sldAttraction.Dispose();
325 | });
326 | });
327 |
328 | Button btnRemoveAtom = new() { Text = "Remove Atom", UseVisualStyleBackColor = true, TextAlign = ContentAlignment.MiddleCenter, BackColor = Color.Red, FlatStyle = FlatStyle.Flat, Tag = "LAST" };
329 | btnRemoveAtom.Click += new((s, e) => Universe.RemoveAtom(atom));
330 |
331 | accordionAtomUI.Controls.AddRange(new Control[]{ sldNumber, btnResetParticles, sldRadius, accRadiationsUI, accAttractionsUI, btnRemoveAtom });
332 |
333 | PanelSettings.Controls.Add(accordionAtomUI);
334 | }
335 | #endregion
336 |
337 | public void UniverseDraw(Graphics graphics, int alpha)
338 | {
339 | foreach (Atom atom in Universe.Atoms)
340 | {
341 | foreach (Particle particle in atom.Particles)
342 | {
343 | float x = (float)particle.X - (float)atom.Radius;
344 | float y = (float)particle.Y - (float)atom.Radius;
345 |
346 | float width = (float)atom.Diameter;
347 | float height = (float)atom.Diameter;
348 |
349 | ParticleDraw(graphics, alpha, atom.Color, x, y, width, height);
350 | }
351 | }
352 | }
353 | public void ParticleDraw(Graphics graphics, int alpha, Color color, float x, float y, float width, float height)
354 | {
355 | Color fill = Settings.ParticlesContraste ? ControlPaint.Dark(color) : ControlPaint.Light(color);
356 | Color draw = Settings.ParticlesContraste ? ControlPaint.Light(color) : ControlPaint.Dark(color);
357 |
358 | graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
359 | graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighSpeed;
360 |
361 | graphics.FillEllipse(new SolidBrush(Color.FromArgb(alpha, fill)), x, y, width, height);
362 | if (!Settings.ParticlesBorderless) graphics.DrawEllipse(new Pen(Color.FromArgb(alpha, draw), 1f), x, y, width, height);
363 | }
364 | public void UpdateEnvironment(long deltaTime)
365 | {
366 | ElapsedTime += deltaTime;
367 | if (ElapsedTime >= 1000)
368 | {
369 | FramesPerSecond = FrameCounter;
370 | ElapsedTime = 0;
371 | FrameCounter = 0;
372 | }
373 | FrameCounter++;
374 | }
375 | public void RenderEnvironment(long deltaTime)
376 | {
377 | if (Settings.SimulationAnimated)
378 | {
379 | using Graphics graphics = Graphics.FromImage(BackgroundImageBuffer);
380 |
381 | graphics.Clear(Settings.SimulationBackColor);
382 |
383 | for (int i = 1; i <= Settings.SimulationStepsPerFrame; i++)
384 | {
385 | int alpha = i * 255 / Settings.SimulationStepsPerFrame;
386 | Universe.Step();
387 | UniverseDraw(graphics, alpha);
388 | }
389 |
390 | BackgroundImage = BackgroundImageBuffer;
391 | Invalidate();
392 | }
393 |
394 | ToolStripStatusLabelTotalParticlesValue.Text = Universe.ParticlesCount().ToString();
395 | ToolStripStatusLabelFpsValue.Text = FramesPerSecond.ToString();
396 | }
397 | }
--------------------------------------------------------------------------------
/FrmSimulation.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | text/microsoft-resx
50 |
51 |
52 | 2.0
53 |
54 |
55 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
56 |
57 |
58 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
59 |
60 |
61 | 17, 17
62 |
63 |
--------------------------------------------------------------------------------
/ParticleLifeSimulation.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | WinExe
5 | net6.0-windows
6 | enable
7 | true
8 | enable
9 | AnyCPU
10 | False
11 | False
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | True
39 | True
40 | Resources.resx
41 |
42 |
43 | UserControl
44 |
45 |
46 |
47 |
48 |
49 | ResXFileCodeGenerator
50 | Resources.Designer.cs
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/ParticleLifeSimulation.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.2.32505.173
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ParticleLifeSimulation", "ParticleLifeSimulation.csproj", "{C191DEEA-D001-493F-9313-B5A4FCE7222E}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {C191DEEA-D001-493F-9313-B5A4FCE7222E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {C191DEEA-D001-493F-9313-B5A4FCE7222E}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {C191DEEA-D001-493F-9313-B5A4FCE7222E}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {C191DEEA-D001-493F-9313-B5A4FCE7222E}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {D00D9857-C9F0-4FD1-98CC-D5606E871A97}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/Program.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace ParticleLifeSimulation;
5 |
6 | public static class Program
7 | {
8 | ///
9 | /// The main entry point for the application.
10 | ///
11 | [STAThread]
12 | private static void Main()
13 | {
14 | // To customize application configuration such as set high DPI settings or default font,
15 | // see https://aka.ms/applicationconfiguration.
16 | Stopwatch stopwatch = new();
17 | ApplicationConfiguration.Initialize();
18 | Application.Idle += new((s, ev) =>
19 | {
20 | while (AppStillIdle)
21 | {
22 | long deltaTime = stopwatch.ElapsedMilliseconds;
23 | stopwatch.Restart();
24 | foreach (IFormLoop form in Application.OpenForms.OfType())
25 | {
26 | // Render a frame during idle time (no messages are waiting)
27 | form.UpdateEnvironment(deltaTime);
28 | form.RenderEnvironment(deltaTime);
29 | }
30 | }
31 | });
32 | Application.Run(new FrmSimulation());
33 | }
34 |
35 | [System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously
36 | [DllImport("User32.dll", CharSet = CharSet.Auto)]
37 | private static extern bool PeekMessage(out Message msg, IntPtr hWnd, uint messageFilterMin, uint messageFilterMax, uint flags);
38 | private static bool AppStillIdle => !PeekMessage(out _, IntPtr.Zero, 0, 0, 0);
39 | }
40 |
41 | internal interface IFormLoop
42 | {
43 | public void UpdateEnvironment(long deltaTime);
44 | public void RenderEnvironment(long deltaTime);
45 | }
--------------------------------------------------------------------------------
/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // Ce code a été généré par un outil.
4 | // Version du runtime :4.0.30319.42000
5 | //
6 | // Les modifications apportées à ce fichier peuvent provoquer un comportement incorrect et seront perdues si
7 | // le code est régénéré.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace ParticleLifeSimulation.Properties {
12 | using System;
13 |
14 |
15 | ///
16 | /// Une classe de ressource fortement typée destinée, entre autres, à la consultation des chaînes localisées.
17 | ///
18 | // Cette classe a été générée automatiquement par la classe StronglyTypedResourceBuilder
19 | // à l'aide d'un outil, tel que ResGen ou Visual Studio.
20 | // Pour ajouter ou supprimer un membre, modifiez votre fichier .ResX, puis réexécutez ResGen
21 | // avec l'option /str ou régénérez votre projet VS.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Resources() {
33 | }
34 |
35 | ///
36 | /// Retourne l'instance ResourceManager mise en cache utilisée par cette classe.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ParticleLifeSimulation.Properties.Resources", typeof(Resources).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Remplace la propriété CurrentUICulture du thread actuel pour toutes
51 | /// les recherches de ressources à l'aide de cette classe de ressource fortement typée.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 |
63 | ///
64 | /// Recherche une ressource localisée de type System.Drawing.Bitmap.
65 | ///
66 | internal static System.Drawing.Bitmap arrow_bottom {
67 | get {
68 | object obj = ResourceManager.GetObject("arrow_bottom", resourceCulture);
69 | return ((System.Drawing.Bitmap)(obj));
70 | }
71 | }
72 |
73 | ///
74 | /// Recherche une ressource localisée de type System.Drawing.Bitmap.
75 | ///
76 | internal static System.Drawing.Bitmap arrow_right {
77 | get {
78 | object obj = ResourceManager.GetObject("arrow_right", resourceCulture);
79 | return ((System.Drawing.Bitmap)(obj));
80 | }
81 | }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 |
122 | ..\Resources\arrow_bottom.ico;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
123 |
124 |
125 | ..\Resources\arrow_right.ico;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
126 |
127 |
--------------------------------------------------------------------------------
/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "ParticleLifeSimulation": {
4 | "commandName": "Project"
5 | }
6 | }
7 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BlinkSun/ParticleLifeSimulation/2e450fc3883f120ada4356625e3c1986650c49d0/README.md
--------------------------------------------------------------------------------
/Resources/arrow_bottom.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BlinkSun/ParticleLifeSimulation/2e450fc3883f120ada4356625e3c1986650c49d0/Resources/arrow_bottom.ico
--------------------------------------------------------------------------------
/Resources/arrow_right.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BlinkSun/ParticleLifeSimulation/2e450fc3883f120ada4356625e3c1986650c49d0/Resources/arrow_right.ico
--------------------------------------------------------------------------------
/Settings.cs:
--------------------------------------------------------------------------------
1 | using System.Configuration;
2 |
3 | namespace ParticleLifeSimulation
4 | {
5 | public class Settings : ApplicationSettingsBase
6 | {
7 | [UserScopedSetting]
8 | [DefaultSettingValue("Control")]
9 | public Color SimulationBackColor
10 | {
11 | get => (Color)this[nameof(SimulationBackColor)];
12 | set => this[nameof(SimulationBackColor)] = value;
13 | }
14 |
15 | [UserScopedSetting]
16 | [DefaultSettingValue("1")]
17 | public int SimulationStepsPerFrame
18 | {
19 | get => (int)this[nameof(SimulationStepsPerFrame)];
20 | set => this[nameof(SimulationStepsPerFrame)] = value;
21 | }
22 |
23 | [UserScopedSetting]
24 | [DefaultSettingValue("true")]
25 | public bool SimulationAnimated
26 | {
27 | get => (bool)this[nameof(SimulationAnimated)];
28 | set => this[nameof(SimulationAnimated)] = value;
29 | }
30 |
31 | [UserScopedSetting]
32 | [DefaultSettingValue("false")]
33 | public bool ParticlesContraste
34 | {
35 | get => (bool)this[nameof(ParticlesContraste)];
36 | set => this[nameof(ParticlesContraste)] = value;
37 | }
38 |
39 | [UserScopedSetting]
40 | [DefaultSettingValue("false")]
41 | public bool ParticlesBorderless
42 | {
43 | get => (bool)this[nameof(ParticlesBorderless)];
44 | set => this[nameof(ParticlesBorderless)] = value;
45 | }
46 |
47 | [UserScopedSetting]
48 | [DefaultSettingValue("false")]
49 | public bool ParticlesSystemColorsIncluded
50 | {
51 | get => (bool)this[nameof(ParticlesSystemColorsIncluded)];
52 | set => this[nameof(ParticlesSystemColorsIncluded)] = value;
53 | }
54 |
55 | [UserScopedSetting]
56 | [DefaultSettingValue("false")]
57 | public bool ParticlesCompoundColorNamesIncluded
58 | {
59 | get => (bool)this[nameof(ParticlesCompoundColorNamesIncluded)];
60 | set => this[nameof(ParticlesCompoundColorNamesIncluded)] = value;
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/UserControls/Accordion.cs:
--------------------------------------------------------------------------------
1 | using ParticleLifeSimulation.Core;
2 | using ParticleLifeSimulation.Properties;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Drawing;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 |
10 | namespace ParticleLifeSimulation.UserControls
11 | {
12 | public class Accordion : FlowLayoutPanel
13 | {
14 | public Action? ParticleDraw = null;
15 | public CheckBox ChkTitleBar;
16 | public bool IsDraggable { get; set; } = false;
17 | public Atom? Atom { get; set; } = null;
18 | public string Title
19 | {
20 | get => ChkTitleBar.Text;
21 | set => ChkTitleBar.Text = value;
22 | }
23 |
24 | public Accordion()
25 | {
26 | WrapContents = false;
27 | BorderStyle = BorderStyle.FixedSingle;
28 | FlowDirection = FlowDirection.TopDown;
29 | BackColor = SystemColors.Control;
30 | ControlAdded += new((s, cev) => ResizeAccordionControls());
31 | ControlRemoved += new((s, cev) => ResizeAccordionControls());
32 | ClientSizeChanged += new((s, ev) => ResizeAccordionControls());
33 |
34 | ChkTitleBar = new()
35 | {
36 | Appearance = Appearance.Button,
37 | FlatStyle = FlatStyle.Flat,
38 | TextAlign = ContentAlignment.MiddleLeft,
39 | TextImageRelation = TextImageRelation.ImageBeforeText,
40 | ImageAlign = ContentAlignment.MiddleLeft,
41 | UseVisualStyleBackColor = true,
42 | Tag = "TITLEBAR",
43 | Margin = new(0),
44 | };
45 | ChkTitleBar.FlatAppearance.BorderSize = 0;
46 | ChkTitleBar.MouseDown += new((s, mev) =>
47 | {
48 | if (IsDraggable)
49 | {
50 | RectangleF rectangleF = new(ChkTitleBar.ClientSize.Width - ChkTitleBar.ClientSize.Height, 0, ChkTitleBar.ClientSize.Height, ChkTitleBar.ClientSize.Height);
51 | if (rectangleF.Contains(mev.Location)) DoDragDrop(this, DragDropEffects.Move);
52 | }
53 | });
54 | ChkTitleBar.Paint += new((s, pev) =>
55 | {
56 | if (Atom is not null && ParticleDraw is not null)
57 | {
58 | Bitmap bitmap = new(ChkTitleBar.ClientSize.Height, ChkTitleBar.ClientSize.Height);
59 | using (Graphics graphics = Graphics.FromImage(bitmap))
60 | {
61 | graphics.Clear(Color.Transparent);
62 | float x = bitmap.Width * 0.25f;
63 | float y = bitmap.Height * 0.25f;
64 | float width = bitmap.Width / 2.0f;
65 | float height = bitmap.Height / 2.0f;
66 | ParticleDraw(graphics, 255, Atom.Color, x, y, width, height);
67 | }
68 | pev.Graphics.DrawImage(bitmap, ChkTitleBar.ClientSize.Width - ChkTitleBar.ClientSize.Height, 0, ChkTitleBar.ClientSize.Height, ChkTitleBar.ClientSize.Height);
69 | }
70 | });
71 | ChkTitleBar.CheckedChanged += new((s, ev) => ResizeAccordionControls());
72 |
73 | Controls.Add(ChkTitleBar);
74 | }
75 |
76 | public void ResizeAccordionControls()
77 | {
78 | Size iconSize = new(ChkTitleBar.ClientSize.Height / 4 * 3, ChkTitleBar.ClientSize.Height / 4 * 3);
79 | IEnumerable resetChildIndexes = Enumerable.Empty();
80 |
81 | //width
82 | foreach (Control control in Controls)
83 | control.Width = ClientSize.Width - control.Margin.Right - control.Margin.Left;
84 |
85 | //height
86 | if (ChkTitleBar.Checked)
87 | {
88 | Bitmap bitmap = new(Resources.arrow_bottom, iconSize);
89 | ChkTitleBar.Image = bitmap;
90 | int height = Margin.Top;
91 | foreach (Control control in Controls)
92 | {
93 | height += control.Height + control.Margin.Top + control.Margin.Bottom;
94 | if (control.Tag is string) resetChildIndexes = resetChildIndexes.Append(control);
95 | }
96 | Height = height;
97 | }
98 | else
99 | {
100 | Bitmap bitmap = new(Resources.arrow_right, iconSize);
101 | ChkTitleBar.Image = bitmap;
102 | Height = ChkTitleBar.ClientSize.Height + 2;
103 | }
104 |
105 | //position
106 | foreach (Control control in resetChildIndexes)
107 | {
108 | switch ((string)control.Tag)
109 | {
110 | case "TITLEBAR":
111 | Controls.SetChildIndex(control, 0);
112 | break;
113 | case "LAST":
114 | Controls.SetChildIndex(control, Controls.Count - 1);
115 | break;
116 | }
117 | }
118 | }
119 |
120 | protected override void Dispose(bool disposing)
121 | {
122 | if (disposing)
123 | {
124 | ChkTitleBar.Dispose();
125 | ParticleDraw = null;
126 | Atom = null;
127 | }
128 |
129 | base.Dispose(disposing);
130 | }
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/UserControls/Slider.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace ParticleLifeSimulation.UserControls
2 | {
3 | partial class Slider
4 | {
5 | ///
6 | /// Variable nécessaire au concepteur.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Nettoyage des ressources utilisées.
12 | ///
13 | /// true si les ressources managées doivent être supprimées ; sinon, false.
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Code généré par le Concepteur de composants
24 |
25 | ///
26 | /// Méthode requise pour la prise en charge du concepteur - ne modifiez pas
27 | /// le contenu de cette méthode avec l'éditeur de code.
28 | ///
29 | private void InitializeComponent()
30 | {
31 | this.SuspendLayout();
32 | //
33 | // Slider
34 | //
35 | this.Name = "Slider";
36 | this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.Slider_MouseUpDownMove);
37 | this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.Slider_MouseUpDownMove);
38 | this.MouseUp += new System.Windows.Forms.MouseEventHandler(this.Slider_MouseUpDownMove);
39 | this.ResumeLayout(false);
40 |
41 | }
42 |
43 | #endregion
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/UserControls/Slider.cs:
--------------------------------------------------------------------------------
1 | namespace ParticleLifeSimulation.UserControls
2 | {
3 | public partial class Slider : UserControl
4 | {
5 | #region Events
6 | public event EventHandler? OnValueChanged;
7 | #endregion
8 |
9 | #region Properties
10 | public bool AutoEllipsis { get; set; } = true;
11 | public bool AutoWidth { get; set; } = false;
12 | public bool AutoHeight { get; set; } = true;
13 | public double MinValue { get; set; } = 0.0;
14 | public double MaxValue { get; set; } = 10.0;
15 | private double _value = 5.0;
16 | public double Value
17 | {
18 | get { return _value; }
19 | set
20 | {
21 | if (_value != Math.Clamp(value, MinValue, MaxValue))
22 | {
23 | _value = Math.Clamp(value, MinValue, MaxValue);
24 | Invalidate();
25 | OnValueChanged?.Invoke(this, new EventArgs());
26 | }
27 | }
28 | }
29 | private Color _sliderColor = SystemColors.Highlight;
30 | public Color SliderColor
31 | {
32 | get { return _sliderColor; }
33 | set
34 | {
35 | _sliderColor = value;
36 | Invalidate();
37 | }
38 | }
39 | public string ValueFormat { get; set; } = "F0";
40 |
41 | #endregion
42 |
43 | #region Constructors
44 | public Slider()
45 | {
46 | InitializeComponent();
47 | }
48 | #endregion
49 |
50 | #region Methodes
51 | protected override void OnTextChanged(EventArgs e)
52 | {
53 | base.OnTextChanged(e);
54 | SizeF textSizeF = Graphics.FromHwndInternal(Handle).MeasureString($"{Text} {Value.ToString(ValueFormat)}", Font);
55 | if (AutoWidth || AutoHeight)
56 | {
57 | if (AutoWidth) Width = Convert.ToInt32(textSizeF.Width);
58 | if (AutoHeight) Height = Convert.ToInt32(textSizeF.Height);
59 | Invalidate();
60 | }
61 | }
62 | protected override void OnPaint(PaintEventArgs e)
63 | {
64 | base.OnPaint(e);
65 | // Slider Bar
66 | StringFormat formatter = new(StringFormatFlags.NoWrap) { LineAlignment = StringAlignment.Center };
67 | Brush brush = new SolidBrush(SliderColor);
68 | double normalizeValue = MinValue * -1;
69 | int x = (int)((Value + normalizeValue) * Width / (MaxValue + normalizeValue));
70 | e.Graphics.FillRectangle(brush, new Rectangle(0, 0, x, Height));
71 |
72 | // Slider Texts:
73 | string value = Value.ToString(ValueFormat);
74 | SizeF valueSizeF = e.Graphics.MeasureString(value, Font);
75 | // - Text
76 | RectangleF textRectF = new(0f, 0f, Width - (AutoEllipsis ? valueSizeF.Width : 0), Height);
77 | formatter.Alignment = StringAlignment.Near;
78 | if (AutoEllipsis) formatter.Trimming = StringTrimming.EllipsisCharacter;
79 | e.Graphics.DrawString(Text, Font, new SolidBrush(ForeColor), textRectF, formatter);
80 | // - Value
81 | RectangleF valueRectF = new(Width - valueSizeF.Width, 0f, valueSizeF.Width, Height);
82 | formatter.Alignment = StringAlignment.Far;
83 | formatter.Trimming = StringTrimming.Character;
84 | e.Graphics.DrawString(value, Font, new SolidBrush(ForeColor), valueRectF, formatter);
85 | }
86 | private void Slider_MouseUpDownMove(object sender, MouseEventArgs e)
87 | {
88 | if (e.Button == MouseButtons.Left)
89 | {
90 | double normValue = MinValue * -1;
91 | Value = (e.X * (MaxValue + normValue) / Width) - normValue;
92 | }
93 | }
94 | #endregion
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/images/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BlinkSun/ParticleLifeSimulation/2e450fc3883f120ada4356625e3c1986650c49d0/images/screenshot.png
--------------------------------------------------------------------------------