[NUI.Physics2D] Add binding for Chipmunk2D physics engine
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI.Physics2D / src / public / chipmunk / Body.cs
1 /*
2  * Copyright (c) 2023 Codefoco (codefoco@codefoco.com)
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  * 
11  * The above copyright notice and this permission notice shall be included in all
12  * copies or substantial portions of the Software.
13  * 
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20  * SOFTWARE.
21  */
22
23 using System;
24 using System.ComponentModel;
25 using System.Runtime.InteropServices;
26 using System.Collections.Generic;
27
28 #if __IOS__ || __TVOS__ || __WATCHOS__ || __MACCATALYST__
29 using ObjCRuntime;
30 #endif
31
32 using cpBody = System.IntPtr;
33 using cpArbiter = System.IntPtr;
34 using cpConstraint = System.IntPtr;
35 using cpShape = System.IntPtr;
36 using cpSpace = System.IntPtr;
37 using cpDataPointer = System.IntPtr;
38 using System.Diagnostics;
39
40 namespace Tizen.NUI.Physics2D.Chipmunk
41 {
42     /// <summary>
43     /// Mass and moment are ignored when <see cref="BodyType"/> is <see cref="BodyType.Kinematic"/>
44     /// or <see cref="BodyType.Static"/>. Guessing the mass for a body is usually fine, but guessing
45     /// a moment of inertia can lead to a very poor simulation. It’s recommended to use Chipmunk’s
46     /// moment-calculating functions to estimate the moment for you.
47     /// </summary>
48     [EditorBrowsable(EditorBrowsableState.Never)]
49     public class Body : IDisposable
50     {
51 #pragma warning disable IDE0032
52         private readonly cpBody body;
53 #pragma warning restore IDE0032
54
55         /// <summary>
56         /// The native handle.
57         /// </summary>
58         [EditorBrowsable(EditorBrowsableState.Never)]
59         public cpBody Handle => body;
60
61         /// <summary>
62         /// Create a Dynamic Body with no mass and no moment.
63         /// </summary>
64         [EditorBrowsable(EditorBrowsableState.Never)]
65         public Body()
66             : this(BodyType.Dynamic)
67         {
68         }
69
70         internal Body(cpBody handle)
71         {
72             body = handle;
73             RegisterUserData();
74         }
75
76         /// <summary>
77         ///  Create a <see cref="Body"/> of the given <see cref="BodyType"/>.
78         /// </summary>
79         [EditorBrowsable(EditorBrowsableState.Never)]
80         public Body(BodyType type)
81         {
82             body = InitializeBody(type);
83             RegisterUserData();
84         }
85
86         /// <summary>
87         /// Creates a body with the given mass and moment.
88         /// </summary>
89         [EditorBrowsable(EditorBrowsableState.Never)]
90         public Body(double mass, double moment) : this(mass, moment, BodyType.Dynamic)
91         {
92         }
93
94         /// <summary>
95         /// Creates a body with the given mass and moment, of the give <see cref="BodyType"/>.
96         /// </summary>
97         [EditorBrowsable(EditorBrowsableState.Never)]
98         public Body(double mass, double moment, BodyType type)
99         {
100             body = InitializeBody(type);
101             NativeMethods.cpBodySetMass(body, mass);
102             NativeMethods.cpBodySetMoment(body, moment);
103             RegisterUserData();
104         }
105
106         void RegisterUserData()
107         {
108             cpDataPointer pointer = NativeInterop.RegisterHandle(this);
109             NativeMethods.cpBodySetUserData(body, pointer);
110         }
111
112         void ReleaseUserData()
113         {
114             cpDataPointer pointer = NativeMethods.cpBodyGetUserData(body);
115             NativeInterop.ReleaseHandle(pointer);
116         }
117
118         /// <summary>
119         /// Get a <see cref="Body"/> object from a native cpBody handle.
120         /// </summary>
121         [EditorBrowsable(EditorBrowsableState.Never)]
122         public static Body FromHandle(cpBody body)
123         {
124             cpDataPointer handle = NativeMethods.cpBodyGetUserData(body);
125             return NativeInterop.FromIntPtr<Body>(handle);
126         }
127
128         /// <summary>
129         /// Get the managed <see cref="Body"/> object from the native handle.
130         /// </summary>
131         [EditorBrowsable(EditorBrowsableState.Never)]
132         public static Body FromHandleSafe(cpBody nativeBodyHandle)
133         {
134             if (nativeBodyHandle == IntPtr.Zero)
135             {
136                 return null;
137             }
138
139             return FromHandle(nativeBodyHandle);
140         }
141
142         private static cpBody InitializeBody(BodyType type)
143         {
144             if (type == BodyType.Kinematic)
145             {
146                 return NativeMethods.cpBodyNewKinematic();
147             }
148
149             if (type == BodyType.Static)
150             {
151                 return NativeMethods.cpBodyNewStatic();
152             }
153
154             return NativeMethods.cpBodyNew(0.0, 0.0);
155         }
156
157         /// <summary>
158         /// Destroy and free the body.
159         /// </summary>
160         [EditorBrowsable(EditorBrowsableState.Never)]
161         public void Free()
162         {
163             var space = Space;
164
165             if (space != null)
166                 space.RemoveBody(this);
167
168             ReleaseUserData();
169             NativeMethods.cpBodyFree(body);
170         }
171
172         /// <summary>
173         /// Dispose the body.
174         /// </summary>
175         protected virtual void Dispose(bool dispose)
176         {
177             if (!dispose)
178             {
179                 Debug.WriteLine("Disposing body {0} on finalizer... (consider Dispose explicitly)", body);
180             }
181
182             Free();
183         }
184
185         /// <summary>
186         /// Dispose the body.
187         /// </summary>
188         [EditorBrowsable(EditorBrowsableState.Never)]
189         public void Dispose()
190         {
191             Dispose(true);
192             GC.SuppressFinalize(this);
193         }
194
195         // Properties
196
197         /// <summary>
198         /// Arbitrary user data.
199         /// </summary>
200         [EditorBrowsable(EditorBrowsableState.Never)]
201         public object Data { get; set; }
202
203         /// <summary>
204         /// Rotation of the body in radians. When changing the rotation, you may also want to call
205         /// <see cref="Space.ReindexShapesForBody"/> to update the collision detection information
206         /// for the attached shapes if you plan to make any queries against the space. A body
207         /// rotates around its center of gravity, not its position.
208         /// </summary>
209         [EditorBrowsable(EditorBrowsableState.Never)]
210         public double Angle
211         {
212             get => NativeMethods.cpBodyGetAngle(body);
213             set => NativeMethods.cpBodySetAngle(body, value);
214         }
215
216         /// <summary>
217         /// Set body position and rotation angle (in radians)
218         /// </summary>
219         /// <param name="position"></param>
220         /// <param name="angle"></param>
221         [EditorBrowsable(EditorBrowsableState.Never)]
222         public void SetTransform(Vect position, double angle)
223         {
224             NativeMethods.cpBodySetTransform(body, position, angle);
225         }
226
227         /// <summary>
228         /// Get body position and rotation angle (in radians)
229         /// </summary>
230         /// <param name="position"></param>
231         /// <param name="angle"></param>
232         [EditorBrowsable(EditorBrowsableState.Never)]
233         public void GetTransform(out Vect position, out double angle)
234         {
235             NativeMethods.cpBodyGetTransform(body, out position, out angle);
236         }
237
238         /// <summary>
239         /// The way the body behaves in physics simulations.
240         /// </summary>
241         [EditorBrowsable(EditorBrowsableState.Never)]
242         public BodyType Type
243         {
244             get => (BodyType)NativeMethods.cpBodyGetType(body);
245             set => NativeMethods.cpBodySetType(body, (int)value);
246         }
247
248         /// <summary>
249         /// Mass of the rigid body. Mass does not have to be expressed in any particular units, but
250         /// relative masses should be consistent. 
251         /// </summary>
252         [EditorBrowsable(EditorBrowsableState.Never)]
253         public double Mass
254         {
255             get => NativeMethods.cpBodyGetMass(body);
256             set => NativeMethods.cpBodySetMass(body, value);
257         }
258
259         /// <summary>
260         /// Moment of inertia of the body. The mass tells you how hard it is to push an object,
261         /// the MoI tells you how hard it is to spin the object. Don't try to guess the MoI, use the
262         /// MomentFor*() functions to estimate it, or the physics may behave strangely. 
263         /// </summary>
264         [EditorBrowsable(EditorBrowsableState.Never)]
265         public double Moment
266         {
267             get => NativeMethods.cpBodyGetMoment(body);
268             set => NativeMethods.cpBodySetMoment(body, value);
269         }
270
271         /// <summary>
272         /// Get the space this body is associated with, or null if it is not currently associated.
273         /// </summary>
274         [EditorBrowsable(EditorBrowsableState.Never)]
275         public Space Space
276         {
277             get
278             {
279                 cpSpace space = NativeMethods.cpBodyGetSpace(body);
280                 return Space.FromHandleSafe(space);
281             }
282         }
283
284         /// <summary>
285         /// Position of the body. When changing the position, you may also want to call
286         /// <see cref="Space.ReindexShapesForBody"/> to update the collision detection information
287         /// for the attached shapes if you plan to make any queries against the space.
288         /// </summary>
289         [EditorBrowsable(EditorBrowsableState.Never)]
290         public Vect Position
291         {
292             get => NativeMethods.cpBodyGetPosition(body);
293             set => NativeMethods.cpBodySetPosition(body, value);
294         }
295
296         /// <summary>
297         /// Location of the center of gravity in body-local coordinates. The default value is
298         /// (0, 0), meaning the center of gravity is the same as the position of the body.
299         /// </summary>
300         [EditorBrowsable(EditorBrowsableState.Never)]
301         public Vect CenterOfGravity
302         {
303             get => NativeMethods.cpBodyGetCenterOfGravity(body);
304             set => NativeMethods.cpBodySetCenterOfGravity(body, value);
305         }
306
307         /// <summary>
308         /// Linear velocity of the center of gravity of the body.
309         /// </summary>
310         [EditorBrowsable(EditorBrowsableState.Never)]
311         public Vect Velocity
312         {
313             get => NativeMethods.cpBodyGetVelocity(body);
314             set => NativeMethods.cpBodySetVelocity(body, value);
315         }
316
317         /// <summary>
318         /// Force applied to the center of gravity of the body. This value is reset for every time
319         /// step.
320         /// </summary>
321         [EditorBrowsable(EditorBrowsableState.Never)]
322         public Vect Force
323         {
324             get => NativeMethods.cpBodyGetForce(body);
325             set => NativeMethods.cpBodySetForce(body, value);
326         }
327
328         /// <summary>
329         /// The angular velocity of the body in radians per second.
330         /// </summary>
331         [EditorBrowsable(EditorBrowsableState.Never)]
332         public double AngularVelocity
333         {
334             get => NativeMethods.cpBodyGetAngularVelocity(body);
335             set => NativeMethods.cpBodySetAngularVelocity(body, value);
336         }
337
338         /// <summary>
339         /// The torque applied to the body. This value is reset for every time step.
340         /// </summary>
341         [EditorBrowsable(EditorBrowsableState.Never)]
342         public double Torque
343         {
344             get => NativeMethods.cpBodyGetTorque(body);
345             set => NativeMethods.cpBodySetTorque(body, value);
346         }
347
348 #if __IOS__ || __TVOS__ || __WATCHOS__ || __MACCATALYST__
349 #pragma warning disable CA1416 // Validate platform compatibility
350         [MonoPInvokeCallback(typeof(BodyArbiterIteratorFunction))]
351 #pragma warning restore CA1416 // Validate platform compatibility
352 #endif
353         private static void AddEachArbiterToArray(cpBody body, cpArbiter arbiter, IntPtr data)
354         {
355             var list = (List<Arbiter>)GCHandle.FromIntPtr(data).Target;
356             var a = new Arbiter(arbiter);
357             list.Add(a);
358         }
359
360         private static BodyArbiterIteratorFunction eachArbiterFunc = AddEachArbiterToArray;
361
362         /// <summary>
363         /// The rotation vector for the body. Can be used with cpvrotate() or cpvunrotate() to perform fast rotations.
364         /// </summary>
365         [EditorBrowsable(EditorBrowsableState.Never)]
366         public Vect Rotation => NativeMethods.cpBodyGetRotation(body);
367
368         /// <summary>
369         /// Get the list of body Arbiters
370         /// </summary>
371         [EditorBrowsable(EditorBrowsableState.Never)]
372         public IReadOnlyList<Arbiter> Arbiters
373         {
374             get
375             {
376                 var list = new List<Arbiter>();
377                 var gcHandle = GCHandle.Alloc(list);
378                 NativeMethods.cpBodyEachArbiter(body, eachArbiterFunc.ToFunctionPointer(), GCHandle.ToIntPtr(gcHandle));
379                 gcHandle.Free();
380                 return list;
381             }
382         }
383
384 #if __IOS__ || __TVOS__ || __WATCHOS__ || __MACCATALYST__
385 #pragma warning disable CA1416 // Validate platform compatibility
386         [MonoPInvokeCallback(typeof(BodyArbiterIteratorFunction))]
387 #pragma warning restore CA1416 // Validate platform compatibility
388 #endif
389         private static void AddEachConstraintToArray(cpBody body, cpConstraint constraint, IntPtr data)
390         {
391             var list = (List<Constraint>)GCHandle.FromIntPtr(data).Target;
392             var c = Constraint.FromHandle(constraint);
393             list.Add(c);
394         }
395
396         private static BodyConstraintIteratorFunction eachConstraintFunc = AddEachConstraintToArray;
397
398         /// <summary>
399         /// All constraints attached to the body
400         /// </summary>
401         [EditorBrowsable(EditorBrowsableState.Never)]
402         public IReadOnlyList<Constraint> Constraints
403         {
404             get
405             {
406                 var list = new List<Constraint>();
407                 var gcHandle = GCHandle.Alloc(list);
408                 NativeMethods.cpBodyEachConstraint(body, eachConstraintFunc.ToFunctionPointer(), GCHandle.ToIntPtr(gcHandle));
409                 gcHandle.Free();
410                 return list.ToArray();
411             }
412         }
413
414 #if __IOS__ || __TVOS__ || __WATCHOS__ || __MACCATALYST__
415 #pragma warning disable CA1416 // Validate platform compatibility
416         [MonoPInvokeCallback(typeof(BodyShapeIteratorFunction))]
417 #pragma warning restore CA1416 // Validate platform compatibility
418 #endif
419         private static void AddEachShapeToArray(cpBody body, cpShape shape, IntPtr data)
420         {
421             var list = (List<Shape>)GCHandle.FromIntPtr(data).Target;
422             var s = Shape.FromHandle(shape);
423             list.Add(s);
424         }
425
426         private static BodyShapeIteratorFunction eachShapeFunc = AddEachShapeToArray;
427
428         /// <summary>
429         /// All shapes attached to the body
430         /// </summary>
431         [EditorBrowsable(EditorBrowsableState.Never)]
432         public IReadOnlyList<Shape> Shapes
433         {
434             get
435             {
436                 var list = new List<Shape>();
437                 var gcHandle = GCHandle.Alloc(list);
438                 NativeMethods.cpBodyEachShape(body, eachShapeFunc.ToFunctionPointer(), GCHandle.ToIntPtr(gcHandle));
439                 gcHandle.Free();
440                 return list.ToArray();
441             }
442         }
443
444         /// <summary>
445         /// Returns true if body is sleeping.
446         /// </summary>
447         [EditorBrowsable(EditorBrowsableState.Never)]
448         public bool IsSleeping => NativeMethods.cpBodyIsSleeping(body) != 0;
449
450         // Actions
451
452         /// <summary>
453         ///     Reset the idle timer on a body.
454         ///     If it was sleeping, wake it and any other bodies it was touching.
455         /// </summary>
456         [EditorBrowsable(EditorBrowsableState.Never)]
457         public void Activate() => NativeMethods.cpBodyActivate(body);
458
459         /// <summary>
460         /// Similar in function to Activate(). Activates all bodies touching body. If filter is not NULL, then only bodies touching through filter will be awoken.
461         /// </summary>
462         /// <param name="filter"></param>
463         [EditorBrowsable(EditorBrowsableState.Never)]
464         public void ActivateStatic(Shape filter) => NativeMethods.cpBodyActivateStatic(body, filter.Handle);
465
466         /// <summary>
467         /// Add the local force force to body as if applied from the body local point.
468         /// </summary>
469         /// <param name="force"></param>
470         /// <param name="point"></param>
471         [EditorBrowsable(EditorBrowsableState.Never)]
472         public void ApplyForceAtLocalPoint(Vect force, Vect point)
473         {
474             NativeMethods.cpBodyApplyForceAtLocalPoint(body, force, point);
475         }
476
477         /// <summary>
478         /// Apply torque.
479         /// </summary>
480         /// <param name="torque"></param>
481         [EditorBrowsable(EditorBrowsableState.Never)]
482         public void ApplyTorque(double torque)
483         {
484             NativeMethods.cpBodyApplyTorque(body, torque);
485         }
486
487         /// <summary>
488         /// Apply angular impulse.
489         /// </summary>
490         /// <param name="impulse"></param>
491         [EditorBrowsable(EditorBrowsableState.Never)]
492         public void ApplyAngularImpulse(double impulse)
493         {
494             NativeMethods.cpBodyApplyAngularImpulse(body, impulse);
495         }
496
497         /// <summary>
498         ///     Add the force force to body as if applied from the world point.
499         ///     People are sometimes confused by the difference between a force and an impulse.
500         ///     An impulse is a very large force applied over a very short period of time.
501         ///     Some examples are a ball hitting a wall or cannon firing.
502         ///     Chipmunk treats impulses as if they occur instantaneously by adding directly to the velocity of an object.
503         ///     Both impulses and forces are affected the mass of an object.
504         ///     Doubling the mass of the object will halve the effect.
505         /// </summary>
506         /// <param name="force"></param>
507         /// <param name="point"></param>
508         [EditorBrowsable(EditorBrowsableState.Never)]
509         public void ApplyForceAtWorldPoint(Vect force, Vect point)
510         {
511             NativeMethods.cpBodyApplyForceAtWorldPoint(body, force, point);
512         }
513
514         /// <summary>
515         /// Apply an impulse to a body. Both the impulse and point are expressed in world coordinates.
516         /// </summary>
517         /// <param name="impulse"></param>
518         /// <param name="point"></param>
519         [EditorBrowsable(EditorBrowsableState.Never)]
520         public void ApplyImpulseAtWorldPoint(Vect impulse, Vect point)
521         {
522             NativeMethods.cpBodyApplyImpulseAtWorldPoint(body, impulse, point);
523         }
524
525         /// <summary>
526         /// Apply an impulse to a body. Both the impulse and point are expressed in body local coordinates.
527         /// </summary>
528         /// <param name="impulse"></param>
529         /// <param name="point"></param>
530         [EditorBrowsable(EditorBrowsableState.Never)]
531         public void ApplyImpulseAtLocalPoint(Vect impulse, Vect point)
532         {
533             NativeMethods.cpBodyApplyImpulseAtLocalPoint(body, impulse, point);
534         }
535
536         /// <summary>
537         /// Forces a body to fall asleep immediately even if it’s in midair. Cannot be called from a callback.
538         /// </summary>
539         [EditorBrowsable(EditorBrowsableState.Never)]
540         public void Sleep()
541         {
542             NativeMethods.cpBodySleep(body);
543         }
544
545         /// <summary>
546         /// When objects in Chipmunk sleep, they sleep as a group of all objects that are touching or jointed together.
547         /// When an object is woken up, all of the objects in its group are woken up.
548         /// SleepWithGroup() allows you group sleeping objects together. It acts identically to Sleep() if you pass null as
549         /// group by starting a new group.
550         /// If you pass a sleeping body for group, body will be awoken when group is awoken.
551         /// You can use this to initialize levels and start stacks of objects in a pre-sleeping state.
552         /// </summary>
553         /// <param name="group"></param>
554         [EditorBrowsable(EditorBrowsableState.Never)]
555         public void SleepWithGroup(Body group)
556         {
557             NativeMethods.cpBodySleepWithGroup(body, group != null ? group.Handle : IntPtr.Zero);
558         }
559
560 #if __IOS__ || __TVOS__ || __WATCHOS__ || __MACCATALYST__
561 #pragma warning disable CA1416 // Validate platform compatibility
562         [MonoPInvokeCallback(typeof(BodyVelocityFunction))]
563 #pragma warning restore CA1416 // Validate platform compatibility
564 #endif
565         private static void BodyVelocityFunctionCallback(cpBody bodyHandle, Vect gravity, double damping, double dt)
566         {
567             var body = FromHandle(bodyHandle);
568
569             body.velocityUpdateFunction(body, gravity, damping, dt);
570         }
571
572         private static BodyVelocityFunction BodyVelocityFunctionCallbackDelegate = BodyVelocityFunctionCallback;
573
574         private Action<Body, Vect, double, double> velocityUpdateFunction;
575         /// <summary>
576         /// Set the callback used to update a body's velocity.
577         /// Parameters: body, gravity, damping and deltaTime
578         /// </summary>
579         [EditorBrowsable(EditorBrowsableState.Never)]
580         public Action<Body, Vect, double, double> VelocityUpdateFunction
581         {
582             get => velocityUpdateFunction;
583             set
584             {
585                 velocityUpdateFunction = value;
586
587                 IntPtr callbackPointer;
588
589                 if (value == null)
590                     callbackPointer = NativeMethods.cpBodyGetDefaultVelocityUpdateFunc();
591                 else
592                     callbackPointer = BodyVelocityFunctionCallbackDelegate.ToFunctionPointer();
593
594                 NativeMethods.cpBodySetVelocityUpdateFunc(body, callbackPointer);
595             }
596         }
597
598 #if __IOS__ || __TVOS__ || __WATCHOS__ || __MACCATALYST__
599 #pragma warning disable CA1416 // Validate platform compatibility
600         [MonoPInvokeCallback(typeof(BodyPositionFunction))]
601 #pragma warning restore CA1416 // Validate platform compatibility
602 #endif
603         private static void BodyPositionFunctionCallback(cpBody bodyHandle, double dt)
604         {
605             var body = FromHandle(bodyHandle);
606
607             body.positionUpdateFunction(body, dt);
608         }
609
610         private static BodyPositionFunction BodyUpdateFunctionCallbackDelegate = BodyPositionFunctionCallback;
611
612         private Action<Body, double> positionUpdateFunction;
613
614         /// <summary>
615         /// Set the callback used to update a body's position.
616         /// Parameters: body, deltaTime
617         /// </summary>
618         [EditorBrowsable(EditorBrowsableState.Never)]
619         public Action<Body, double> PositionUpdateFunction
620         {
621             get => positionUpdateFunction;
622             set
623             {
624                 positionUpdateFunction = value;
625
626                 IntPtr callbackPointer;
627
628                 if (value == null)
629                     callbackPointer = NativeMethods.cpBodyGetDefaultPositionUpdateFunc();
630                 else
631                     callbackPointer = BodyUpdateFunctionCallbackDelegate.ToFunctionPointer();
632
633                 NativeMethods.cpBodySetPositionUpdateFunc(body, callbackPointer);
634             }
635         }
636
637         /// <summary>
638         /// Default velocity integration function..
639         /// </summary>
640         /// <param name="gravity"></param>
641         /// <param name="damping"></param>
642         /// <param name="dt"></param>
643         [EditorBrowsable(EditorBrowsableState.Never)]
644         public void UpdateVelocity(Vect gravity, double damping, double dt)
645         {
646             NativeMethods.cpBodyUpdateVelocity(body, gravity, damping, dt);
647         }
648
649         /// <summary>
650         /// Default position integration function.
651         /// </summary>
652         /// <param name="dt"></param>
653         [EditorBrowsable(EditorBrowsableState.Never)]
654         public void UpdatePosition(double dt)
655         {
656             NativeMethods.cpBodyUpdatePosition(body, dt);
657         }
658
659         /// <summary>
660         /// Convert body relative/local coordinates to absolute/world coordinates.
661         /// </summary>
662         /// <param name="point"></param>
663         /// <returns></returns>
664         [EditorBrowsable(EditorBrowsableState.Never)]
665         public Vect LocalToWorld(Vect point)
666         {
667             return NativeMethods.cpBodyLocalToWorld(body, point);
668         }
669
670         /// <summary>
671         /// Convert body absolute/world coordinates to  relative/local coordinates.
672         /// </summary>
673         /// <param name="point"></param>
674         /// <returns></returns>
675         [EditorBrowsable(EditorBrowsableState.Never)]
676         public Vect WorldToLocal(Vect point)
677         {
678             return NativeMethods.cpBodyWorldToLocal(body, point);
679         }
680
681         /// <summary>
682         /// Get the velocity on a body (in world units) at a point on the body in world coordinates.
683         /// </summary>
684         /// <param name="point"></param>
685         /// <returns></returns>
686         [EditorBrowsable(EditorBrowsableState.Never)]
687         public Vect GetVelocityAtWorldPoint(Vect point)
688         {
689             return NativeMethods.cpBodyGetVelocityAtWorldPoint(body, point);
690         }
691
692         /// <summary>
693         /// Get the velocity on a body (in world units) at a point on the body in local coordinates.
694         /// </summary>
695         /// <param name="point"></param>
696         /// <returns></returns>
697         [EditorBrowsable(EditorBrowsableState.Never)]
698         public Vect GetVelocityAtLocalPoint(Vect point)
699         {
700             return NativeMethods.cpBodyGetVelocityAtLocalPoint(body, point);
701         }
702
703         /// <summary>
704         /// Get the kinetic energy of a body.
705         /// </summary>
706         [EditorBrowsable(EditorBrowsableState.Never)]
707         public double KineticEnergy => NativeMethods.cpBodyKineticEnergy(body);
708
709         /// <summary>
710         /// Calculate the moment of inertia for a solid box centered on the body.
711         /// </summary>
712         /// <param name="mass"></param>
713         /// <param name="width"></param>
714         /// <param name="height"></param>
715         /// <returns></returns>
716         [EditorBrowsable(EditorBrowsableState.Never)]
717         public static double MomentForBox(double mass, double width, double height)
718         {
719             return NativeMethods.cpMomentForBox(mass, width, height);
720         }
721
722         /// <summary>
723         /// Get the list of all bodies in contact with this one
724         /// </summary>
725         [EditorBrowsable(EditorBrowsableState.Never)]
726         public IReadOnlyList<Body> AllContactedBodies
727         {
728             get
729             {
730                 int count = NativeMethods.cpBodyGetContactedBodiesCount(body);
731
732                 if (count == 0)
733                     return Array.Empty<Body>();
734
735                 IntPtr ptrBodies = Marshal.AllocHGlobal(IntPtr.Size * count);
736                 NativeMethods.cpBodyGetUserDataContactedBodies(body, ptrBodies);
737
738                 IntPtr[] userDataArray = new IntPtr[count];
739
740                 Marshal.Copy(ptrBodies, userDataArray, 0, count);
741
742                 Marshal.FreeHGlobal(ptrBodies);
743
744                 Body[] bodies = new Body[count];
745
746                 for (int i = 0; i < count; i++)
747                 {
748                     Body b = NativeInterop.FromIntPtr<Body>(userDataArray[i]);
749                     bodies[i] = b;
750                 }
751
752                 return bodies;
753             }
754         }
755
756         /// <summary>
757         /// Check if a Body is in contact with another
758         /// </summary>
759         /// <param name="other"></param>
760         /// <returns></returns>
761         [EditorBrowsable(EditorBrowsableState.Never)]
762         public bool ContactWith(Body other)
763         {
764             return NativeMethods.cpBodyContactWith(body, other.body) != 0;
765         }
766     }
767 }