aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/libraries/ModifiedBulletX/ModifiedBulletX/Collision/NarrowPhaseCollision/GjkEpa.cs
diff options
context:
space:
mode:
Diffstat (limited to 'libraries/ModifiedBulletX/ModifiedBulletX/Collision/NarrowPhaseCollision/GjkEpa.cs')
-rw-r--r--libraries/ModifiedBulletX/ModifiedBulletX/Collision/NarrowPhaseCollision/GjkEpa.cs633
1 files changed, 633 insertions, 0 deletions
diff --git a/libraries/ModifiedBulletX/ModifiedBulletX/Collision/NarrowPhaseCollision/GjkEpa.cs b/libraries/ModifiedBulletX/ModifiedBulletX/Collision/NarrowPhaseCollision/GjkEpa.cs
new file mode 100644
index 0000000..50ae7ab
--- /dev/null
+++ b/libraries/ModifiedBulletX/ModifiedBulletX/Collision/NarrowPhaseCollision/GjkEpa.cs
@@ -0,0 +1,633 @@
1/*
2 Bullet for XNA Copyright (c) 2003-2007 Vsevolod Klementjev http://www.codeplex.com/xnadevru
3 Bullet original C++ version Copyright (c) 2003-2007 Erwin Coumans http://bulletphysics.com
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21
22using System;
23using System.Collections.Generic;
24using System.Text;
25using MonoXnaCompactMaths;
26
27namespace XnaDevRu.BulletX
28{
29 /// <summary>
30 /// GJK-EPA collision solver by Nathanael Presson
31 /// Nov.2006
32 /// </summary>
33 public class GjkEpa
34 {
35 //private static readonly int _precision = 1 /* U(sizeof(F) == 4)*/;
36
37 private static readonly float _infinity = MathHelper.Infinity;
38 //private static readonly float _pi = (float)Math.PI;
39 private static readonly float _twoPi = (float)(Math.PI * 2);
40
41 private static readonly int _gjkMaxIterations = 128;
42 private static readonly int _gjkHashSize = 1 << 6;
43 private static readonly int _gjkHashMask = _gjkHashSize - 1;
44 private static readonly float _gjkInSimplexEpsilon = 0.0001f;
45 private static readonly float _gjkSquareInSimplexEpsilon = _gjkInSimplexEpsilon * _gjkInSimplexEpsilon;
46
47 private static readonly int _epaMaxIterations = 256;
48 private static readonly float _epaInFaceEpsilon = 0.01f;
49 private static readonly float _epaAccuracy = 0.001f;
50
51 public static float EpaAccuracy { get { return _epaAccuracy; } }
52
53 private static float Abs(float v) { return (v < 0 ? -v : v); }
54 private static float Sign(float v) { return (v < 0 ? -1 : 1); }
55
56 static void Swap<T>(ref T a, ref T b)
57 {
58 T t = a;
59 a = b;
60 b = t;
61 }
62
63 public class Gjk
64 {
65 public class MinkowskiVertice
66 {
67 private Vector3 _vertice; /* Minkowski vertice */
68 private Vector3 _ray; /* Ray */
69
70 public Vector3 Vertice { get { return _vertice; } set { _vertice = value; } }
71 public Vector3 Ray { get { return _ray; } set { _ray = value; } }
72 }
73 public class He
74 {
75 private Vector3 _ray;
76 private He _next;
77
78 public He Next { get { return _next; } set { _next = value; } }
79 public Vector3 Ray { get { return _ray; } set { _ray = value; } }
80 }
81
82 private He[] _table = new He[_gjkHashSize];
83 private Matrix[] _wrotations = new Matrix[2];
84 private Vector3[] _positions = new Vector3[2];
85 private ConvexShape[] _shapes = new ConvexShape[2];
86 private MinkowskiVertice[] _simplex = new MinkowskiVertice[5];
87 private Vector3 _ray;
88 private int _order;
89 private int _iterations;
90 private float _margin;
91 private bool _failed;
92
93 public Gjk(Matrix wrotationA, Vector3 positionA, ConvexShape shapeA,
94 Matrix wrotationB, Vector3 positionB, ConvexShape shapeB)
95 : this(wrotationA, positionA, shapeA, wrotationB, positionB, shapeB, 0) { }
96
97 public Gjk(Matrix wrotationA, Vector3 positionA, ConvexShape shapeA,
98 Matrix wrotationB, Vector3 positionB, ConvexShape shapeB,
99 float pmargin)
100 {
101 for (int i = 0; i < _simplex.Length; i++)
102 _simplex[i] = new MinkowskiVertice();
103
104 for (int i = 0; i < _wrotations.Length; i++)
105 _wrotations[i] = new Matrix();
106
107 for (int i = 0; i < _positions.Length; i++)
108 _positions[i] = new Vector3();
109
110 _wrotations[0] = wrotationA; _positions[0] = positionA;
111 _shapes[0] = shapeA;
112 _wrotations[0].Translation = Vector3.Zero;
113 _wrotations[1] = wrotationB; _positions[1] = positionB;
114 _shapes[1] = shapeB;
115 _wrotations[1].Translation = Vector3.Zero;
116 //sablock = sa->BeginBlock();
117 _margin = pmargin;
118 _failed = false;
119 }
120
121 public bool Failed { get { return _failed; } }
122 public int Iterations { get { return _iterations; } }
123 public int Order { get { return _order; } }
124 public MinkowskiVertice[] Simplex { get { return _simplex; } }
125
126 public int Hash(Vector3 v)
127 {
128 int h = ((int)(v.X * 15461) ^ (int)(v.Y * 83003) ^ (int)(v.Z * 15473));
129 return (h * 169639) & _gjkHashMask;
130 }
131
132 public bool FetchSupport()
133 {
134 int h = Hash(_ray);
135 He e = _table[h];
136 while (e != null)
137 {
138 if (e.Ray == _ray)
139 {
140 --_order;
141 return (false);
142 }
143 else e = e.Next;
144 }
145 e = new He();
146 e.Ray = _ray;
147 e.Next = _table[h];
148 _table[h] = e;
149 Support(_ray, ref _simplex[++_order]);
150 return (Vector3.Dot(_ray, _simplex[_order].Vertice) > 0);
151 }
152
153 public Vector3 LocalSupport(Vector3 d, int i)
154 {
155 Matrix m = _wrotations[i];
156 m.Translation = Vector3.Zero;
157 Vector3 vtx = Vector3.TransformNormal(d, m);
158 Vector3 result = MathHelper.MatrixToVector(_wrotations[i], _shapes[i].LocalGetSupportingVertex(vtx));
159 return (result + _positions[i]);
160 }
161
162 public void Support(Vector3 d, ref MinkowskiVertice v)
163 {
164 v.Ray = d;
165 v.Vertice = LocalSupport(d, 0) - LocalSupport(-d, 1) + d * _margin;
166 }
167
168 public bool SolveSimplex2(Vector3 ao, Vector3 ab)
169 {
170 if (Vector3.Dot(ab, ao) >= 0)
171 {
172 Vector3 cabo = Vector3.Cross(ab, ao);
173 if (cabo.LengthSquared() > _gjkSquareInSimplexEpsilon)
174 { _ray = Vector3.Cross(cabo, ab); }
175 else
176 { return true; }
177 }
178 else
179 {
180 _order = 0;
181 _simplex[0].Ray = _simplex[1].Ray;
182 _simplex[0].Vertice = _simplex[1].Vertice;
183
184 _ray = ao;
185 }
186 return false;
187 }
188
189 public bool SolveSimplex3(Vector3 ao, Vector3 ab, Vector3 ac)
190 {
191 return (SolveSimplex3a(ao, ab, ac, Vector3.Cross(ab, ac)));
192 }
193
194 public bool SolveSimplex3a(Vector3 ao, Vector3 ab, Vector3 ac, Vector3 cabc)
195 {
196 if ((Vector3.Dot(Vector3.Cross(cabc, ab), ao)) < -_gjkInSimplexEpsilon)
197 {
198 _order = 1;
199 _simplex[0].Vertice = _simplex[1].Vertice;
200 _simplex[0].Ray = _simplex[1].Ray;
201
202 _simplex[1].Vertice = _simplex[2].Vertice;
203 _simplex[1].Ray = _simplex[2].Ray;
204
205 return (SolveSimplex2(ao, ab));
206 }
207 else if (Vector3.Dot(Vector3.Cross(cabc, ac), ao) > +_gjkInSimplexEpsilon)
208 {
209 _order = 1;
210 _simplex[1].Vertice = _simplex[2].Vertice;
211 _simplex[1].Ray = _simplex[2].Ray;
212
213 return (SolveSimplex2(ao, ac));
214 }
215 else
216 {
217 float d = Vector3.Dot(cabc, ao);
218 if (Abs(d) > _gjkInSimplexEpsilon)
219 {
220 if (d > 0)
221 { _ray = cabc; }
222 else
223 { _ray = -cabc; Swap<MinkowskiVertice>(ref _simplex[0], ref _simplex[1]); }
224 return (false);
225 }
226 else return (true);
227 }
228 }
229
230 public bool SolveSimplex4(Vector3 ao, Vector3 ab, Vector3 ac, Vector3 ad)
231 {
232 Vector3 crs;
233 if (Vector3.Dot((crs = Vector3.Cross(ab, ac)), ao) > _gjkInSimplexEpsilon)
234 {
235 _order = 2;
236 _simplex[0].Vertice = _simplex[1].Vertice;
237 _simplex[0].Ray = _simplex[1].Ray;
238
239 _simplex[1].Vertice = _simplex[2].Vertice;
240 _simplex[1].Ray = _simplex[2].Ray;
241
242 _simplex[2].Vertice = _simplex[3].Vertice;
243 _simplex[2].Ray = _simplex[3].Ray;
244
245 return (SolveSimplex3a(ao, ab, ac, crs));
246 }
247 else if (Vector3.Dot((crs = Vector3.Cross(ac, ad)), ao) > _gjkInSimplexEpsilon)
248 {
249 _order = 2;
250 _simplex[2].Vertice = _simplex[3].Vertice;
251 _simplex[2].Ray = _simplex[3].Ray;
252
253 return (SolveSimplex3a(ao, ac, ad, crs));
254 }
255 else if (Vector3.Dot((crs = Vector3.Cross(ad, ab)), ao) > _gjkInSimplexEpsilon)
256 {
257 _order = 2;
258
259 _simplex[1].Vertice = _simplex[0].Vertice;
260 _simplex[1].Ray = _simplex[0].Ray;
261
262 _simplex[0].Vertice = _simplex[2].Vertice;
263 _simplex[0].Ray = _simplex[2].Ray;
264
265 _simplex[2].Vertice = _simplex[3].Vertice;
266 _simplex[2].Ray = _simplex[3].Ray;
267
268 return (SolveSimplex3a(ao, ad, ab, crs));
269 }
270 else return (true);
271 }
272
273 public bool SearchOrigin()
274 {
275 return SearchOrigin(new Vector3(1, 0, 0));
276 }
277
278 public bool SearchOrigin(Vector3 initray)
279 {
280 _iterations = 0;
281 unchecked
282 {
283 _order = (int)(-1);
284 }
285 _failed = false;
286 _ray = Vector3.Normalize(initray);
287
288 //ClearMemory(table, sizeof(void*) * GJK_hashsize);
289 for (int i = 0; i < _table.Length; i++)
290 _table[i] = null;
291 FetchSupport();
292 _ray = -_simplex[0].Vertice;
293 for (; _iterations < _gjkMaxIterations; ++_iterations)
294 {
295 float rl = _ray.Length();
296 _ray /= rl > 0 ? rl : 1;
297 if (FetchSupport())
298 {
299 bool found = (false);
300 switch (_order)
301 {
302 case 1: found = SolveSimplex2(-_simplex[1].Vertice, _simplex[0].Vertice - _simplex[1].Vertice); break;
303 case 2: found = SolveSimplex3(-_simplex[2].Vertice, _simplex[1].Vertice - _simplex[2].Vertice, _simplex[0].Vertice - _simplex[2].Vertice); break;
304 case 3: found = SolveSimplex4(-_simplex[3].Vertice, _simplex[2].Vertice - _simplex[3].Vertice, _simplex[1].Vertice - _simplex[3].Vertice, _simplex[0].Vertice - _simplex[3].Vertice); break;
305 }
306 if (found) return (true);
307 }
308 else return (false);
309 }
310 _failed = true;
311 return (false);
312 }
313
314 public bool EncloseOrigin()
315 {
316 switch (_order)
317 {
318 /* Point */
319 case 0: break;
320 /* Line */
321 case 1:
322 Vector3 ab = _simplex[1].Vertice - _simplex[0].Vertice;
323 Vector3[] b ={ Vector3.Cross(ab, new Vector3(1, 0, 0)),
324 Vector3.Cross(ab, new Vector3(0, 1, 0)),
325 Vector3.Cross(ab, new Vector3(0, 0, 1)) };
326 float[] m ={ b[0].LengthSquared(), b[1].LengthSquared(), b[2].LengthSquared() };
327 Matrix r = Matrix.CreateFromQuaternion(new Quaternion(Vector3.Normalize(ab), _twoPi / 3));
328 Vector3 w = b[m[0] > m[1] ? m[0] > m[2] ? 0 : 2 : m[1] > m[2] ? 1 : 2];
329 Support(Vector3.Normalize(w), ref _simplex[4]); w = Vector3.TransformNormal(w, r);
330 Support(Vector3.Normalize(w), ref _simplex[2]); w = Vector3.TransformNormal(w, r);
331 Support(Vector3.Normalize(w), ref _simplex[3]); w = Vector3.TransformNormal(w, r);
332 _order = 4;
333 return true;
334 /* Triangle */
335 case 2:
336 Vector3 n = Vector3.Normalize(Vector3.Cross(_simplex[1].Vertice - _simplex[0].Vertice, _simplex[2].Vertice - _simplex[0].Vertice));
337 Support(n, ref _simplex[3]);
338 Support(-n, ref _simplex[4]);
339 _order = 4;
340 return true;
341 /* Tetrahedron */
342 case 3: return (true);
343 /* Hexahedron */
344 case 4: return (true);
345 }
346 return (false);
347 }
348 }
349
350 public class Epa
351 {
352 public class Face
353 {
354 public Gjk.MinkowskiVertice[] _vertices = new Gjk.MinkowskiVertice[3];
355 public Face[] _faces = new Face[3];
356 public int[] _e = new int[3];
357 public Vector3 _n;
358 public float _d;
359 public int _mark;
360 public Face _prev;
361 public Face _next;
362 }
363
364 private Gjk _gjk;
365 private Face _root;
366 private int _nfaces;
367 private int _iterations;
368 private Vector3[,] _features = new Vector3[2, 3];
369 private Vector3[] _nearest = new Vector3[2];
370 private Vector3 _normal;
371 private float _depth;
372 private bool _failed;
373
374 public Epa(Gjk gjk)
375 {
376 this._gjk = gjk;
377 }
378
379 public bool Failed { get { return _failed; } }
380 public int Iterations { get { return _iterations; } }
381 public Vector3 Normal { get { return _normal; } }
382 public Vector3[] Nearest { get { return _nearest; } }
383
384 public Vector3 GetCoordinates(Face face)
385 {
386 Vector3 o = face._n * -face._d;
387 float[] a ={ Vector3.Cross(face._vertices[0].Vertice - o, face._vertices[1].Vertice - o).Length(),
388 Vector3.Cross(face._vertices[1].Vertice - o, face._vertices[2].Vertice - o).Length(),
389 Vector3.Cross(face._vertices[2].Vertice - o, face._vertices[0].Vertice - o).Length()};
390 float sm = a[0] + a[1] + a[2];
391 return (new Vector3(a[1], a[2], a[0]) / (sm > 0 ? sm : 1));
392 }
393
394 public Face FindBest()
395 {
396 Face bf = null;
397 if (_root != null)
398 {
399 Face cf = _root;
400 float bd = _infinity;
401 do
402 {
403 if (cf._d < bd) { bd = cf._d; bf = cf; }
404 } while (null != (cf = cf._next));
405 }
406 return bf;
407 }
408
409 public bool Set(ref Face f, Gjk.MinkowskiVertice a, Gjk.MinkowskiVertice b, Gjk.MinkowskiVertice c)
410 {
411 Vector3 nrm = Vector3.Cross(b.Vertice - a.Vertice, c.Vertice - a.Vertice);
412 float len = nrm.Length();
413 bool valid = (Vector3.Dot(Vector3.Cross(a.Vertice, b.Vertice), nrm) >= -_epaInFaceEpsilon &&
414 Vector3.Dot(Vector3.Cross(b.Vertice, c.Vertice), nrm) >= -_epaInFaceEpsilon &&
415 Vector3.Dot(Vector3.Cross(c.Vertice, a.Vertice), nrm) >= -_epaInFaceEpsilon);
416 f._vertices[0] = a;
417 f._vertices[1] = b;
418 f._vertices[2] = c;
419 f._mark = 0;
420 f._n = nrm / (len > 0 ? len : _infinity);
421 f._d = Max(0, -Vector3.Dot(f._n, a.Vertice));
422 return valid;
423 }
424
425 public Face NewFace(Gjk.MinkowskiVertice a, Gjk.MinkowskiVertice b, Gjk.MinkowskiVertice c)
426 {
427 Face pf = new Face();
428 if (Set(ref pf, a, b, c))
429 {
430 if (_root != null) _root._prev = pf;
431 pf._prev = null;
432 pf._next = _root;
433 _root = pf;
434 ++_nfaces;
435 }
436 else
437 {
438 pf._prev = pf._next = null;
439 }
440 return (pf);
441 }
442
443 public void Detach(ref Face face)
444 {
445 if (face._prev != null || face._next != null)
446 {
447 --_nfaces;
448 if (face == _root)
449 {
450 _root = face._next;
451 _root._prev = null;
452 }
453 else
454 {
455 if (face._next == null)
456 {
457 face._prev._next = null;
458 }
459 else
460 {
461 face._prev._next = face._next;
462 face._next._prev = face._prev;
463 }
464 }
465 face._prev = face._next = null;
466 }
467 }
468
469 public void Link(ref Face f0, int e0, ref Face f1, int e1)
470 {
471 f0._faces[e0] = f1; f1._e[e1] = e0;
472 f1._faces[e1] = f0; f0._e[e0] = e1;
473 }
474
475 public Gjk.MinkowskiVertice Support(Vector3 w)
476 {
477 Gjk.MinkowskiVertice v = new Gjk.MinkowskiVertice();
478 _gjk.Support(w, ref v);
479 return v;
480 }
481
482 private static int[] mod3 ={ 0, 1, 2, 0, 1 };
483
484 public int BuildHorizon(int markid, Gjk.MinkowskiVertice w, ref Face f, int e, ref Face cf, ref Face ff)
485 {
486 int ne = (0);
487 if (f._mark != markid)
488 {
489 int e1 = (mod3[e + 1]);
490 if ((Vector3.Dot(f._n, w.Vertice) + f._d) > 0)
491 {
492 Face nf = NewFace(f._vertices[e1], f._vertices[e], w);
493 Link(ref nf, 0, ref f, e);
494 if (cf != null) Link(ref cf, 1, ref nf, 2); else ff = nf;
495 cf = nf; ne = 1;
496 }
497 else
498 {
499 int e2 = (mod3[e + 2]);
500 Detach(ref f);
501 f._mark = markid;
502 ne += BuildHorizon(markid, w, ref f._faces[e1], f._e[e1], ref cf, ref ff);
503 ne += BuildHorizon(markid, w, ref f._faces[e2], f._e[e2], ref cf, ref ff);
504 }
505 }
506 return (ne);
507 }
508
509 public float EvaluatePD()
510 {
511 return EvaluatePD(_epaAccuracy);
512 }
513
514 private int[,] fidx;
515 private int[,] eidx;
516
517 public float EvaluatePD(float accuracy)
518 {
519 //Block* sablock = sa->BeginBlock();
520 Face bestface = null;
521 int markid = 1;
522 _depth = -_infinity;
523 _normal = new Vector3();
524 _root = null;
525 _nfaces = 0;
526 _iterations = 0;
527 _failed = false;
528 /* Prepare hull */
529 if (_gjk.EncloseOrigin())
530 {
531 int nfidx = 0;
532 int neidx = 0;
533 Gjk.MinkowskiVertice[] basemkv = new Gjk.MinkowskiVertice[5];
534 Face[] basefaces = new Face[6];
535 switch (_gjk.Order)
536 {
537 /* Tetrahedron */
538 case 3:
539 {
540 fidx = new int[,] { { 2, 1, 0 }, { 3, 0, 1 }, { 3, 1, 2 }, { 3, 2, 0 } };
541 eidx = new int[,] { { 0, 0, 2, 1 }, { 0, 1, 1, 1 }, { 0, 2, 3, 1 }, { 1, 0, 3, 2 }, { 2, 0, 1, 2 }, { 3, 0, 2, 2 } };
542 nfidx = 4; neidx = 6;
543 } break;
544 /* Hexahedron */
545 case 4:
546 {
547 fidx = new int[,] { { 2, 0, 4 }, { 4, 1, 2 }, { 1, 4, 0 }, { 0, 3, 1 }, { 0, 2, 3 }, { 1, 3, 2 } };
548 eidx = new int[,] { { 0, 0, 4, 0 }, { 0, 1, 2, 1 }, { 0, 2, 1, 2 }, { 1, 1, 5, 2 }, { 1, 0, 2, 0 }, { 2, 2, 3, 2 }, { 3, 1, 5, 0 }, { 3, 0, 4, 2 }, { 5, 1, 4, 1 } };
549 nfidx = 6; neidx = 9;
550 } break;
551 }
552 int i;
553
554 for (i = 0; i <= _gjk.Order; ++i)
555 {
556 //basemkv[i] = (GJK::Mkv*)sa->Allocate(sizeof(GJK::Mkv));
557 basemkv[i] = new Gjk.MinkowskiVertice();
558 basemkv[i].Vertice = _gjk.Simplex[i].Vertice;
559 basemkv[i].Ray = _gjk.Simplex[i].Ray;
560 }
561 for (i = 0; i < nfidx; ++i)
562 {
563 basefaces[i] = NewFace(basemkv[fidx[i, 0]], basemkv[fidx[i, 1]], basemkv[fidx[i, 2]]);
564 }
565 for (i = 0; i < neidx; ++i)
566 {
567 Link(ref basefaces[eidx[i, 0]], eidx[i, 1], ref basefaces[eidx[i, 2]], eidx[i, 3]);
568 }
569 }
570 if (0 == _nfaces)
571 {
572 return _depth;
573 }
574 /* Expand hull */
575 for (; _iterations < _epaMaxIterations; ++_iterations)
576 {
577 Face bf = FindBest();
578 if (bf != null)
579 {
580 Gjk.MinkowskiVertice w = Support(-bf._n);
581 float d = Vector3.Dot(bf._n, w.Vertice) + bf._d;
582 bestface = bf;
583 if (d < -accuracy)
584 {
585 Face cf = null;
586 Face ff = null;
587 int nf = 0;
588 Detach(ref bf);
589 bf._mark = ++markid;
590 for (int i = 0; i < 3; ++i)
591 {
592 nf += BuildHorizon(markid, w, ref bf._faces[i], bf._e[i], ref cf, ref ff);
593 }
594 if (nf <= 2) { break; }
595 Link(ref cf, 1, ref ff, 2);
596 }
597 else break;
598 }
599 else break;
600 }
601 /* Extract contact */
602 if (bestface != null)
603 {
604 Vector3 b = GetCoordinates(bestface);
605 _normal = bestface._n;
606 _depth = Max(0, bestface._d);
607 for (int i = 0; i < 2; ++i)
608 {
609 float s = i != 0 ? -1 : 1;
610 for (int j = 0; j < 3; ++j)
611 {
612 _features[i, j] = _gjk.LocalSupport(s * bestface._vertices[j].Ray, i);
613 }
614 }
615 _nearest[0] = _features[0, 0] * b.X + _features[0, 1] * b.Y + _features[0, 2] * b.Z;
616 _nearest[1] = _features[1, 0] * b.X + _features[1, 1] * b.Y + _features[1, 2] * b.Z;
617 }
618 else _failed = true;
619 return _depth;
620 }
621
622 private float Max(float a, float b)
623 {
624 return (a > b ? a : b);
625 }
626
627 private float Min(float a, float b)
628 {
629 return (a < b ? a : b);
630 }
631 }
632 }
633}