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: Radu Serban
13 // =============================================================================
14 //
15 // Base class for a sprocket template with gear profile composed of circular
16 // arcs and a flat seat, suitable for interaction with double-pin track shoes.
17 //
18 // =============================================================================
19 
20 #include <cmath>
21 
22 #include "chrono_vehicle/tracked_vehicle/sprocket/ChSprocketDoublePin.h"
23 #include "chrono_vehicle/tracked_vehicle/track_shoe/ChTrackShoeDoublePin.h"
24 #include "chrono_vehicle/tracked_vehicle/ChTrackAssembly.h"
25 
26 namespace chrono {
27 namespace vehicle {
28 
29 // -----------------------------------------------------------------------------
30 // -----------------------------------------------------------------------------
31 class SprocketDoublePinContactCB : public ChSystem::CustomCollisionCallback {
32   public:
SprocketDoublePinContactCB(ChTrackAssembly * track,double envelope,int gear_nteeth,double gear_RT,double gear_R,double gear_C,double gear_W,double separation,double shoe_len,double shoe_R,bool lateral_contact,double lateral_backlash,const ChVector<> & shoe_pin)33     SprocketDoublePinContactCB(ChTrackAssembly* track,     ///< containing track assembly
34                                double envelope,            ///< collision detection envelope
35                                int gear_nteeth,            ///< number of teeth of the sprocket gear
36                                double gear_RT,             ///< radius if the addendum circle
37                                double gear_R,              ///< radius of the tooth arc profile
38                                double gear_C,              ///< height of profile arcs
39                                double gear_W,              ///< offset of profile arcs
40                                double separation,          ///< separation between sprocket gears
41                                double shoe_len,            ///< length of track shoe connector
42                                double shoe_R,              ///< radius of track shoe connector
43                                bool lateral_contact,       ///< if true, enable lateral contact
44                                double lateral_backlash,    ///< play relative to shoe guiding pin
45                                const ChVector<>& shoe_pin  ///< location of shoe guide pin center
46                                )
47         : m_track(track),
48           m_gear_nteeth(gear_nteeth),
49           m_gear_RT(gear_RT),
50           m_gear_R(gear_R),
51           m_gear_C(gear_C),
52           m_gear_W(gear_W),
53           m_shoe_len(shoe_len),
54           m_shoe_R(shoe_R),
55           m_lateral_contact(lateral_contact),
56           m_lateral_backlash(lateral_backlash),
57           m_shoe_pin(shoe_pin) {
58         m_sprocket = static_cast<ChSprocketDoublePin*>(m_track->GetSprocket().get());
59 
60         ////m_gear_Rhat = gear_R - envelope;
61         ////m_shoe_Rhat = shoe_R + envelope;
62         m_R_sum = (m_shoe_len + 2 * m_shoe_R) + m_gear_RT;
63         m_gear_RC = std::sqrt(m_gear_C * m_gear_C + m_gear_W * m_gear_W);
64         m_gear_D = m_gear_W - m_gear_R;
65         m_beta = CH_C_2PI / m_gear_nteeth;
66         m_sbeta = std::sin(m_beta / 2);
67         m_cbeta = std::cos(m_beta / 2);
68 
69         // Create contact material for sprocket - guiding pin contacts (to prevent detracking)
70         // Note: zero friction
71         MaterialInfo minfo;
72         minfo.mu = 0;
73         minfo.cr = 0.1f;
74         minfo.Y = 1e7f;
75         m_material = minfo.CreateMaterial(m_sprocket->GetGearBody()->GetSystem()->GetContactMethod());
76     }
77 
78     virtual void OnCustomCollision(ChSystem* system) override;
79 
80   private:
81     // Test collision between a connector body and the sprocket's gear profiles.
82     void CheckConnectorSprocket(std::shared_ptr<ChBody> connector,                 // connector body
83                                 std::shared_ptr<ChMaterialSurface> mat_connector,  // connector contact material
84                                 const ChVector<>& locS_abs                         // center of sprocket (global frame)
85     );
86 
87     // Test collision between a circle and the gear profile (in the plane of the gear).
88     void CheckCircleProfile(std::shared_ptr<ChBody> connector,                 // connector body
89                             std::shared_ptr<ChMaterialSurface> mat_connector,  // connector contact material
90                             const ChVector<>& loc,                             // center of circular connector end
91                             const ChVector<>& p1L,
92                             const ChVector<>& p2L,
93                             const ChVector<>& p3L,
94                             const ChVector<>& p4L,
95                             const ChVector<>& p1R,
96                             const ChVector<>& p2R,
97                             const ChVector<>& p3R,
98                             const ChVector<>& p4R);
99 
100     void CheckCircleArc(std::shared_ptr<ChBody> connector,                 // connector body
101                         std::shared_ptr<ChMaterialSurface> mat_connector,  // connector contact material
102                         const ChVector<>& cc,                              // circle center
103                         double cr,                                         // circle radius
104                         const ChVector<> ac,                               // arc center
105                         double ar,                                         // arc radius
106                         const ChVector<>& p1,                              // arc end point 1
107                         const ChVector<>& p2                               // arc end point 2
108     );
109 
110     void CheckCircleSegment(std::shared_ptr<ChBody> connector,                 // connector body
111                             std::shared_ptr<ChMaterialSurface> mat_connector,  // connector contact material
112                             const ChVector<>& cc,                              // circle center
113                             double cr,                                         // circle radius
114                             const ChVector<>& p1,                              // segment end point 1
115                             const ChVector<>& p2                               // segment end point 2
116                             );
117 
118     // Test collision of a shoe guiding pin with the sprocket gear.
119     // This may introduce one contact.
120     void CheckPinSprocket(std::shared_ptr<ChTrackShoeDoublePin> shoe,  // track shoe
121                           const ChVector<>& locPin_abs,                // center of guiding pin (global frame)
122                           const ChVector<>& dirS_abs                   // sprocket Y direction (global frame)
123     );
124 
125     ChTrackAssembly* m_track;         // pointer to containing track assembly
126     ChSprocketDoublePin* m_sprocket;  // pointer to the sprocket
127 
128     int m_gear_nteeth;    // sprocket gear, number of teeth
129     double m_gear_RT;     // sprocket gear, outer tooth radius (radius of addendum circle)
130     double m_gear_R;      // sprocket gear, arc radius
131     double m_gear_C;      // sprocket gear, arc center height
132     double m_gear_W;      // sprocket gear, arc center offset
133 
134     double m_gear_D;   // tooth width (D = W - R)
135     double m_gear_RC;  // radius of arc centers circle (RC^2 = C^2 + W^2)
136 
137     double m_beta;   // angle between two consecutive gear teeth
138     double m_sbeta;  // sin(beta/2)
139     double m_cbeta;  // cos(beta/2)
140 
141     double m_shoe_len;  // length of track shoe connector
142     double m_shoe_R;    // radius of track shoe connector
143 
144     bool m_lateral_contact;     // if true, generate lateral contacts
145     double m_lateral_backlash;  // backlash relative to shoe guiding pin
146     ChVector<> m_shoe_pin;      // single-pin shoe, center of guiding pin
147 
148     ////double m_gear_Rhat;  // adjusted gear arc radius
149     ////double m_shoe_Rhat;  // adjusted shoe cylinder radius
150 
151     double m_R_sum;  // test quantity for broadphase check
152 
153     std::shared_ptr<ChMaterialSurface> m_material;  // material for sprocket-pin contact (detracking)
154 };
155 
156 // Add contacts between the sprocket and track shoes.
OnCustomCollision(ChSystem * system)157 void SprocketDoublePinContactCB::OnCustomCollision(ChSystem* system) {
158     // Return now if collision disabled on sprocket or track shoes.
159     if (m_track->GetNumTrackShoes() == 0)
160         return;
161     if (!m_sprocket->GetGearBody()->GetCollide() || !m_track->GetTrackShoe(0)->GetShoeBody()->GetCollide())
162         return;
163 
164     // Sprocket gear center location, expressed in global frame
165     ChVector<> locS_abs = m_sprocket->GetGearBody()->GetPos();
166 
167     // Sprocket "normal" (Y axis), expressed in global frame
168     ChVector<> dirS_abs = m_sprocket->GetGearBody()->GetA().Get_A_Yaxis();
169 
170     // Loop over all track shoes in the associated track
171     for (size_t is = 0; is < m_track->GetNumTrackShoes(); ++is) {
172         auto shoe = std::static_pointer_cast<ChTrackShoeDoublePin>(m_track->GetTrackShoe(is));
173 
174         // Perform collision test for the "left" connector body
175         CheckConnectorSprocket(shoe->m_connector_L, shoe->GetSprocketContactMaterial(), locS_abs);
176 
177         // Perform collision test for the "right" connector body
178         CheckConnectorSprocket(shoe->m_connector_R, shoe->GetSprocketContactMaterial(), locS_abs);
179 
180         if (m_lateral_contact) {
181             // Express guiding pin center in the global frame
182             ChVector<> locPin_abs = shoe->GetShoeBody()->TransformPointLocalToParent(m_shoe_pin);
183 
184             // Perform collision detection with the central pin
185             CheckPinSprocket(shoe, locPin_abs, dirS_abs);
186         }
187     }
188 }
189 
190 // Perform collision test between the specified connector body and the associated sprocket.
CheckConnectorSprocket(std::shared_ptr<ChBody> connector,std::shared_ptr<ChMaterialSurface> mat_connector,const ChVector<> & locS_abs)191 void SprocketDoublePinContactCB::CheckConnectorSprocket(std::shared_ptr<ChBody> connector,
192                                                         std::shared_ptr<ChMaterialSurface> mat_connector,
193                                                         const ChVector<>& locS_abs) {
194     // (1) Express the center of the connector body in the sprocket frame
195     ChVector<> loc = m_sprocket->GetGearBody()->TransformPointParentToLocal(connector->GetPos());
196 
197     // (2) Broadphase collision test: no contact if the connector's center is too far from the
198     // center of the gear (test performed in the sprocket's x-z plane).
199     if (loc.x() * loc.x() + loc.z() * loc.z() > m_R_sum * m_R_sum)
200         return;
201 
202     // (3) Working in the frame of the sprocket, find the candidate tooth space.
203     // This is the closest tooth space to the connector center point.
204 
205     // Angle formed by 'locC' and the line z>0
206     double angle = std::atan2(loc.x(), loc.z());
207     // Find angle of closest tooth space
208     double alpha = m_beta * std::round(angle / m_beta);
209 
210     // (4) Calculate the points that define the current tooth space.
211     // Note that all these points will have Y = locC.y
212     ChVector<> p0L(-(m_gear_RT - m_gear_D) * m_sbeta, loc.y(), (m_gear_RT - m_gear_D) * m_cbeta);
213     ChVector<> p0R(+(m_gear_RT - m_gear_D) * m_sbeta, loc.y(), (m_gear_RT - m_gear_D) * m_cbeta);
214 
215     ChVector<> p1L = p0L + ChVector<>(+m_gear_D * m_cbeta, 0, m_gear_D * m_sbeta);
216     ChVector<> p1R = p0R + ChVector<>(-m_gear_D * m_cbeta, 0, m_gear_D * m_sbeta);
217 
218     ChVector<> p2L = ChVector<>(-m_gear_C * m_sbeta, loc.y(), m_gear_C * m_cbeta) +
219                      ChVector<>(+m_gear_D * m_cbeta, 0, m_gear_D * m_sbeta);
220     ChVector<> p2R = ChVector<>(+m_gear_C * m_sbeta, loc.y(), m_gear_C * m_cbeta) +
221                      ChVector<>(-m_gear_D * m_cbeta, 0, m_gear_D * m_sbeta);
222 
223     ChVector<> p3L = p2L + ChVector<>(+m_gear_R * m_cbeta, 0, m_gear_R * m_sbeta);
224     ChVector<> p3R = p2R + ChVector<>(-m_gear_R * m_cbeta, 0, m_gear_R * m_sbeta);
225 
226     ChVector<> p4L = p3L - ChVector<>(0, 0, m_gear_R);
227     ChVector<> p4R = p3R - ChVector<>(0, 0, m_gear_R);
228 
229     ChMatrix33<> rot(alpha, ChVector<>(0, 1, 0));
230 
231     p0L = rot * p0L;
232     p1L = rot * p1L;
233     p2L = rot * p2L;
234     p3L = rot * p3L;
235     p4L = rot * p4L;
236 
237     p0R = rot * p0R;
238     p1R = rot * p1R;
239     p2R = rot * p2R;
240     p3R = rot * p3R;
241     p4R = rot * p4R;
242 
243     // (5) Express the centers of the two connector end-caps in the sprocket frame.
244     ChVector<> P1_abs = connector->TransformPointLocalToParent(ChVector<>(m_shoe_len / 2, 0, 0));
245     ChVector<> P2_abs = connector->TransformPointLocalToParent(ChVector<>(-m_shoe_len / 2, 0, 0));
246     ChVector<> P1 = m_sprocket->GetGearBody()->TransformPointParentToLocal(P1_abs);
247     ChVector<> P2 = m_sprocket->GetGearBody()->TransformPointParentToLocal(P2_abs);
248 
249     // (6) Perform collision test between the front end of the connector and the gear profile.
250     CheckCircleProfile(connector, mat_connector, P1, p1L, p2L, p3L, p4L, p1R, p2R, p3R, p4R);
251 
252     // (7) Perform collision test between the rear end of the connector and the gear profile.
253     CheckCircleProfile(connector, mat_connector, P2, p1L, p2L, p3L, p4L, p1R, p2R, p3R, p4R);
254 }
255 
256 // Working in the (x-z) plane of the gear, perform a 2D collision test between a circle
257 // of radius m_shoe_R centered at the specified location and the gear profile.
CheckCircleProfile(std::shared_ptr<ChBody> connector,std::shared_ptr<ChMaterialSurface> mat_connector,const ChVector<> & loc,const ChVector<> & p1L,const ChVector<> & p2L,const ChVector<> & p3L,const ChVector<> & p4L,const ChVector<> & p1R,const ChVector<> & p2R,const ChVector<> & p3R,const ChVector<> & p4R)258 void SprocketDoublePinContactCB::CheckCircleProfile(std::shared_ptr<ChBody> connector,
259                                                     std::shared_ptr<ChMaterialSurface> mat_connector,
260                                                     const ChVector<>& loc,
261                                                     const ChVector<>& p1L,
262                                                     const ChVector<>& p2L,
263                                                     const ChVector<>& p3L,
264                                                     const ChVector<>& p4L,
265                                                     const ChVector<>& p1R,
266                                                     const ChVector<>& p2R,
267                                                     const ChVector<>& p3R,
268                                                     const ChVector<>& p4R) {
269     // Check circle against arc centered at p3L.
270     CheckCircleArc(connector, mat_connector, loc, m_shoe_R, p3L, m_gear_R, p2L, p4L);
271 
272     // Check circle against arc centered at p3R.
273     CheckCircleArc(connector, mat_connector, loc, m_shoe_R, p3R, m_gear_R, p3R, p4R);
274 
275     // Check circle against segment p1L - p2L.
276     CheckCircleSegment(connector, mat_connector, loc, m_shoe_R, p1L, p2L);
277 
278     // Check circle against segment p1R - p2R.
279     CheckCircleSegment(connector, mat_connector, loc, m_shoe_R, p1R, p2R);
280 
281     // Check circle against segment p4L - p4R.
282     CheckCircleSegment(connector, mat_connector, loc, m_shoe_R, p4L, p4R);
283 }
284 
285 // Working in the (x-z) plane, perform a 2D collision test between a circle of radius 'cr'
286 // centered at 'cc' (on the connector body) and an arc on the circle of radius 'ar' centered
287 // at 'ac', with the arc endpoints 'p1' and 'p2' (on the gear body).
CheckCircleArc(std::shared_ptr<ChBody> connector,std::shared_ptr<ChMaterialSurface> mat_connector,const ChVector<> & cc,double cr,const ChVector<> ac,double ar,const ChVector<> & p1,const ChVector<> & p2)288 void SprocketDoublePinContactCB::CheckCircleArc(std::shared_ptr<ChBody> connector,                 // connector body
289                                                 std::shared_ptr<ChMaterialSurface> mat_connector,  // conector material
290                                                 const ChVector<>& cc,                              // circle center
291                                                 double cr,                                         // circle radius
292                                                 const ChVector<> ac,                               // arc center
293                                                 double ar,                                         // arc radius
294                                                 const ChVector<>& p1,                              // arc end point 1
295                                                 const ChVector<>& p2                               // arc end point 2
296 ) {
297     // Find distance between centers
298     ChVector<> delta = cc - ac;
299     double dist2 = delta.Length2();
300 
301     // If the two centers (circle and arc) are separated by less than the difference
302     // of their adjusted radii, there is no contact.
303     double Rdiff = ar - cr;
304     if (dist2 <= Rdiff * Rdiff)
305         return;
306 
307     // If the contact point is outside the arc (p1-p2), there is no contact.
308     ChVector<> a = p1 - ac;
309     ChVector<> b = p2 - ac;
310     ChVector<> axb = Vcross(a, b);
311     ChVector<> axd = Vcross(a, delta);
312     ChVector<> dxb = Vcross(delta, b);
313     if (Vdot(axb, axd) <= 0 || Vdot(axb, dxb) <= 0)
314         return;
315 
316     // Generate contact information (still in the sprocket frame)
317     double dist = std::sqrt(dist2);
318     ChVector<> normal = -delta / dist;
319     ChVector<> pt_gear = ac - m_gear_R * normal;
320     ChVector<> pt_shoe = cc - m_shoe_R * normal;
321 
322     // Fill in contact information and add the contact to the system.
323     // Express all vectors in the global frame
324     collision::ChCollisionInfo contact;
325     contact.modelA = m_sprocket->GetGearBody()->GetCollisionModel().get();
326     contact.modelB = connector->GetCollisionModel().get();
327     contact.shapeA = nullptr;
328     contact.shapeB = nullptr;
329     contact.vN = m_sprocket->GetGearBody()->TransformDirectionLocalToParent(normal);
330     contact.vpA = m_sprocket->GetGearBody()->TransformPointLocalToParent(pt_gear);
331     contact.vpB = m_sprocket->GetGearBody()->TransformPointLocalToParent(pt_shoe);
332     contact.distance = Rdiff - dist;
333     ////contact.eff_radius = cr;  //// TODO: take into account ar?
334 
335     m_sprocket->GetGearBody()->GetSystem()->GetContactContainer()->AddContact(contact, m_sprocket->GetContactMaterial(),
336                                                                               mat_connector);
337 }
338 
339 // Working in the (x-z) plane, perform a 2D collision test between the circle of radius 'cr'
340 // centered at 'cc' (on the connector body) and the line segment with endpoints 'p1' and 'p2'
341 // (on the gear body).
CheckCircleSegment(std::shared_ptr<ChBody> connector,std::shared_ptr<ChMaterialSurface> mat_connector,const ChVector<> & cc,double cr,const ChVector<> & p1,const ChVector<> & p2)342 void SprocketDoublePinContactCB::CheckCircleSegment(
343     std::shared_ptr<ChBody> connector,                 // connector body
344     std::shared_ptr<ChMaterialSurface> mat_connector,  // conector material
345     const ChVector<>& cc,                              // circle center
346     double cr,                                         // circle radius
347     const ChVector<>& p1,                              // segment end point 1
348     const ChVector<>& p2                               // segment end point 2
349 ) {
350     // Find closest point on segment to circle center: X = p1 + t * (p2-p1)
351     ChVector<> s = p2 - p1;
352     double t = Vdot(cc - p1, s) / Vdot(s, s);
353     ChClampValue(t, 0.0, 1.0);
354 
355     ChVector<> pt_gear = p1 + t * s;
356 
357     // No contact if circle center is too far from segment.
358     ChVector<> delta = cc - pt_gear;
359     double dist2 = delta.Length2();
360     if (dist2 >= cr * cr)
361         return;
362 
363     // Generate contact information (still in sprocket frame)
364     double dist = std::sqrt(dist2);
365     ChVector<> normal = delta / dist;
366     ChVector<> pt_shoe = cc - cr * normal;
367 
368     // Fill in contact information and add the contact to the system.
369     // Express all vectors in the global frame
370     collision::ChCollisionInfo contact;
371     contact.modelA = m_sprocket->GetGearBody()->GetCollisionModel().get();
372     contact.modelB = connector->GetCollisionModel().get();
373     contact.shapeA = nullptr;
374     contact.shapeB = nullptr;
375     contact.vN = m_sprocket->GetGearBody()->TransformDirectionLocalToParent(normal);
376     contact.vpA = m_sprocket->GetGearBody()->TransformPointLocalToParent(pt_gear);
377     contact.vpB = m_sprocket->GetGearBody()->TransformPointLocalToParent(pt_shoe);
378     contact.distance = dist - cr;
379     ////contact.eff_radius = cr;
380 
381     m_sprocket->GetGearBody()->GetSystem()->GetContactContainer()->AddContact(contact, m_sprocket->GetContactMaterial(),
382                                                                               mat_connector);
383 }
384 
CheckPinSprocket(std::shared_ptr<ChTrackShoeDoublePin> shoe,const ChVector<> & locPin_abs,const ChVector<> & dirS_abs)385 void SprocketDoublePinContactCB::CheckPinSprocket(std::shared_ptr<ChTrackShoeDoublePin> shoe,
386                                                   const ChVector<>& locPin_abs,
387                                                   const ChVector<>& dirS_abs) {
388     // Express pin center in the sprocket frame
389     ChVector<> locPin = m_sprocket->GetGearBody()->TransformPointParentToLocal(locPin_abs);
390 
391     // No contact if the pin is close enough to the sprocket's center
392     if (std::abs(locPin.y()) < m_lateral_backlash)
393         return;
394 
395     // No contact if pin is too far from sprocket center
396     if (locPin.x() * locPin.x() + locPin.z() * locPin.z() > m_gear_RT * m_gear_RT)
397         return;
398 
399     // Fill in contact information and add the contact to the system.
400     // Express all vectors in the global frame
401     collision::ChCollisionInfo contact;
402     contact.modelA = m_sprocket->GetGearBody()->GetCollisionModel().get();
403     contact.modelB = shoe->GetShoeBody()->GetCollisionModel().get();
404     contact.shapeA = nullptr;
405     contact.shapeB = nullptr;
406     if (locPin.y() < 0) {
407         contact.distance = m_lateral_backlash + locPin.y();
408         contact.vN = dirS_abs;
409     } else {
410         contact.distance = m_lateral_backlash - locPin.y();
411         contact.vN = -dirS_abs;
412     }
413     contact.vpA = locPin_abs - contact.distance * contact.vN;
414     contact.vpB = locPin_abs;
415 
416     ////std::cout << "CONTACT";
417     ////std::cout << "  pin: " << locPin.y();
418     ////std::cout << "  delta: " << contact.distance;
419     ////std::cout << "  normal: " << contact.vN;
420     ////std::cout << std::endl;
421 
422     m_sprocket->GetGearBody()->GetSystem()->GetContactContainer()->AddContact(contact, m_material, m_material);
423 }
424 
425 // -----------------------------------------------------------------------------
426 // -----------------------------------------------------------------------------
ChSprocketDoublePin(const std::string & name)427 ChSprocketDoublePin::ChSprocketDoublePin(const std::string& name) : ChSprocket(name) {}
428 
429 // -----------------------------------------------------------------------------
430 // -----------------------------------------------------------------------------
GetCollisionCallback(ChTrackAssembly * track)431 std::shared_ptr<ChSystem::CustomCollisionCallback> ChSprocketDoublePin::GetCollisionCallback(ChTrackAssembly* track) {
432     // Check compatibility between this type of sprocket and the track shoes.
433     // We expect track shoes of type ChSinglePinShoe.
434     auto shoe = std::dynamic_pointer_cast<ChTrackShoeDoublePin>(track->GetTrackShoe(0));
435     assert(shoe);
436 
437     // Extract parameterization of gear profile
438     int gear_nteeth = GetNumTeeth();
439     double gear_RT = GetOuterRadius();
440     double gear_R = GetArcRadius();
441     double gear_C = GetArcCenterHeight();
442     double gear_W = GetArcCenterOffset();
443     double lateral_backlash = GetLateralBacklash();
444 
445     // Extract parameterization of the shoe connector contact geometry.
446     double shoe_len = shoe->GetConnectorLength();
447     double shoe_R = shoe->GetConnectorRadius();
448     ChVector<> shoe_locPin = shoe->GetLateralContactPoint();
449 
450     // Create and return the callback object. Note: this pointer will be freed by the base class.
451     return chrono_types::make_shared<SprocketDoublePinContactCB>(track, 0.005, gear_nteeth, gear_RT, gear_R, gear_C,
452                                                                  gear_W, GetSeparation(), shoe_len, shoe_R,
453                                                                  m_lateral_contact, lateral_backlash, shoe_locPin);
454 }
455 
456 // -----------------------------------------------------------------------------
457 // Create and return the sprocket gear profile.
458 // -----------------------------------------------------------------------------
GetProfile() const459 std::shared_ptr<geometry::ChLinePath> ChSprocketDoublePin::GetProfile() const {
460     auto profile = chrono_types::make_shared<geometry::ChLinePath>();
461 
462     int num_teeth = GetNumTeeth();
463     double R_T = GetOuterRadius();
464     double C = GetArcCenterHeight();
465     double W = GetArcCenterOffset();
466     double R = GetArcRadius();
467     double D = W - R;
468 
469     double beta = CH_C_2PI / num_teeth;
470     double sbeta = std::sin(beta / 2);
471     double cbeta = std::cos(beta / 2);
472 
473     for (int i = 0; i < num_teeth; ++i) {
474         ChVector<> p0L(-(R_T - D) * sbeta, (R_T - D) * cbeta, 0);
475         ChVector<> p0R(+(R_T - D) * sbeta, (R_T - D) * cbeta, 0);
476 
477         ChVector<> p1L = p0L + ChVector<>(+D * cbeta, D * sbeta, 0);
478         ChVector<> p1R = p0R + ChVector<>(-D * cbeta, D * sbeta, 0);
479 
480         ChVector<> p2L = ChVector<>(-C * sbeta, C * cbeta, 0) + ChVector<>(+D * cbeta, D * sbeta, 0);
481         ChVector<> p2R = ChVector<>(+C * sbeta, C * cbeta, 0) + ChVector<>(-D * cbeta, D * sbeta, 0);
482 
483         ChVector<> p3L = p2L + ChVector<>(+R * cbeta, R * sbeta, 0);
484         ChVector<> p3R = p2R + ChVector<>(-R * cbeta, R * sbeta, 0);
485 
486         ChVector<> p4L = p3L - ChVector<>(0, R, 0);
487         ChVector<> p4R = p3R - ChVector<>(0, R, 0);
488 
489         double alpha = -i * beta;
490         ChMatrix33<> rot(alpha, ChVector<>(0, 0, 1));
491 
492         p0L = rot * p0L;
493         p1L = rot * p1L;
494         p2L = rot * p2L;
495         p3L = rot * p3L;
496         p4L = rot * p4L;
497 
498         p0R = rot * p0R;
499         p1R = rot * p1R;
500         p2R = rot * p2R;
501         p3R = rot * p3R;
502         p4R = rot * p4R;
503 
504         geometry::ChLineArc arc1(ChCoordsys<>(p0L), D, alpha + CH_C_PI_2 + beta / 2, alpha + beta / 2);
505         geometry::ChLineSegment seg2(p1L, p2L);
506         geometry::ChLineArc arc3(ChCoordsys<>(p3L), R, alpha + CH_C_PI + beta / 2, alpha + 1.5 * CH_C_PI, true);
507         geometry::ChLineSegment seg4(p4L, p4R);
508         geometry::ChLineArc arc5(ChCoordsys<>(p3R), R, alpha + 1.5 * CH_C_PI, alpha + CH_C_2PI - beta / 2, true);
509         geometry::ChLineSegment seg6(p2R, p1R);
510         geometry::ChLineArc arc7(ChCoordsys<>(p0R), D, alpha + CH_C_PI - beta / 2, alpha + CH_C_PI_2 - beta / 2);
511 
512         profile->AddSubLine(arc1);
513         profile->AddSubLine(seg2);
514         profile->AddSubLine(arc3);
515         profile->AddSubLine(seg4);
516         profile->AddSubLine(arc5);
517         profile->AddSubLine(seg6);
518         profile->AddSubLine(arc7);
519     }
520 
521     return profile;
522 }
523 
524 }  // end namespace vehicle
525 }  // end namespace chrono
526