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