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