1 // =============================================================================
2 // PROJECT CHRONO - http://projectchrono.org
3 //
4 // Copyright (c) 2014 projectchrono.org
5 // All rights reserved.
6 //
7 // Use of this source code is governed by a BSD-style license that can be found
8 // in the LICENSE file at the top level of the distribution and at
9 // http://projectchrono.org/license-chrono.txt.
10 //
11 // =============================================================================
12 // Authors: Alessandro Tasora, Radu Serban
13 // =============================================================================
14 
15 //// RADU
16 ////   TODO: Eliminate the namespace 'chrono::collision'
17 ////         Make the internal use AddContact private (protected) here and in derived classes.
18 ////         (with the collision namespace, this would require ugly dependencies)
19 
20 #ifndef CH_CONTACT_CONTAINER_H
21 #define CH_CONTACT_CONTAINER_H
22 
23 #include <list>
24 #include <unordered_map>
25 
26 #include "chrono/collision/ChCollisionInfo.h"
27 #include "chrono/physics/ChBody.h"
28 #include "chrono/physics/ChContactable.h"
29 #include "chrono/physics/ChMaterialSurface.h"
30 
31 namespace chrono {
32 
33 /// Class representing a container of many contacts.
34 class ChApi ChContactContainer : public ChPhysicsItem {
35   public:
ChContactContainer()36     ChContactContainer() : add_contact_callback(nullptr), report_contact_callback(nullptr) {}
37     ChContactContainer(const ChContactContainer& other);
~ChContactContainer()38     virtual ~ChContactContainer() {}
39 
40     /// Get the number of added contacts.
41     virtual int GetNcontacts() const = 0;
42 
43     /// Remove (delete) all contained contact data.
44     virtual void RemoveAllContacts() = 0;
45 
46     /// The collision system will call BeginAddContact() before adding all contacts (for example with AddContact() or
47     /// similar). By default it deletes all previous contacts. More efficient implementations might reuse contacts if
48     /// possible.
BeginAddContact()49     virtual void BeginAddContact() { RemoveAllContacts(); }
50 
51     /// Add a contact between two collision shapes, storing it into this container.
52     /// A compositecontact material is created from the two given materials.
53     /// In this case, the collision info object may have null pointers to collision shapes.
54     virtual void AddContact(const collision::ChCollisionInfo& cinfo,
55                             std::shared_ptr<ChMaterialSurface> mat1,
56                             std::shared_ptr<ChMaterialSurface> mat2) = 0;
57 
58     /// Add a contact between two collision shapes, storing it into this container.
59     /// The collision info object is assumed to contain valid pointers to the two colliding shapes.
60     /// A composite contact material is created from their material properties.
61     virtual void AddContact(const collision::ChCollisionInfo& cinfo) = 0;
62 
63     /// The collision system will call EndAddContact() after adding all contacts (for example with AddContact() or
64     /// similar).
EndAddContact()65     virtual void EndAddContact() {}
66 
67     /// Class to be used as a callback interface for some user defined action to be taken
68     /// each time a contact is added to the container.
69     /// It can be used to modify the composite material properties for the contact pair.
70     class ChApi AddContactCallback {
71       public:
~AddContactCallback()72         virtual ~AddContactCallback() {}
73 
74         /// Callback used to process contact points being added to the container.
75         /// A derived user-provided callback class must implement this. The provided
76         /// composite material should be downcast to the appropriate type.
77         virtual void OnAddContact(
78             const collision::ChCollisionInfo& contactinfo,  ///< information about the collision pair
79             ChMaterialComposite* const material             ///< composite material can be modified
80             ) = 0;
81     };
82 
83     /// Specify a callback object to be used each time a contact point is added to the container.
84     /// Note that derived classes may not support this. If supported, the OnAddContact() method
85     /// of the provided callback object will be called for each contact pair to allow modifying the
86     /// composite material properties.
RegisterAddContactCallback(std::shared_ptr<AddContactCallback> callback)87     virtual void RegisterAddContactCallback(std::shared_ptr<AddContactCallback> callback) { add_contact_callback = callback; }
88 
89     /// Get the callback object to be used each time a contact point is added to the container.
GetAddContactCallback()90     virtual std::shared_ptr<AddContactCallback> GetAddContactCallback() { return add_contact_callback; }
91 
92     /// Class to be used as a callback interface for some user defined action to be taken
93     /// for each contact (already added to the container, maybe with already computed forces).
94     /// It can be used to report or post-process contacts.
95     class ChApi ReportContactCallback {
96       public:
~ReportContactCallback()97         virtual ~ReportContactCallback() {}
98 
99         /// Callback used to report contact points already added to the container.
100         /// If it returns false, the contact scanning will be stopped.
101         virtual bool OnReportContact(
102             const ChVector<>& pA,             ///< contact pA
103             const ChVector<>& pB,             ///< contact pB
104             const ChMatrix33<>& plane_coord,  ///< contact plane coordsystem (A column 'X' is contact normal)
105             const double& distance,           ///< contact distance
106             const double& eff_radius,         ///< effective radius of curvature at contact
107             const ChVector<>& react_forces,   ///< react.forces (if already computed). In coordsystem 'plane_coord'
108             const ChVector<>& react_torques,  ///< react.torques, if rolling friction (if already computed).
109             ChContactable* contactobjA,  ///< model A (note: some containers may not support it and could be nullptr)
110             ChContactable* contactobjB   ///< model B (note: some containers may not support it and could be nullptr)
111             ) = 0;
112     };
113 
114     /// Scan all the contacts and for each contact executes the OnReportContact() function of the provided callback
115     /// object.
ReportAllContacts(std::shared_ptr<ReportContactCallback> callback)116     virtual void ReportAllContacts(std::shared_ptr<ReportContactCallback> callback) {}
117 
118     /// Compute contact forces on all contactable objects in this container.
ComputeContactForces()119     virtual void ComputeContactForces() {}
120 
121     /// Return the resultant contact force acting on the specified contactable object.
122     virtual ChVector<> GetContactableForce(ChContactable* contactable) = 0;
123 
124     /// Return the resultant contact torque acting on the specified contactable object.
125     virtual ChVector<> GetContactableTorque(ChContactable* contactable) = 0;
126 
127     /// Method for serialization of transient data to archives.
128     virtual void ArchiveOUT(ChArchiveOut& marchive);
129 
130     /// Method for de-serialization of transient data from archives.
131     virtual void ArchiveIN(ChArchiveIn& marchive);
132 
133   protected:
134     struct ForceTorque {
135         ChVector<> force;
136         ChVector<> torque;
137     };
138 
139     std::shared_ptr<AddContactCallback> add_contact_callback;
140     ReportContactCallback* report_contact_callback;
141 
142     /// Utility function to accumulate contact forces from a specified list of contacts.
143     /// This function is templated by the contact type (assumed to be derived from ChContactTuple).
144     /// Contact forces are accumulated in a map keyed by the contactable objects.
145     /// Derived ChContactContainer classes can use this utility (processing their various lists
146     /// of contacts) to cache information used for reporting through GetContactableForce and
147     /// GetContactableTorque.
148     template <class Tcont>
SumAllContactForces(std::list<Tcont * > & contactlist,std::unordered_map<ChContactable *,ForceTorque> & contactforces)149     void SumAllContactForces(std::list<Tcont*>& contactlist,
150                              std::unordered_map<ChContactable*, ForceTorque>& contactforces) {
151         for (auto contact = contactlist.begin(); contact != contactlist.end(); ++contact) {
152             // Extract information for current contact (expressed in global frame)
153             ChMatrix33<> A = (*contact)->GetContactPlane();
154             ChVector<> force_loc = (*contact)->GetContactForce();
155             ChVector<> force = A * force_loc;
156             ChVector<> p1 = (*contact)->GetContactP1();
157             ChVector<> p2 = (*contact)->GetContactP2();
158 
159             // Calculate contact torque for first object (expressed in global frame).
160             // Recall that -force is applied to the first object.
161             ChVector<> torque1(0);
162             if (ChBody* body = dynamic_cast<ChBody*>((*contact)->GetObjA())) {
163                 torque1 = Vcross(p1 - body->GetPos(), -force);
164             }
165 
166             // If there is already an entry for the first object, accumulate.
167             // Otherwise, insert a new entry.
168             auto entry1 = contactforces.find((*contact)->GetObjA());
169             if (entry1 != contactforces.end()) {
170                 entry1->second.force -= force;
171                 entry1->second.torque += torque1;
172             } else {
173                 ForceTorque ft{-force, torque1};
174                 contactforces.insert(std::make_pair((*contact)->GetObjA(), ft));
175             }
176 
177             // Calculate contact torque for second object (expressed in global frame).
178             // Recall that +force is applied to the second object.
179             ChVector<> torque2(0);
180             if (ChBody* body = dynamic_cast<ChBody*>((*contact)->GetObjB())) {
181                 torque2 = Vcross(p2 - body->GetPos(), force);
182             }
183 
184             // If there is already an entry for the first object, accumulate.
185             // Otherwise, insert a new entry.
186             auto entry2 = contactforces.find((*contact)->GetObjB());
187             if (entry2 != contactforces.end()) {
188                 entry2->second.force += force;
189                 entry2->second.torque += torque2;
190             } else {
191                 ForceTorque ft{force, torque2};
192                 contactforces.insert(std::make_pair((*contact)->GetObjB(), ft));
193             }
194         }
195     }
196 };
197 
198 CH_CLASS_VERSION(ChContactContainer, 0)
199 
200 }  // end namespace chrono
201 
202 #endif
203